Metadata

Datastore menyediakan akses terprogram ke beberapa metadatanya untuk mendukung metaprogramming, menerapkan fungsi administratif backend, menyederhanakan caching yang konsisten, dan tujuan serupa; Anda dapat menggunakannya, misalnya, untuk membangun penampil Datastore kustom untuk aplikasi Anda. Metadata yang tersedia mencakup informasi tentang entity group, namespace, jenis entity, dan properti yang digunakan aplikasi Anda, serta representasi properti untuk setiap properti.

Dasbor Datastore di Konsol Google Cloud juga menyediakan beberapa metadata tentang aplikasi Anda, tetapi data yang ditampilkan di sana berbeda dalam beberapa hal penting dengan yang ditampilkan oleh fungsi-fungsi ini.

  • Keaktualan. Membaca metadata menggunakan API akan mendapatkan data saat ini, sedangkan data di dasbor hanya diperbarui sekali sehari.
  • Daftar Isi. Beberapa metadata di dasbor tidak tersedia melalui API; dan sebaliknya.
  • Kecepatan. Aktivitas dan kueri Metadata ditagih dengan cara yang sama seperti aktivitas dan kueri Datastore. Kueri metadata yang mengambil informasi tentang namespace, jenis, dan properti umumnya lambat untuk dijalankan. Pada dasarnya, perkirakan bahwa kueri metadata yang menampilkan entity N akan memerlukan waktu yang sama seperti N kueri biasa yang masing-masing menampilkan satu entity. Selain itu, kueri representasi properti (kueri properti khusus non-kunci) lebih lambat dari kueri properti khusus kunci. Metadata yang diperoleh metadata entity group agak lebih cepat daripada mendapatkan entity reguler.

Metadata entity group

Cloud Datastore menyediakan akses ke "versi" entity group, angka yang benar-benar positif yang dijamin akan meningkat pada setiap perubahan pada entity group tersebut.

Versi entity group diperoleh dengan memanggil get() pada entity pseudo khusus yang berisi properti __version__ yang benar-benar positif. Kunci entity pseudo dapat dibuat menggunakan metode Entities.createEntityGroupKey():

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

Perilaku lama

Dalam perilaku versi entity group lama, versi entity group hanya meningkat saat terjadi perubahan pada entity group. Perilaku metadata entity group lama dapat digunakan, misalnya, untuk menjaga cache yang konsisten dari kueri ancestor yang kompleks di suatu entity group.

Contoh ini menyimpan hasil kueri ke dalam cache (jumlah hasil yang cocok) dan menggunakan perilaku lama versi entity group untuk menggunakan nilai yang di-cache jika nilai tersebut terbaru:

// 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 __entity_group__ mungkin tidak ada untuk entity group yang belum pernah ditulis.

Kueri metadata

Class Java Entities yang ditentukan dalam paket com.google.appengine.api.datastore, menyediakan tiga jenis entity khusus yang dicadangkan untuk kueri metadata. Kedua hal tersebut ditunjukkan oleh konstanta statis class Entities:

Konstanta statis Jenis entity
Entities.NAMESPACE_METADATA_KIND __namespace__
Entities.KIND_METADATA_KIND __kind__
Entities.PROPERTY_METADATA_KIND __property__

Jenis ini tidak akan bertentangan dengan pengguna lain dengan nama yang sama yang mungkin sudah ada di aplikasi Anda. Dengan membuat kueri jenis khusus ini, Anda dapat mengambil entity yang berisi metadata yang diinginkan.

Entity yang ditampilkan oleh kueri metadata dibuat secara dinamis, berdasarkan status Datastore saat ini. Meskipun Anda dapat membuat objek Entity lokal dari jenis __namespace__, __kind__, atau __property__, setiap upaya untuk menyimpannya di Datastore akan gagal dengan IllegalArgumentException.

Cara termudah untuk mengeluarkan kueri metadata adalah dengan Data Datastore API level rendah. Contoh berikut mencetak nama semua namespace dalam aplikasi:

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

Kueri namespace

Jika aplikasi menggunakan Namespaces API, Anda dapat menggunakan kueri namespace untuk menemukan semua namespace yang digunakan dalam entity aplikasi tersebut. Dengan begitu, Anda dapat melakukan aktivitas seperti fungsi administratif di beberapa namespace.

Kueri namespace menampilkan entity __namespace__ jenis khusus yang nama kuncinya adalah nama namespace. (Pengecualiannya adalah namespace default yang ditetapkan oleh string kosong "": karena string kosong bukan nama kunci yang valid, namespace ini dikunci dengan ID numerik 1.) Kueri jenis ini hanya mendukung pemfilteran untuk rentang pada properti pseudo khusus __key__, yang nilainya adalah kunci entity. Hasilnya dapat diurutkan berdasarkan nilai __key__ menaik (tetapi tidak menurun). Karena entity __namespace__ tidak memiliki properti, kueri khusus kunci dan non-kunci menampilkan informasi yang sama.

Contoh berikut menampilkan daftar namespace aplikasi dalam rentang antara dua nama yang ditentukan, start dan end:

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

Kueri jenis

Kueri jenis menampilkan entity jenis __kind__ yang nama kuncinya adalah nama jenis entity. Kueri jenis ini secara implisit dibatasi untuk namespace saat ini dan hanya mendukung pemfilteran untuk rentang melalui properti pseudo __key__. Hasilnya dapat diurutkan berdasarkan nilai __key__ menaik (tetapi tidak menurun). Karena entity __kind__ tidak memiliki properti, kueri khusus kunci dan non-kunci menampilkan informasi yang sama.

Contoh berikut mencetak semua jenis yang namanya diawali dengan huruf kecil:

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

Kueri properti

Kueri properti menampilkan entity jenis __property__ yang menunjukkan properti yang terkait dengan jenis entity. Entity yang mewakili properti P jenis K dibuat sebagai berikut:

  • Kunci entity memiliki jenis __property__ dan nama kunci P.
  • Kunci parent entity memiliki jenis __kind__ dan nama kunci K.

Perilaku kueri properti bergantung pada apakah kueri tersebut keys-only atau khusus non-kunci (representasi properti), seperti yang dijelaskan di subbagian di bawah.

Kueri properti: khusus kunci

Kueri properti khusus kunci menampilkan kunci untuk setiap properti terindeks dari jenis entity yang ditentukan. (Properti yang tidak diindeks tidak disertakan.) Contoh berikut mencetak nama semua jenis entity aplikasi dan properti yang terkait dengan masing-masing jenis entity tersebut:

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

Kueri jenis ini secara implisit dibatasi untuk namespace saat ini dan mendukung pemfilteran hanya untuk rentang melalui properti pseudo __key__, dengan kunci menunjukkan entity __kind__ atau __property__. Hasilnya dapat diurutkan berdasarkan nilai __key__ menaik (tetapi tidak menurun). Pemfilteran diterapkan pada pasangan jenis properti, yang diurutkan pertama berdasarkan jenis dan kedua berdasarkan properti: misalnya, Anda memiliki entity dengan properti berikut:

  • jenis Account dengan properti
    • balance
    • company
  • jenis Employee dengan properti
    • name
    • ssn
  • jenis Invoice dengan properti
    • date
    • amount
  • jenis Manager dengan properti
    • name
    • title
  • jenis Product dengan properti
    • description
    • price

Kueri untuk menampilkan data properti akan terlihat seperti ini:

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

Kueri di atas akan menampilkan hal berikut:

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

Perhatikan bahwa hasilnya tidak menyertakan properti name dari jenis Employee dan properti title dari jenis Manager, maupun properti apa pun dengan jenis Account dan Product, karena berada di luar rentang yang ditetapkan untuk kueri ini.

Kueri properti juga mendukung pemfilteran ancestor pada kunci __kind__ atau __property__, untuk membatasi hasil kueri ke satu jenis atau properti. Anda dapat menggunakannya, misalnya, untuk mendapatkan properti yang terkait dengan jenis entity tertentu, seperti dalam contoh berikut:

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

Kueri properti: khusus non-kunci (representasi properti)

Kueri properti khusus non-kunci, yang dikenal sebagai kueri representasi properti, menampilkan informasi tambahan tentang representasi yang digunakan oleh setiap pasangan jenis properti. (Properti yang tidak diindeks tidak disertakan.) Entity yang ditampilkan untuk properti P sejenis K memiliki kunci yang sama dengan kueri khusus kunci terkait, serta properti property_representation tambahan yang menampilkan representasi properti. Nilai properti ini adalah class instance java.util.Collection<String> yang berisi satu string untuk setiap representasi properti P yang ditemukan dalam entity jenis K.

Perhatikan bahwa representasi tidak sama dengan class properti; beberapa class properti dapat dipetakan ke representasi yang sama. (Misalnya, java.lang.String dan com.google.appengine.api.datastore.PhoneNumber sama-sama menggunakan representasi STRING.)

Tabel berikut dipetakan dari class properti ke representasinya:

Class properti Representasi
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> Representasi T

Contoh berikut menemukan semua representasi properti tertentu untuk jenis entity tertentu:

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