查詢限制

本頁面涵蓋在 Google App Engine 查詢 Cloud Datastore 的相關限制。下方列出您在開發 Cloud Datastore 時會遇到的一般限制。

實體若缺少查詢中指定的屬性,就不會包含在查詢結果中

相同種類的實體不一定具備相同的屬性。針對每一個在查詢篩選器和排序順序中指定的屬性,實體都必須擁有一個值 (可能是空值),才有資格做為查詢結果。否則用於執行查詢的索引就會忽略該實體,查詢結果自然就不會包含該實體。

篩選未建立索引的屬性不會傳回任何結果

查詢找不到未建立索引的屬性值,也無法排序這類屬性。請參閱資料儲存庫索引頁面,瞭解未建立索引的屬性相關詳細說明。

不等式篩選器最多限用於一個屬性

查詢機制會要求一個查詢的所有可能結果在索引中彼此相鄰,以避免必須掃描整個索引。為符合這項限制,單一查詢不能將不等式比較運算 (LESS_THANLESS_THAN_OR_EQUALGREATER_THANGREATER_THAN_OR_EQUALNOT_EQUAL) 用於所有篩選器中超過一個屬性。例如,以下查詢的兩個不等式篩選器會套用至相同屬性,因此查詢有效:

Java 8

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);

Java 7

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);

但是,以下查詢「無效」,原因在於其對兩個不同的屬性使用不等式篩選器:

Java 8

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);

Java 7

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) 篩選器,以及用於查詢單一屬性的一或多個不等式篩選器。因此,以下是有效查詢:

Java 8

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);

Java 7

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);

如果未指定排序順序,就不會定義查詢結果的順序

如果查詢不指定排序順序,則會依擷取順序傳回結果。隨著 Cloud Datastore 實作的發展 (或是應用程式的索引變更時),這個順序可能會改變。因此,如果應用程式要求按照特定順序排列查詢結果,請務必在查詢中明確指定排序順序。

如果有等式篩選器,則屬性的排序順序將被忽略

如果查詢有特定屬性的等式篩選器,系統就會忽略針對這個屬性指定的任何排序順序。屬性的所有結果值均相同且不需進一步排序,這樣就不需要針對單值屬性進行多餘的處理,因此可輕鬆地達到最佳化。但如果是多值屬性,除了等式篩選器比對而得出的值之外,可能還包含其他值。由於這樣的使用情形並不多見,套用排序順序需耗費較多成本,而且需要建立其他索引,因此即使是多值屬性,Cloud Datastore 查詢規劃工具也會直接忽略排序順序。這可能會導致系統採用與排序順序不同的順序傳回查詢結果。

必須先排序不等式篩選器中使用的屬性

如要擷取所有與不等式篩選器相符的結果,查詢會掃描索引中符合篩選器的前兩列,接著再繼續掃描,直到發現不一致的列為止。連續列必須按照不等式篩選器中使用的屬性排序,排列在任何其他屬性之前,才能包含完整的結果集。因此,如果查詢指定一或多個不等式篩選器及一或多個排序順序,第一個排序順序必須參照不等式篩選器中具名的相同屬性。以下為有效的查詢:

Java 8

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);

Java 7

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);

以下查詢「無效」,因為沒有按照不等式篩選器中所用的屬性排序:

Java 8

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);

Java 7

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);

同理可證,這個查詢也無效,原因在於不等式篩選器所用的屬性並不是排序後的第一個屬性:

Java 8

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);

Java 7

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 種類的實體包含 12 這兩個 x 屬性值,就「無法」與查詢相符:

Java 8

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

Java 7

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

實體每一個 x 值均符合其中一個篩選器,但沒有一個值能同時符合兩個篩選器。請注意,等式篩選器不適用這項原則。舉例來說,同一實體「會」符合查詢

Java 8

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

Java 7

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

即使實體沒有任何一個 x 值同時符合兩種篩選條件也無妨。

NOT_EQUAL 運算子的用途在於進行「值不是」測試。因此,例如以下查詢

Java 8

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

Java 7

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

符合 x 值不是 1 的任何 Widget 實體。

在 Java 中,您也可以使用如下的查詢

Java 8

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

Java 7

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 屬性值的實體之前。

交易內部查詢必須包含祖系篩選器

Cloud Datastore 交易只能使用屬於同一個實體群組 (具有共同祖系) 的實體運作。為維護這項限制,在交易中執行的所有查詢都須包含一個祖系篩選器,且該篩選器會如同交易中的其他作業,在相同的實體群組中指定祖系。

本頁內容對您是否有任何幫助?請提供意見:

傳送您對下列選項的寶貴意見...

這個網頁
Java 適用的 App Engine 標準環境