Metadatos

Cloud Datastore proporciona acceso programático a algunos de sus metadatos para permitir la metaprogramación, la implementación de funciones administrativas de backend, la simplificación del almacenamiento en caché coherente y propósitos similares. Lo puedes usar, por ejemplo, para crear un lector personalizado de Cloud Datastore en tu aplicación. Los metadatos disponibles incluyen información sobre los grupos de entidad, los espacios de nombres, los tipos de entidad y las propiedades que usa tu aplicación, así como las representaciones de propiedad para cada propiedad.

El panel de Cloud Datastore en GCP Console también proporciona algunos metadatos sobre tu aplicación, pero los datos que se muestran allí difieren en algunos aspectos importantes de los que se muestran en estas funciones.

  • Actualidad: Cuando se leen los metadatos con la API, se obtienen los datos actuales, mientras que los datos del panel se actualizan solo una vez por día.
  • Contenido: Algunos metadatos del panel no están disponibles mediante las API y viceversa.
  • Velocidad. La obtención y consulta de metadatos se facturan de la misma manera que las consultas y obtenciones de Cloud Datastore. En general, las consultas de metadatos que recuperan información en espacios de nombres, tipos y propiedades son de ejecución lenta. Como regla general, una consulta de metadatos que muestra N entidades puede tomar el mismo tiempo que las N consultas comunes que muestran una sola entidad cada una. Además, las consultas de representación de propiedad (consultas de propiedad no solo de claves) son más lentas que las consultas de propiedad solo de claves. La obtención de metadatos de grupos de entidad es algo más rápida que la de una entidad normal.

Metadatos de grupo de entidades

Cloud Datastore brinda acceso a la "versión" de un grupo de entidad, una cantidad estrictamente positiva que aumenta ineludiblemente con cada cambio realizado en el grupo de entidad.

Para obtener la versión de un grupo de entidad, se debe llamar get() en una seudoentidad especial que contiene una propiedad __version__ estrictamente positiva. La clave de la seudoentidad se puede crear con el método 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));
}

Comportamiento heredado

En el comportamiento heredado de la versión de grupo de entidad, la versión del grupo de entidad aumenta solo con los cambios al grupo de entidad. El comportamiento heredado de los metadatos del grupo de entidad se podría usar, por ejemplo, para mantener una caché coherente de una consulta principal compleja en un grupo de entidad.

Este ejemplo almacena en caché resultados de consulta (un conteo de resultados coincidentes) y usa el comportamiento heredado de versiones de grupo de entidad para usar los valores almacenados en caché si es recurrente:

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

Es posible que no existan las entidades __entity_group__ para grupos de entidad a los que nunca se escribió.

Consultas de metadatos

La clase de Java Entities, definida en el paquete com.google.appengine.api.datastore, proporciona tres categorías de entidad especiales que se reservan para las consultas de metadatos. Se denotan mediante constantes estáticas de la clase Entities:

Contenido estático Tipo de entidad
Entities.NAMESPACE_METADATA_KIND __namespace__
Entities.KIND_METADATA_KIND __kind__
Entities.PROPERTY_METADATA_KIND __property__

Estos tipos no entrarán en conflicto con otros que tengan el mismo nombre y que ya existan en tu aplicación. Si consultas estos tipos especiales, puedes recuperar entidades que contengan los metadatos deseados.

Las entidades que se muestran por consultas de metadatos se generan dinámicamente según el estado actual de Cloud Datastore. Si bien puedes crear objetos Entity locales de las categorías __namespace__, __kind__ o __property__, todo intento de almacenarlos en Cloud Datastore fallará con una IllegalArgumentException.

La manera más fácil de generar consultas de metadatos es con la API de Datastore de bajo nivel. El ejemplo a continuación imprime los nombres de todos los espacios de nombre en un aplicación.

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

Consultas de espacio de nombres

Si tu aplicación usa la API de espacios de nombre, puedes usar una consulta de espacio de nombres para encontrar todos los espacios de nombre que se usan en las entidades de la aplicación. Esto te permite realizar actividades como las funciones administrativas en varios espacios de nombres.

Las consultas de espacios de nombres muestran entidades con el tipo especial __namespace__ cuyo nombre de clave es el de un espacio de nombres. (Una excepción es el espacio de nombres predeterminado designado por la string vacía "": ya que la string vacía no es un nombre de clave válido, y a este espacio de nombre se le asigna el ID numérico 1.) Las consultas de este tipo admiten el filtrado solo para rangos superiores a la seudopropiedad especial __key__, cuyo valor es la clave de la entidad. Se pueden ordenar los resultados según el valor __key__ de forma ascendente (pero no descendente). Dado que las entidades __namespace__ no tienen propiedades, tanto las consultas solo de claves como las que no lo son muestran la misma información.

En el ejemplo siguiente, se muestra una lista de los espacios de nombres de una aplicación en el rango entre dos nombres especificados, start y end:

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

Consultas de tipos

Las consultas de categoría muestran entidades de la categoría __kind__ cuyo nombre de clave es el nombre de una categoría de entidad. Las consultas de este tipo se restringen de forma implícita al espacio de nombres actual y permiten filtrar solo rangos superiores a la seudopropiedad __key__. Se pueden ordenar los resultados según el valor __key__ de forma ascendente (pero no descendente). Dado que las entidades __kind__ no tienen propiedades, tanto las consultas solo de clave como las que no lo son muestran la misma información.

Los ejemplos a continuación muestran todos los tipos cuyos nombres comienzan con una letra minúscula.

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

Consultas de propiedades

Las consultas de propiedad muestran entidades de la categoría __property__ que denotan las propiedades asociadas a una categoría de entidad. La entidad que representa la propiedad P de tipo K se construye de la siguiente forma:

  • La clave de la entidad tiene la categoría __property__ y el nombre de clave P.
  • La clave de la entidad principal tiene la categoría __kind__ y el nombre de clave K.

El comportamiento de una consulta de propiedad depende de si es una consulta solo de claves o no solo de claves (representación de propiedad), como se detalla en las siguientes subsecciones.

Consultas de propiedades: solo de claves

Las consultas de propiedad solo de claves muestran una clave para cada propiedad indexada de un tipo de entidad especificada. (No se incluyen las propiedades no indexadas). El siguiente ejemplo muestra los nombres de todos los tipos de entidades de una aplicación y las propiedades asociadas a cada uno:

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

Las consultas de este tipo se restringen de forma implícita al espacio de nombre actual y admiten filtros solo para rangos superiores a la seudopropiedad __key__, en los que las claves denotan entidades __kind__ o __property__. Se pueden ordenar los resultados según el valor __key__ de forma ascendente (pero no descendente). Se aplica el filtro a los pares tipo-propiedad, ordenados primero por tipo y después por propiedad. Por ejemplo, imagina que tienes una entidad con las propiedades siguientes:

  • tipo Account con propiedades
    • balance
    • company
  • tipo Employee con propiedades
    • name
    • ssn
  • tipo Invoice con propiedades
    • date
    • amount
  • tipo Manager con propiedades
    • name
    • title
  • tipo Product con propiedades
    • description
    • price

La consulta que muestra los datos de la propiedad se verá de la siguiente manera:

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

La consulta anterior mostraría lo siguiente:

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

Observa que los resultados no incluyen la propiedad name de la categoría Employee ni la propiedad title de la categoría Manager, ni ninguna propiedad de las categorías Account y Product, ya que no entran en el rango especificado para la consulta.

Las consultas de propiedades permiten el filtrado principal en una clave __kind__ o __property__ a fin de limitar los resultados de la consulta a un solo tipo o a una sola propiedad. Puedes usar esto, por ejemplo, para obtener las propiedades asociadas a un tipo de entidad dada, como en el ejemplo siguiente:

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

Consultas de propiedad: no solo de claves (representación de propiedad)

Las consultas de propiedad no solo de claves, conocidas como consultas de representación de propiedad, muestran información adicional sobre las representaciones que usa cada propiedad. (No se incluyen las propiedades no indexadas). La entidad que se muestra para la propiedad P de la categoría K tiene la misma clave que en una consulta de solo claves correspondiente, junto con una propiedad property_representation adicional que muestra las representaciones de la propiedad. El valor de esta propiedad es una instancia de clase java.util.Collection<String> que contiene una string para cada representación de la propiedad P que se encuentra en cualquier entidad de categoría K.

Ten en cuenta que las representaciones no son las mismas que las clases de propiedad; varias clases de propiedad se pueden asignar a la misma representación. Por ejemplo, java.lang.String y com.google.appengine.api.datastore.PhoneNumber usan la representación STRING.

La siguiente tabla muestra las asignaciones desde las clases de propiedad a sus representaciones:

Clase de propiedad Representación
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> Representación de T

En el siguiente ejemplo, se encuentran todas las representaciones de la propiedad especificada para un tipo de entidad dado:

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");
}
¿Te ha resultado útil esta página? Enviar comentarios:

Enviar comentarios sobre...

Entorno estándar de App Engine para Java 8