メタデータ

Cloud Datastore では一部のメタデータにプログラムからアクセスできます。これによりメタプログラミングやバックエンドの管理機能の実装が可能となり、整合性のあるキャッシュ保存が簡単になります。これは、アプリケーション用にカスタムのデータストア ビューアを作成する場合などに役立ちます。使用できるメタデータとしては、アプリケーションが使用するエンティティ グループ、名前空間、エンティティの種類、プロパティに関する情報のほか、各プロパティのプロパティ表現などが挙げられます。

アプリケーションに関する一部のメタデータは、GCP Console の Cloud Datastore ダッシュボードでも提供されています。ただし、ここに表示されるデータは、いくつかの重要な側面において関数から返されるデータとは異なります。

  • 鮮度: API を使用してメタデータを読み取ると最新のデータが取得されますが、ダッシュボードのデータが更新されるのは 1 日 1 回だけです。
  • 内容: ダッシュボードの一部のメタデータは API 経由では使用できません。また、その逆の場合もあります。
  • 速度: メタデータの取得とクエリは Cloud Datastore の取得とクエリと同じ方法で課金されます。名前空間、種類、プロパティに関する情報をフェッチするメタデータ クエリは、実行速度が遅いのが一般的です。目安としては、N 個のエンティティを返すメタデータ クエリにかかる時間は、それぞれが単一のエンティティを返す N 個の通常のクエリにかかる時間とほぼ同じです。また、プロパティ表現クエリ(キーのみではないプロパティ クエリ)は、キーのみのプロパティ クエリよりも遅くなります。エンティティ グループのメタデータに対するメタデータ取得は、通常のエンティティを取得する場合よりもある程度は高速になります。

エンティティ グループのメタデータ

Cloud Datastore では、エンティティ グループの「バージョン」にアクセスできます。バージョンは真に正の数値であり、エンティティ グループに変更が加えられるたびに増加します。

エンティティ グループのバージョンを取得するには、真に正の __version__ プロパティを含む特別な疑似エンティティに対して get() を呼び出します。擬似エンティティのキーは、Entities.createEntityGroupKey() メソッドを使って作成できます。

Java 8

private static long getEntityGroupVersion(DatastoreService ds, Transaction tx, Key entityKey) {
  try {
    return Entities.getVersionProperty(ds.get(tx, Entities.createEntityGroupKey(entityKey)));
  } catch (EntityNotFoundException e) {
    // No entity group information, return a value strictly smaller than any
    // possible version
    return 0;
  }
}

private static void printEntityGroupVersions(DatastoreService ds, PrintWriter writer) {
  Entity entity1 = new Entity("Simple");
  Key key1 = ds.put(entity1);
  Key entityGroupKey = Entities.createEntityGroupKey(key1);

  // Print entity1's entity group version
  writer.println("version " + getEntityGroupVersion(ds, null, key1));

  // Write to a different entity group
  Entity entity2 = new Entity("Simple");
  ds.put(entity2);

  // Will print the same version, as entity1's entity group has not changed
  writer.println("version " + getEntityGroupVersion(ds, null, key1));

  // Change entity1's entity group by adding a new child entity
  Entity entity3 = new Entity("Simple", entity1.getKey());
  ds.put(entity3);

  // Will print a higher version, as entity1's entity group has changed
  writer.println("version " + getEntityGroupVersion(ds, null, key1));
}

Java 7

private static long getEntityGroupVersion(DatastoreService ds, Transaction tx, Key entityKey) {
  try {
    return Entities.getVersionProperty(ds.get(tx, Entities.createEntityGroupKey(entityKey)));
  } catch (EntityNotFoundException e) {
    // No entity group information, return a value strictly smaller than any
    // possible version
    return 0;
  }
}

private static void printEntityGroupVersions(DatastoreService ds, PrintWriter writer) {
  Entity entity1 = new Entity("Simple");
  Key key1 = ds.put(entity1);
  Key entityGroupKey = Entities.createEntityGroupKey(key1);

  // Print entity1's entity group version
  writer.println("version " + getEntityGroupVersion(ds, null, key1));

  // Write to a different entity group
  Entity entity2 = new Entity("Simple");
  ds.put(entity2);

  // Will print the same version, as entity1's entity group has not changed
  writer.println("version " + getEntityGroupVersion(ds, null, key1));

  // Change entity1's entity group by adding a new child entity
  Entity entity3 = new Entity("Simple", entity1.getKey());
  ds.put(entity3);

  // Will print a higher version, as entity1's entity group has changed
  writer.println("version " + getEntityGroupVersion(ds, null, key1));
}

従来の動作

エンティティ グループのバージョンの従来の動作では、エンティティ グループのバージョンはエンティティ グループに変更が加えられたときにのみ増加します。たとえば、エンティティ グループ メタデータの従来の動作を使用して、エンティティ グループに対する複雑な祖先クエリのキャッシュ整合性を確保できます。

この例では、クエリ結果(一致する結果の数)をキャッシュし、エンティティ グループのバージョンの従来の動作を使用して、キャッシュされた値を最新であれば使用します。

Java 8

// A simple class for tracking consistent entity group counts.
private static class EntityGroupCount implements Serializable {

  long version; // Version of the entity group whose count we are tracking
  int count;

  EntityGroupCount(long version, int count) {
    this.version = version;
    this.count = count;
  }

  // Display count of entities in an entity group, with consistent caching
  void showEntityGroupCount(
      DatastoreService ds, MemcacheService cache, PrintWriter writer, Key entityGroupKey) {
    EntityGroupCount egCount = (EntityGroupCount) cache.get(entityGroupKey);
    // Reuses getEntityGroupVersion method from the previous example.
    if (egCount != null && egCount.version == getEntityGroupVersion(ds, null, entityGroupKey)) {
      // Cached value matched current entity group version, use that
      writer.println(egCount.count + " entities (cached)");
    } else {
      // Need to actually count entities. Using a transaction to get a consistent count
      // and entity group version.
      Transaction tx = ds.beginTransaction();
      PreparedQuery pq = ds.prepare(tx, new Query(entityGroupKey));
      int count = pq.countEntities(FetchOptions.Builder.withLimit(5000));
      cache.put(
          entityGroupKey,
          new EntityGroupCount(getEntityGroupVersion(ds, tx, entityGroupKey), count));
      tx.rollback();
      writer.println(count + " entities");
    }
  }
}

Java 7

// A simple class for tracking consistent entity group counts.
private static class EntityGroupCount implements Serializable {
  long version; // Version of the entity group whose count we are tracking
  int count;

  EntityGroupCount(long version, int count) {
    this.version = version;
    this.count = count;
  }

  // Display count of entities in an entity group, with consistent caching
  void showEntityGroupCount(
      DatastoreService ds, MemcacheService cache, PrintWriter writer, Key entityGroupKey) {
    EntityGroupCount egCount = (EntityGroupCount) cache.get(entityGroupKey);
    // Reuses getEntityGroupVersion method from the previous example.
    if (egCount != null && egCount.version == getEntityGroupVersion(ds, null, entityGroupKey)) {
      // Cached value matched current entity group version, use that
      writer.println(egCount.count + " entities (cached)");
    } else {
      // Need to actually count entities. Using a transaction to get a consistent count
      // and entity group version.
      Transaction tx = ds.beginTransaction();
      PreparedQuery pq = ds.prepare(tx, new Query(entityGroupKey));
      int count = pq.countEntities(FetchOptions.Builder.withLimit(5000));
      cache.put(
          entityGroupKey,
          new EntityGroupCount(getEntityGroupVersion(ds, tx, entityGroupKey), count));
      tx.rollback();
      writer.println(count + " entities");
    }
  }
}

書き込みが行われたことのないエンティティ グループの場合、__entity_group__ エンティティが存在しないことがあります。

メタデータ クエリ

Java クラス Entitiescom.google.appengine.api.datastore パッケージで定義されており、メタデータ クエリ用に予約された特別なエンティティの種類が 3 つ用意されています。これらの種類は、Entities クラスの静的定数で示されます。

静的定数 エンティティの種類
Entities.NAMESPACE_METADATA_KIND __namespace__
Entities.KIND_METADATA_KIND __kind__
Entities.PROPERTY_METADATA_KIND __property__

これらの種類は、アプリケーションにすでに存在する可能性がある同じ名前の他のエンティティとは競合しません。このような特殊な種類に対してクエリを実行することで、目的のメタデータを含むエンティティを取得できます。

メタデータ クエリによって返されるエンティティは、Cloud Datastore の現在の状態に基づいて動的に生成されます。種類が __namespace__、__kind__、__property__ のローカル Entity オブジェクトを作成することはできますが、Cloud Datastore に保存しようとすると IllegalArgumentException となり、失敗します。

メタデータ クエリを実行する最も簡単な方法は、低水準 Datastore API を使用することです。たとえば、次のコードは、アプリケーションのすべての名前空間の名前を出力します。

Java 8

void printAllNamespaces(DatastoreService ds, PrintWriter writer) {
  Query q = new Query(Entities.NAMESPACE_METADATA_KIND);

  for (Entity e : ds.prepare(q).asIterable()) {
    // A nonzero numeric id denotes the default namespace;
    // see <a href="#Namespace_Queries">Namespace Queries</a>, below
    if (e.getKey().getId() != 0) {
      writer.println("<default>");
    } else {
      writer.println(e.getKey().getName());
    }
  }
}

Java 7

void printAllNamespaces(DatastoreService ds, PrintWriter writer) {
  Query q = new Query(Entities.NAMESPACE_METADATA_KIND);

  for (Entity e : ds.prepare(q).asIterable()) {
    // A nonzero numeric id denotes the default namespace;
    // see <a href="#Namespace_Queries">Namespace Queries</a>, below
    if (e.getKey().getId() != 0) {
      writer.println("<default>");
    } else {
      writer.println(e.getKey().getName());
    }
  }
}

名前空間クエリ

アプリケーションで Namespaces API を使う場合、名前空間クエリを使用すると、アプリケーションのエンティティで使用されているすべての名前空間を検索できます。これにより、複数の名前空間にまたがる管理機能などのアクティビティを実行できます。

名前空間クエリは、__namespace__ という特別な種類のエンティティを返します。このようなエンティティのキー名は名前空間の名前です。(空の文字列 "" で示されるデフォルトの名前空間は例外です。空の文字列は有効なキー名ではないため、この名前空間には数値 ID の 1 が代わりにキーとして設定されます)。このタイプのクエリは、特殊な疑似プロパティ __key__ を対象とした範囲のフィルタリングだけをサポートします。このプロパティの値はエンティティのキーです。結果は __key__ 値の昇順で並べ替えることができます(降順にすることはできません)。__namespace__ のエンティティはプロパティを持たないため、キーのみのクエリでもキーのみではないクエリでも同じ情報が返されます。

次の例は、アプリケーションの名前空間のうち、指定された 2 つの名前、startend の間にある名前空間のリストを返します。

Java 8

List<String> getNamespaces(DatastoreService ds, String start, String end) {

  // Start with unrestricted namespace query
  Query q = new Query(Entities.NAMESPACE_METADATA_KIND);
  List<Filter> subFilters = new ArrayList();
  // Limit to specified range, if any
  if (start != null) {
    subFilters.add(
        new FilterPredicate(
            Entity.KEY_RESERVED_PROPERTY,
            FilterOperator.GREATER_THAN_OR_EQUAL,
            Entities.createNamespaceKey(start)));
  }
  if (end != null) {
    subFilters.add(
        new FilterPredicate(
            Entity.KEY_RESERVED_PROPERTY,
            FilterOperator.LESS_THAN_OR_EQUAL,
            Entities.createNamespaceKey(end)));
  }

  q.setFilter(CompositeFilterOperator.and(subFilters));

  // Initialize result list
  List<String> results = new ArrayList<String>();

  // Build list of query results
  for (Entity e : ds.prepare(q).asIterable()) {
    results.add(Entities.getNamespaceFromNamespaceKey(e.getKey()));
  }

  // Return result list
  return results;
}

Java 7

List<String> getNamespaces(DatastoreService ds, String start, String end) {

  // Start with unrestricted namespace query
  Query q = new Query(Entities.NAMESPACE_METADATA_KIND);
  List<Filter> subFilters = new ArrayList();
  // Limit to specified range, if any
  if (start != null) {
    subFilters.add(
        new FilterPredicate(
            Entity.KEY_RESERVED_PROPERTY,
            FilterOperator.GREATER_THAN_OR_EQUAL,
            Entities.createNamespaceKey(start)));
  }
  if (end != null) {
    subFilters.add(
        new FilterPredicate(
            Entity.KEY_RESERVED_PROPERTY,
            FilterOperator.LESS_THAN_OR_EQUAL,
            Entities.createNamespaceKey(end)));
  }

  q.setFilter(CompositeFilterOperator.and(subFilters));

  // Initialize result list
  List<String> results = new ArrayList<String>();

  // Build list of query results
  for (Entity e : ds.prepare(q).asIterable()) {
    results.add(Entities.getNamespaceFromNamespaceKey(e.getKey()));
  }

  // Return result list
  return results;
}

種類クエリ

種類クエリは、種類が __kind__ であるエンティティを返します。このエンティティのキー名はエンティティの種類の名前です。このタイプのクエリは暗黙的に現在の名前空間に限定され、__key__ 疑似プロパティを対象とした範囲のフィルタリングだけをサポートします。結果は __key__ 値の昇順で並べ替えることができます(降順にすることはできません)。__kind__ のエンティティはプロパティを持たないため、キーのみのクエリでもキーのみではないクエリでも同じ情報が返されます。

次の例は、名前の先頭が小文字であるすべての種類を出力します。

Java 8

void printLowercaseKinds(DatastoreService ds, PrintWriter writer) {

  // Start with unrestricted kind query
  Query q = new Query(Entities.KIND_METADATA_KIND);

  List<Filter> subFils = new ArrayList();

  // Limit to lowercase initial letters
  subFils.add(
      new FilterPredicate(
          Entity.KEY_RESERVED_PROPERTY,
          FilterOperator.GREATER_THAN_OR_EQUAL,
          Entities.createKindKey("a")));

  String endChar = Character.toString((char) ('z' + 1)); // Character after 'z'

  subFils.add(
      new FilterPredicate(
          Entity.KEY_RESERVED_PROPERTY,
          FilterOperator.LESS_THAN,
          Entities.createKindKey(endChar)));

  q.setFilter(CompositeFilterOperator.and(subFils));

  // Print heading
  writer.println("Lowercase kinds:");

  // Print query results
  for (Entity e : ds.prepare(q).asIterable()) {
    writer.println("  " + e.getKey().getName());
  }
}

Java 7

void printLowercaseKinds(DatastoreService ds, PrintWriter writer) {

  // Start with unrestricted kind query
  Query q = new Query(Entities.KIND_METADATA_KIND);

  List<Filter> subFils = new ArrayList();

  // Limit to lowercase initial letters
  subFils.add(
      new FilterPredicate(
          Entity.KEY_RESERVED_PROPERTY,
          FilterOperator.GREATER_THAN_OR_EQUAL,
          Entities.createKindKey("a")));

  String endChar = Character.toString((char) ('z' + 1)); // Character after 'z'

  subFils.add(
      new FilterPredicate(
          Entity.KEY_RESERVED_PROPERTY,
          FilterOperator.LESS_THAN,
          Entities.createKindKey(endChar)));

  q.setFilter(CompositeFilterOperator.and(subFils));

  // Print heading
  writer.println("Lowercase kinds:");

  // Print query results
  for (Entity e : ds.prepare(q).asIterable()) {
    writer.println("  " + e.getKey().getName());
  }
}

プロパティ クエリ

プロパティ クエリは、種類が __property__ のエンティティを返します。このようなエンティティは、エンティティの種類に関連付けられたプロパティを示します。プロパティが P、種類が K で表現されるエンティティは次のように構築されます。

  • エンティティのキーの種類は __property__、キー名は P です。
  • 親エンティティのキーの種類は __kind__、キー名は K です。

プロパティ クエリの動作は、キーのみのクエリかキーのみではない(プロパティ表現)クエリかによって異なります。詳しくは以降で説明しています。

プロパティ クエリ: キーのみ

キーのみのプロパティ クエリは、指定したエンティティの種類のインデックス付けされた各プロパティのキーを返します(インデックス付けされていないプロパティは含まれません)。次の例は、アプリケーションのエンティティの種類すべての名前と、それぞれに関連付けられているプロパティを出力します。

Java 8

void printProperties(DatastoreService ds, PrintWriter writer) {

  // Create unrestricted keys-only property query
  Query q = new Query(Entities.PROPERTY_METADATA_KIND).setKeysOnly();

  // Print query results
  for (Entity e : ds.prepare(q).asIterable()) {
    writer.println(e.getKey().getParent().getName() + ": " + e.getKey().getName());
  }
}

Java 7

void printProperties(DatastoreService ds, PrintWriter writer) {

  // Create unrestricted keys-only property query
  Query q = new Query(Entities.PROPERTY_METADATA_KIND).setKeysOnly();

  // Print query results
  for (Entity e : ds.prepare(q).asIterable()) {
    writer.println(e.getKey().getParent().getName() + ": " + e.getKey().getName());
  }
}

このタイプのクエリは暗黙的に現在の名前空間に限定され、疑似プロパティ __key__ を対象とした範囲のフィルタリングだけをサポートします。この場合、キーは __kind__ または __property__ エンティティを示します。結果は __key__ 値の昇順で並べ替えることができます(降順にすることはできません)。フィルタリングは種類とプロパティの(この順に並んだ)ペアに適用されます。たとえば、次のようなプロパティを持つエンティティがあるとします。

  • 種類が Account で次のプロパティを持つ
    • balance
    • company
  • 種類が Employee で次のプロパティを持つ
    • name
    • ssn
  • 種類が Invoice で次のプロパティを持つ
    • date
    • amount
  • 種類が Manager で次のプロパティを持つ
    • name
    • title
  • 種類が Product で次のプロパティを持つ
    • description
    • price

このプロパティ データを返すクエリは次のようになります。

Java 8

void printPropertyRange(DatastoreService ds, PrintWriter writer) {

  // Start with unrestricted keys-only property query
  Query q = new Query(Entities.PROPERTY_METADATA_KIND).setKeysOnly();

  // Limit range
  q.setFilter(
      CompositeFilterOperator.and(
          new FilterPredicate(
              Entity.KEY_RESERVED_PROPERTY,
              Query.FilterOperator.GREATER_THAN_OR_EQUAL,
              Entities.createPropertyKey("Employee", "salary")),
          new FilterPredicate(
              Entity.KEY_RESERVED_PROPERTY,
              Query.FilterOperator.LESS_THAN_OR_EQUAL,
              Entities.createPropertyKey("Manager", "salary"))));
  q.addSort(Entity.KEY_RESERVED_PROPERTY, SortDirection.ASCENDING);

  // Print query results
  for (Entity e : ds.prepare(q).asIterable()) {
    writer.println(e.getKey().getParent().getName() + ": " + e.getKey().getName());
  }
}

Java 7

void printPropertyRange(DatastoreService ds, PrintWriter writer) {

  // Start with unrestricted keys-only property query
  Query q = new Query(Entities.PROPERTY_METADATA_KIND).setKeysOnly();

  // Limit range
  q.setFilter(
      CompositeFilterOperator.and(
          new FilterPredicate(
              Entity.KEY_RESERVED_PROPERTY,
              Query.FilterOperator.GREATER_THAN_OR_EQUAL,
              Entities.createPropertyKey("Employee", "salary")),
          new FilterPredicate(
              Entity.KEY_RESERVED_PROPERTY,
              Query.FilterOperator.LESS_THAN_OR_EQUAL,
              Entities.createPropertyKey("Manager", "salary"))));
  q.addSort(Entity.KEY_RESERVED_PROPERTY, SortDirection.ASCENDING);

  // Print query results
  for (Entity e : ds.prepare(q).asIterable()) {
    writer.println(e.getKey().getParent().getName() + ": " + e.getKey().getName());
  }
}

上のクエリは、次のデータを返します。

Employee: ssn
Invoice: date
Invoice: amount
Manager: name

結果には種類 Employeename プロパティ、種類 Managertitle プロパティは含まれません。また、種類 Account と種類 Product のプロパティはすべて含まれません。これらのプロパティは、クエリに指定された範囲に含まれないためです。

プロパティ クエリは __kind__ キーまたは __property__ キーによる祖先フィルタリングもサポートしています。これにより、クエリの結果が単一の種類またはプロパティに限定されます。これを利用して、次の例に示すように特定のエンティティの種類に関連付けられたプロパティを取得できます。

Java 8

List<String> propertiesOfKind(DatastoreService ds, String kind) {

  // Start with unrestricted keys-only property query
  Query q = new Query(Entities.PROPERTY_METADATA_KIND).setKeysOnly();

  // Limit to specified kind
  q.setAncestor(Entities.createKindKey(kind));

  // Initialize result list
  ArrayList<String> results = new ArrayList<String>();

  //Build list of query results
  for (Entity e : ds.prepare(q).asIterable()) {
    results.add(e.getKey().getName());
  }

  // Return result list
  return results;
}

Java 7

List<String> propertiesOfKind(DatastoreService ds, String kind) {

  // Start with unrestricted keys-only property query
  Query q = new Query(Entities.PROPERTY_METADATA_KIND).setKeysOnly();

  // Limit to specified kind
  q.setAncestor(Entities.createKindKey(kind));

  // Initialize result list
  ArrayList<String> results = new ArrayList<String>();

  //Build list of query results
  for (Entity e : ds.prepare(q).asIterable()) {
    results.add(e.getKey().getName());
  }

  // Return result list
  return results;
}

プロパティ クエリ: キーのみではない(プロパティ表現)

キーのみではないプロパティ クエリは「プロパティ表現クエリ」と呼ばれ、種類とプロパティの各ペアで使用される表現に関する追加情報を返します(インデックス付けされていないプロパティは含まれません)。種類が K のプロパティ P に対して返されるエンティティには、対応するキーのみのクエリで返されるのものと同じキーと、プロパティの表現を返す追加の property_representation プロパティが含まれます。このプロパティの値はクラス java.util.Collection<String> のインスタンスであり、種類が K のエンティティで検出されるプロパティ P の表現ごとに 1 つの文字列が含まれます。

表現はプロパティ クラスとは異なり、複数のプロパティ クラスが同じ表現にマップされる場合があります(たとえば、java.lang.Stringcom.google.appengine.api.datastore.PhoneNumber の両方で STRING 表現が使用されています)。

次の表に、プロパティ クラスからその表現へのマッピングを示します。

プロパティ クラス 表現
java.lang.Byte INT64
java.lang.Short INT64
java.lang.Integer INT64
java.lang.Long INT64
java.lang.Float DOUBLE
java.lang.Double DOUBLE
java.lang.Boolean BOOLEAN
java.lang.String STRING
com.google.appengine.api.datastore.ShortBlob STRING
java.util.Date INT64
com.google.appengine.api.datastore.GeoPt POINT
com.google.appengine.api.datastore.PostalAddress STRING
com.google.appengine.api.datastore.PhoneNumber STRING
com.google.appengine.api.datastore.Email STRING
com.google.appengine.api.users.User USER
com.google.appengine.api.datastore.IMHandle STRING
com.google.appengine.api.datastore.Link STRING
com.google.appengine.api.datastore.Category STRING
com.google.appengine.api.datastore.Rating INT64
com.google.appengine.api.datastore.Key REFERENCE
com.google.appengine.api.blobstore.BlobKey STRING
java.util.Collection<T> T の表現

次の例は、あるエンティティの種類の指定したプロパティのすべての表現を検出します。

Java 8

Collection<String> representationsOfProperty(DatastoreService ds, String kind, String property) {

  // Start with unrestricted non-keys-only property query
  Query q = new Query(Entities.PROPERTY_METADATA_KIND);

  // Limit to specified kind and property
  q.setFilter(
      new FilterPredicate(
          "__key__", Query.FilterOperator.EQUAL, Entities.createPropertyKey(kind, property)));

  // Get query result
  Entity propInfo = ds.prepare(q).asSingleEntity();

  // Return collection of property representations
  return (Collection<String>) propInfo.getProperty("property_representation");
}

Java 7

Collection<String> representationsOfProperty(DatastoreService ds, String kind, String property) {

  // Start with unrestricted non-keys-only property query
  Query q = new Query(Entities.PROPERTY_METADATA_KIND);

  // Limit to specified kind and property
  q.setFilter(
      new FilterPredicate(
          "__key__", Query.FilterOperator.EQUAL, Entities.createPropertyKey(kind, property)));

  // Get query result
  Entity propInfo = ds.prepare(q).asSingleEntity();

  // Return collection of property representations
  return (Collection<String>) propInfo.getProperty("property_representation");
}

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

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

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