您建议的方法效果很好;您可以将订单项的“appointment_Key_list”建模为列表属性,它将按您的预期工作。您不必使用 IN 运算符 - 这是为了将数据存储中的单个值与您拥有的键列表进行匹配(例如,“WHERE datastore_column IN ('a', 'b', 'c')),而您正在执行相反的操作 - 将单个值与数据存储中的列表进行匹配。
不过,我建议相反的做法可能更适合您的任务:让每个约会都有一个行项目键列表。其操作方式大致相同,但要检索约会的所有数据,您首先要获取约会,然后使用约会实体中的键对行项目进行批量获取。如果您知道约会的密钥,那么您就根本不需要进行任何查询。
我一直试图向 Pindatjuh 解释为什么查询列表属性的效率并不比单值属性低,但显然需要更详细的描述,所以言归正传,这里是......
App Engine 数据存储区索引简介
尽管 Python 和 Java 为数据存储提供了各种高级接口,但数据存储本身讲的是较低级抽象,称为实体。一个实体由以下部分组成:
- 唯一的主键
- (名称,值)对的列表
主键是您已经熟悉的数据存储区键。 (名称、值)对的列表是 App Engine 对实体中数据的表示。到目前为止就这么简单。具有以下值的实体:
a_string = "Hello, world"
an_int = 123
将被序列化为类似这样的内容:
[('a_string', 'Hello, world'), ('an_int', 123)]
但这如何与列表交互呢?嗯,列表被视为“多值”属性。也就是说,包含 n 个项目的列表存储为 n 个单独的属性。一个例子可能会让这一点更清楚:
a_string = "Hello, world"
an_int = 123
a_list_of_ints = [42, 314, 9]
将被序列化为:
[('a_string', 'Hello, world'), ('an_int', 123), ('a_list_of_ints', 42), ('a_list_of_ints', 314), ('a_list_of_ints', 9)]
正如您所看到的,该列表表示一系列值,所有值都具有相同的名称。当您从数据存储区加载数据时,SDK 会看到重复的值并将其转换为列表。
当它与索引交互时,这一点变得很重要。假设您在“a_string”和“an_int”上有一个索引。当您插入或修改一个值时,App Engine 会为其生成一组索引条目;对于上面的索引和上面的实体,它在索引中生成一行,如下所示:
('Hello, world', 123, a_key)
(这里的“a_key”是原始实体的键的占位符。)当您执行使用此索引的查询时,它只需要在索引上进行查找即可查找具有适当前缀的行(例如,“SELECT * FROM Kind WHERE a_string = "Hello, world" ORDER BY an_int')。
但是,当您为列表建立索引时,App Engine 会插入多个索引行。 'an_int' 和 'a_list_of_ints' 上的索引将为上述实体生成这些行:
(123, 42, a_key)
(123, 314, a_key)
(123, 9, a_key)
同样,查询的工作方式与以前相同 - App Engine 只需要在索引中查找具有正确前缀的行。列表中的条目数对查询速度没有影响,只影响生成和写入索引条目所需的时间。事实上,查询规划器不知道“a_list_of_ints”是一个多值属性 - 它只是将其视为任何其他索引条目。
简而言之:
- 在索引和查询方面,包含一个元素的列表与单个属性之间没有实际区别
- 索引列表的大小会影响索引所需的时间和空间,但不会影响查询所需的时间和空间。
- 您可以使用简单的等式过滤器执行查询,以匹配列表中具有给定值的任何实体。