查询限制

本页面介绍从 Google App Engine 查询 Datastore 的限制。下面列出了在针对 Datastore 进行开发时会遇到的一些常见限制。

缺少查询中所指定属性的实体将被忽略

同类实体不需要具有相同的属性。对于查询的过滤条件和排序顺序中指定的每个属性,实体必须拥有一个对应的值(可能为 null),才能出现在查询结果中。否则,用于执行该查询的索引将忽略该实体,因此该实体不会出现在此查询的结果中。

对未编入索引的属性进行过滤时不会返回任何结果

查询无法找到未编入索引的属性值,也无法根据这些属性进行排序。有关未编入索引的属性的详细讨论,请参阅 Datastore 索引页面。

不等性过滤条件最多仅限一个属性

为了不必扫描整个索引,查询机制要求查询的所有潜在结果在索引中彼此相邻。为了满足这项约束,单个查询可能不会在所有过滤条件的多个属性上使用不等性比较(LESS_THANLESS_THAN_OR_EQUALGREATER_THANGREATER_THAN_OR_EQUALNOT_EQUAL)。例如,以下查询有效,因为两个不等性过滤条件均应用于同一个属性:

Filter birthYearMinFilter =
    new FilterPredicate("birthYear", FilterOperator.GREATER_THAN_OR_EQUAL, minBirthYear);

Filter birthYearMaxFilter =
    new FilterPredicate("birthYear", FilterOperator.LESS_THAN_OR_EQUAL, maxBirthYear);

Filter birthYearRangeFilter =
    CompositeFilterOperator.and(birthYearMinFilter, birthYearMaxFilter);

Query q = new Query("Person").setFilter(birthYearRangeFilter);

但是,此查询是无效的,因为它在两个不同的属性上使用不等性过滤条件:

Filter birthYearMinFilter =
    new FilterPredicate("birthYear", FilterOperator.GREATER_THAN_OR_EQUAL, minBirthYear);

Filter heightMaxFilter =
    new FilterPredicate("height", FilterOperator.LESS_THAN_OR_EQUAL, maxHeight);

Filter invalidFilter = CompositeFilterOperator.and(birthYearMinFilter, heightMaxFilter);

Query q = new Query("Person").setFilter(invalidFilter);

请注意,查询可以组合使用不同属性的相等性 (EQUAL) 过滤条件与针对单个属性的一个或多个不等性过滤条件。因此以下是有效的查询:

Filter lastNameFilter = new FilterPredicate("lastName", FilterOperator.EQUAL, targetLastName);

Filter cityFilter = new FilterPredicate("city", FilterOperator.EQUAL, targetCity);

Filter birthYearMinFilter =
    new FilterPredicate("birthYear", FilterOperator.GREATER_THAN_OR_EQUAL, minBirthYear);

Filter birthYearMaxFilter =
    new FilterPredicate("birthYear", FilterOperator.LESS_THAN_OR_EQUAL, maxBirthYear);

Filter validFilter =
    CompositeFilterOperator.and(
        lastNameFilter, cityFilter, birthYearMinFilter, birthYearMaxFilter);

Query q = new Query("Person").setFilter(validFilter);

未指定排序顺序时,查询结果的排序无保证

如果查询未指定排序顺序,结果会按照其检索的顺序返回。随着 Datastore 实现的发展演进(或者如果应用的索引更改),此顺序可能会发生变化。因此,如果您的应用要求查询结果遵循特定顺序,请务必在查询中明确指定该排序顺序。

对于具有等式过滤条件的属性,将忽略排序顺序

对于包含给定属性的相等性过滤条件的查询,将忽略为该属性指定的任何排序顺序。这是一项简单的优化,可以免去单值属性的不必要处理,因为所有结果都具有相同的属性值,因此不需要进一步排序。但是,除了相等性过滤条件之外,多值属性可能还有额外的值。由于此类应用场景较为罕见,并且排序顺序的应用成本高昂,需要额外的索引,因此即使在多值情况下,Datastore 查询规划器也会忽略排序顺序。这可能会导致查询结果以不同于看似默示的排序顺序返回。

不等性过滤条件中所用的属性必须先排序

如需检索与不等性过滤条件匹配的所有结果,查询将扫描与过滤条件匹配的第一行的索引,然后向前扫描,直到遇到不匹配的行。对于包含完整结果集的连续行,必须首先按照不等性过滤条件中所用属性排序,之后再按其他属性排序。因此,如果查询指定一个或多个不等性过滤条件以及一个或多个排序顺序,则第一个排序顺序必须引用不等性过滤条件中指定的相同属性。以下是有效查询:

Filter birthYearMinFilter =
    new FilterPredicate("birthYear", FilterOperator.GREATER_THAN_OR_EQUAL, minBirthYear);

Query q =
    new Query("Person")
        .setFilter(birthYearMinFilter)
        .addSort("birthYear", SortDirection.ASCENDING)
        .addSort("lastName", SortDirection.ASCENDING);

该查询无效,因为它没有按照不等性过滤条件所用属性进行排序:

Filter birthYearMinFilter =
    new FilterPredicate("birthYear", FilterOperator.GREATER_THAN_OR_EQUAL, minBirthYear);

// Not valid. Missing sort on birthYear.
Query q =
    new Query("Person")
        .setFilter(birthYearMinFilter)
        .addSort("lastName", SortDirection.ASCENDING);

同样,此查询也是无效的,因为不等性过滤条件中所用属性不是第一个排序的属性:

Filter birthYearMinFilter =
    new FilterPredicate("birthYear", FilterOperator.GREATER_THAN_OR_EQUAL, minBirthYear);

// Not valid. Sort on birthYear needs to be first.
Query q =
    new Query("Person")
        .setFilter(birthYearMinFilter)
        .addSort("lastName", SortDirection.ASCENDING)
        .addSort("birthYear", SortDirection.ASCENDING);

具有多个值的属性可能会出现意外的行为方式

由于它们被编入索引的方式,相同属性具有多个值的实体有时可能会以意想不到的方式与查询过滤条件和排序顺序进行交互。

如果查询在给定属性上具有多个不等性过滤条件,则只有当属性的至少一个单独值满足所有过滤条件时,实体才与查询匹配。例如,如果 Widget 种类的实体的属性 x 具有 12 这两个值,则该实体将与以下查询不匹配:

Query q =
    new Query("Widget")
        .setFilter(
            CompositeFilterOperator.and(
                new FilterPredicate("x", FilterOperator.GREATER_THAN, 1),
                new FilterPredicate("x", FilterOperator.LESS_THAN, 2)));

该实体的每个 x 值都满足一个过滤条件,但没有一个值同时满足这两个过滤条件。请注意,这不适用于相等性过滤条件。例如,同一个实体将满足以下查询:

Query q =
    new Query("Widget")
        .setFilter(
            CompositeFilterOperator.and(
                new FilterPredicate("x", FilterOperator.EQUAL, 1),
                new FilterPredicate("x", FilterOperator.EQUAL, 2)));

尽管实体的各个 x 值都不满足这两个过滤条件也是如此。

NOT_EQUAL 运算符用于“值不是”测试。例如,查询

Query q = new Query("Widget").setFilter(new FilterPredicate("x", FilterOperator.NOT_EQUAL, 1));

将匹配 x 值不是 1 的任意 Widget 实体。

在 Java 中,您也可以使用类似于下面这样的查询

Query q =
    new Query("Widget")
        .setFilter(
            CompositeFilterOperator.and(
                new FilterPredicate("x", FilterOperator.NOT_EQUAL, 1),
                new FilterPredicate("x", FilterOperator.NOT_EQUAL, 2)));

其行为方式类似于

x < 1 OR (x > 1 AND x < 2) OR x > 2

因此 x 值为 123Widget 实体与之匹配,但相应值为 12 的实体不匹配。

类似地,具有多个值的属性的排列顺序也不同寻常。因为这些属性在每个唯一值的索引中出现一次,所以索引中显示的第一个值将决定实体的排序顺序。

  • 如果查询结果按升序排序,则属性的最小值用于排序。
  • 如果结果按降序排序,则最大值用于排序。
  • 其他值不会影响排列顺序,值的数目也不会影响排列顺序。

这会产生不寻常的结果:无论按升序还是降序排列,具有属性值 19 的实体都会排在具有值 4567 的实体之前

事务内的查询必须包含祖先过滤条件

Datastore 事务仅对属于同一实体组(即来自同一祖先实体)的实体执行操作。为了遵循此限制,在一项事务中执行的所有查询都必须包含一个祖先实体过滤条件,用于指定与该事务中的其他操作属于同一实体组的一个祖先实体。