クエリの制限

このページでは、Google App Engine から Google Cloud Datastore に行うクエリに関する制限について説明します。次のリストは、Cloud Datastore 向けの開発時に課される一般的な制限です。

クエリでプロパティが指定されていないエンティティは無視される

同じ種類のエンティティが同じプロパティを持つとは限りません。エンティティがクエリ結果の対象になるためには、クエリのフィルタと並べ替え順序で指定されているすべてのプロパティについて値(場合によっては null)を持っていなければなりません。そうでない場合、そのエンティティはクエリの実行に使用されているインデックスから除外され、クエリの結果には含まれなくなります。

インデックス付けされていないプロパティに対するフィルタリングからは結果が返されない

クエリはインデックス付けされていないプロパティ値を見つけられず、そのようなプロパティを並べ替えることもできません。インデックス付けされていないプロパティの詳しい説明については、データストア インデックス ページをご覧ください。

不等式フィルタの対象は最大で 1 つのプロパティに制限される

インデックス全体をスキャンしなくても済むように、クエリ メカニズムでは、潜在的なクエリの結果がすべてインデックス内で互いに隣接していることが求められます。この制約を満たすために、単一のクエリの中では、すべてのフィルタにわたり複数のプロパティに対して不等比較(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);

ただし次のクエリは 2 種類のプロパティに対して不等式フィルタを使用しているため無効です。

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フィルタと単一のプロパティに対する 1 つ以上の不等式フィルタを組み合わせて使用できることに留意します。そのため、次のクエリは有効です。

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 のクエリ プランナーでは複数の値を持つ場合であっても単純に並べ替え順序を無視しています。これは、プロパティの値から連想される順序とは異なる順序でクエリの結果が返される原因となる可能性があります。

不等式フィルタで使われているプロパティは最初に並べ替える必要がある

不等式フィルタに一致するすべての結果を取得するため、クエリは、まずフィルタに一致する最初の行のインデックスをスキャンし、一致しない行に遭遇するまでスキャンを進めます。連続した行に結果セット全体が含まれるようにするため、他のプロパティよりも前に、不等式フィルタで使われているプロパティを基準として行を並べ替える必要があります。そのため、クエリで 1 つ以上の不等式フィルタとともに 1 つ以上の並べ替え順序を指定している場合は、最初の並べ替え順序で参照するプロパティが不等式フィルタで指定されているプロパティと同じものでなければなりません。次に有効なクエリを示します。

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

複数の値を持つプロパティは意外な動作をする場合がある

同じプロパティに対して複数の値があるエンティティでは、クエリのフィルタや並べ替え順序に対する動作が予期しない意外なものになる場合があります。これはインデックス付けの仕組みによります。

特定のプロパティに対して複数の不等式フィルタが使われているクエリの場合、そのプロパティの個別の値の少なくとも 1 つがフィルタの「すべて」を満たす場合のみ、エンティティがクエリと一致するものとみなされます。たとえば、種類が Widget であるエンティティのプロパティ x の値が 1 および 2 である場合、クエリに一致しません。

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 の値が 123 である Widget エンティティは一致しますが、値が 12 であるエンティティは一致しません。

同様に、複数の値を持つプロパティの並べ替え順序も通常とは異なります。このようなプロパティは一意の値ごとに 1 回しかインデックスに表れないため、インデックスに表れる最初の値によってエンティティの並べ替え順序が決定されます。

  • クエリ結果を昇順で並べ替えると、順序付けにはプロパティの最小値が使用されます。
  • クエリ結果を降順で並べ替えると、順序付けには最大値が使用されます。
  • その他の値は、並べ替え順序にも値の数にも影響しません。

これにより、プロパティ値 19 のエンティティが、昇順と降順の両方で、プロパティ値 4567 のエンティティよりも前に並べられるという、通常とは異なる並び順になります。

トランザクション内のクエリは祖先フィルタを含んでいなければならない

Cloud Datastore のトランザクションは、同じエンティティ グループ(共通の祖先を持つ子孫)に属するエンティティに対してのみ機能します。この制限を保持するため、トランザクション内で実行するすべてのクエリは、そのトランザクションの他のオペレーションと同じエンティティ グループの祖先を指定する祖先フィルタを含める必要があります。

このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...

Java 8 の App Engine スタンダード環境