如何在Google Cloud Datastore中跨两类匹配属性的实体查询?
Google Cloud Datastore 跨实体关联查询实现方案
场景与需求
我主要使用SQL,对NoSQL不太熟悉。在Google Cloud Datastore中有两个实体:
- User实体:以邮箱ID作为键,包含属性:
email:字符串类型,存储用户邮箱user_name:字符串类型,存储用户名
- Task实体:以整数ID作为键,包含属性:
owner_email:字符串类型,存储任务所有者邮箱details:字符串类型,存储任务详情deadline:日期时间类型,存储任务截止日期
需求是:获取截止日期等于指定日期的Task的owner_email、details,同时关联获取对应的User的user_name。用SQL实现的话写法很直接:
SELECT A.OWNER_EMAIL, A.DETAILS, B.NAME FROM TASK A, USERS B WHERE A.DEADLINE = '2012-12-20' AND A.OWNER_EMAIL = B.EMAIL;
但Datastore并非SQL,我尝试过几种方案但都有问题:
- 反规范化:在Task中添加
user_name属性,但不够规范,修改用户名时需要批量更新所有关联Task,维护成本高 - 祖先键:由于有多个实体需要类似关联,该方案不适用
- 后端循环逐个查询用户名:属于临时拼凑的方案,查询成本太高
目前的代码如下:
owner = user_info.get('email') deadln = eu.get_latest_deadln() query = datastore_client.query(kind='Task') query.add_filter(eu.TASK.DEADLINE, '=', deadln) results = list(query.fetch()) #TODO - get the user name associated with each task return results
可行解决方案
1. 批量获取用户信息(最优推荐)
Datastore支持批量键查询,我们可以先收集所有符合条件的Task的owner_email,再一次性批量查询对应的User实体,避免逐个查询的高成本。代码示例:
owner = user_info.get('email') deadln = eu.get_latest_deadln() # 第一步:查询指定截止日期的所有Task query = datastore_client.query(kind='Task') query.add_filter(eu.TASK.DEADLINE, '=', deadln) tasks = list(query.fetch()) # 第二步:收集唯一的所有者邮箱,避免重复查询 unique_emails = list({task['owner_email'] for task in tasks}) # 构建User实体的键列表(User的键为邮箱字符串) user_keys = [datastore_client.key('User', email) for email in unique_emails] # 批量获取所有User实体 users = datastore_client.get_multi(user_keys) # 第三步:将用户名映射到对应Task email_to_name = {user.key.name: user['user_name'] for user in users if user} for task in tasks: task['user_name'] = email_to_name.get(task['owner_email'], '未知用户') return tasks
该方案仅需两次查询,相比循环逐个查询效率提升显著,同时保持了数据的规范化。
2. 反规范化(权衡后可选)
如果业务中用户名修改频率极低,或者可以接受修改时的批量更新操作,反规范化是性能最优的方案:
- 在Task实体中直接存储
user_name属性 - 当用户修改用户名时,使用Datastore批量更新操作同步所有关联Task;或通过Cloud Functions监听User实体的更新事件,自动同步Task中的用户名
3. GQL有限关联查询(需调整实体结构)
Datastore的GQL支持简单的引用键关联查询,但需要调整Task的关联方式:将owner_email字段改为存储User实体的键(而非邮箱字符串),之后可以用类似SQL JOIN的语法查询:
SELECT Task.owner_email, Task.details, User.user_name FROM Task WHERE Task.deadline = DATETIME('2012-12-20') JOIN User ON Task.owner = User.key
注意:这种JOIN本质是Datastore封装的批量查询,需要提前调整实体的关联字段类型。
内容的提问来源于stack exchange,提问作者EltoCode
相关产品推荐
相关产品推荐

