English

Google App Engine

Metadata Queries

Metadata queries return information about the nature of the data your application stores in the Google App Engine Datastore. Such metadata is commonly used to support metaprogramming, implement backend administrative functions, and similar purposes; you can use it, for instance, to build a custom administration console for your application. The metadata available includes information on the namespaces, entity kinds, and properties your application uses, as well as the property representations associated with each property.

You can also find metadata about your application in the Datastore Statistics tab of the App Engine Administration Console, but the data displayed there differs in some important respects from that returned by metadata queries:

  • Freshness. Metadata queries return data that is current as of the time of the query. Data in the Datastore Statistics tab is updated only once daily.
  • Comprehensiveness. The data in the Datastore Statistics tab is more extensive than what is available via metadata queries.
  • Speed. Metadata queries are billed in the same way as ordinary queries, but are generally slower to execute. As a rule of thumb, expect a metadata query that returns N entities to take about the same time as N ordinary queries each returning a single entity. Furthermore, property representation queries (non-keys-only property queries) are slower than keys-only property queries.

This page describes the following aspects of metadata queries:

Metadata Queries in Java

The Java class Query, defined in the com.google.appengine.api.datastore package, provides three special entity kinds that are reserved for metadata queries. They are denoted by static constants of the Query class:

Static constant Entity kind
Query.NAMESPACE_METADATA_KIND __namespace__
Query.KIND_METADATA_KIND __kind__
Query.PROPERTY_METADATA_KIND __property__

These kinds will not conflict with others of the same names that may already exist in your application. By querying on these special kinds, you can retrieve entities containing the desired metadata.

The entities returned by metadata queries are generated dynamically, based on the current state of the Datastore. While you can create local Entity objects of kinds __namespace__, __kind__, or __property__, any attempt to store them in the Datastore will fail with an IllegalArgumentException.

The easiest way to issue metadata queries is with the low-level Datastore API. The following example prints the names of all namespaces in an application:

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Query;


void printAllNamespaces(DatastoreService ds, PrintWriter writer) {
  
  Query q = new Query(Query.NAMESPACE_METADATA_KIND);
  
  for (Entity e : ds.prepare(q).asIterable()) {
    // A nonzero numeric id denotes the default namespace;
    // see Namespace Queries, below
    if (e.getKey().getId() != 0) {
      writer.println("<default>");
    } else {
      writer.println(e.getKey().getName());
    }
  }

Namespace Queries

If your application uses the Namespaces API, you can use a namespace query to find all namespaces used in the application's entities. This allows you to perform activities such as administrative functions across multiple namespaces.

Namespace queries return entities of the special kind __namespace__ whose key name is the name of a namespace. (An exception is the default namespace designated by the empty string "": since the empty string is not a valid key name, this namespace is keyed with the numeric ID 1 instead.) Queries of this type support filtering only for ranges over the special pseudoproperty __key__, whose value is the entity's key. The results can be sorted by ascending (but not descending) __key__ value. Because __namespace__ entities have no properties, both keys-only and non-keys-only queries return the same information.

The following example returns a list of an application's namespaces in the range between two specified names, start and end:

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Query;

import java.util.ArrayList;
import java.util.List;


// Helper function to make key from namespace name
Key makeNamespaceKey(String namespace) {
  return KeyFactory.createKey(Query.NAMESPACE_METADATA_KIND, namespace);
}


List<String> getNamespaces(DatastoreService ds, String start, String end) {
  
  // Start with unrestricted namespace query
  Query q = new Query(Query.NAMESPACE_METADATA_KIND);
  
  // Limit to specified range, if any
  if (start != null) {
    q.addFilter(Entity.KEY_RESERVED_PROPERTY,
                Query.FilterOperator.GREATER_THAN_OR_EQUAL,
                makeNamespaceKey(start));
  }
  if (end != null) {
    q.addFilter(Entity.KEY_RESERVED_PROPERTY,
                Query.FilterOperator.LESS_THAN_OR_EQUAL,
                makeNamespaceKey(end));
  }
  
  // Initialize result list
  List<String> results = new ArrayList<String>();
  
  // Build list of query results
  for (Entity e : ds.prepare(q).asIterable()) {
    if (e.getKey().getId() != 0) { 
      results.add("");      // Nonzero numeric ID denotes the default namespace
    } else {
      results.add(e.getKey().getName());
    }
  }
  
  // Return result list
  return results;
}

Kind Queries

Kind queries return entities of kind __kind__ whose key name is the name of an entity kind. Queries of this type are implicitly restricted to the current namespace and support filtering only for ranges over the __key__ pseudoproperty. The results can be sorted by ascending (but not descending) __key__ value. Because __kind__ entities have no properties, both keys-only and non-keys-only queries return the same information.

The following example prints all kinds whose names start with a lowercase letter:

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Query;

import java.io.PrintWriter;


// Helper function to make key from kind name
Key makeKindKey(String kind) {
  return KeyFactory.createKey(Query.KIND_METADATA_KIND, kind);
}


void printLowercaseKinds(DatastoreService ds, PrintWriter writer) {
  
  // Start with unrestricted kind query
  Query q = new Query(Query.KIND_METADATA_KIND);
  
  // Limit to lowercase initial letters
  q.addFilter(Entity.KEY_RESERVED_PROPERTY,
              Query.FilterOperator.GREATER_THAN_OR_EQUAL,
              makeKindKey("a"));
  String endChar = Character.toString('z' + 1);           // Character after 'z'
  q.addFilter(Entity.KEY_RESERVED_PROPERTY,
              Query.FilterOperator.LESS_THAN,
              makeKindKey(endChar));
  
  // Print heading
  writer.println("Lowercase kinds:");
  
  // Print query results
  for (Entity e : ds.prepare(q).asIterable()) {
    writer.println("  " + e.getKey().getName());
  }
}

Property Queries

Property queries return entities of kind __property__ denoting the properties associated with an entity kind. The entity representing property P of kind K is built as follows:

  • The entity's key has kind __property__ and key name P.
  • The parent entity's key has kind __kind__ and key name K.

The behavior of a property query depends on whether it is a keys-only or a non-keys-only (property representation) query, as detailed in the subsections below.

Property Queries: Keys-Only

Keys-only property queries return a key for each indexed property of a specified entity kind. (Unindexed properties are not included.) The following example prints the names of all of an application's entity kinds and the properties associated with each:

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Query;

import java.io.PrintWriter;


void printProperties(DatastoreService ds, PrintWriter writer) {
  
  // Create unrestricted keys-only property query
  q = new Query(Query.PROPERTY_METADATA_KIND).setKeysOnly();
  
  // Print query results
  for (Entity e : ds.prepare(q).asIterable()) {
    writer.println(e.getKey().getParent().getName()
                     + ": "
                     + e.getKey().getName());
  }
}

Queries of this type are implicitly restricted to the current namespace and support filtering only for ranges over the pseudoproperty __key__, where the keys denote either __kind__ or __property__ entities. The results can be sorted by ascending (but not descending) __key__ value. Filtering is applied to kind-property pairs, ordered first by kind and second by property: for instance, if you have

  • kind Account with properties
    • balance
    • company
  • kind Employee with properties
    • name
    • ssn
  • kind Invoice with properties
    • date
    • amount
  • kind Manager with properties
    • name
    • title
  • kind Product with properties
    • description
    • price

then the query

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Query;

import java.io.PrintWriter;


// Helper function to make key from kind name
Key makeKindKey(String kind) {
  return KeyFactory.createKey(Query.KIND_METADATA_KIND, kind);
}


// Helper function to make property key from kind and property names
Key makePropertyKey(String kind, String property) {
  Key kindKey = makeKindKey(kind);
  return KeyFactory.createKey(kindKey, Query.PROPERTY_METADATA_KIND, property);
}


void printPropertyRange(DatastoreService ds, PrintWriter writer) {
  
  // Start with unrestricted keys-only property query
  q = new Query(Query.PROPERTY_METADATA_KIND).setKeysOnly();
  
  // Limit range
  q.addFilter(Entity.KEY_RESERVED_PROPERTY,
              Query.FilterOperator.GREATER_THAN_OR_EQUAL,
              makePropertyKey("Employee", "salary"));
  q.addFilter(Entity.KEY_RESERVED_PROPERTY,
              Query.FilterOperator.LESS_THAN_OR_EQUAL,
              makePropertyKey("Manager", "salary"));
  
  // Print query results
  for (Entity e : ds.prepare(q).asIterable()) {
    writer.println(e.getKey().getParent().getName()
                     + ": "
                     + e.getKey().getName());
  }
}

will print the following:

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

Notice that the results do not include the name property of kind Employee and the title property of kind Manager, nor any properties of kinds Account and Product, because they fall outside the range specified for the query.

Note: Since the __key__ kind filters on kind-property pairs, you can't use a single query for the same property name in different kinds. You can query for all properties in all kinds (or in a range of kinds), but you cannot construct a single query that retrieves, for example, all properties with names between m and q in all kinds (or in kinds A and B).

Property queries also support ancestor filtering on a __kind__ or __property__ key, to limit the query results to a single kind or property. You can use this, for instance, to get the properties associated with a given entity kind, as in the following example:

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Query;

import java.util.ArrayList;
import java.util.List;


// Helper function to make key from kind name
Key makeKindKey(String kind) {
  return KeyFactory.createKey(Query.KIND_METADATA_KIND, kind);
}


List propertiesOfKind(DatastoreService ds, String kind) {
  
  // Start with unrestricted keys-only property query
  Query q = new Query(Query.PROPERTY_METADATA_KIND).setKeysOnly();
  
  // Limit to specified kind
  q.setAncestor(makeKindKey(kind));
  
  // Initialize result list
  ArrayList results = new ArrayList();
  
  //Build list of query results
  for (Entity e : ds.prepare(q).asIterable()) {
    results.add(e.getKey().getName());
  }
  
  // Return result list
  return results;
}

Property Queries: Non-Keys-Only (Property Representation)

Non-keys-only property queries, known as property representation queries, return additional information on the representations used by each kind-property pair. The entity returned for property P of kind K has the same key as for a corresponding keys-only query, along with an additional property_representation property returning the property's representations. The value of this property is an instance of class java.util.Collection<String> containing one string for each representation of property P found in any entity of kind K.

Note that representations are not the same as property classes; multiple property classes can map to the same representation. (For example, java.lang.String and com.google.appengine.api.datastore.Blob both use the STRING representation.) The following table maps from property classes to their representations:

Property class Representation
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.Text STRING
com.google.appengine.api.datastore.ShortBlob STRING
com.google.appengine.api.datastore.Blob 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> Representation of T

The following example finds all representations of a specified property for a given entity kind:

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Query;

import java.util.Collection;


// Helper function to make key from kind name
Key makeKindKey(String kind) {
  return KeyFactory.createKey(Query.KIND_METADATA_KIND, kind);
}


// Helper function to make property key from kind and property names
Key makePropertyKey(String kind, String property) {
  Key kindKey = makeKindKey(kind);
  return KeyFactory.createKey(kindKey, Query.PROPERTY_METADATA_KIND, property);
}


Collection<String> representationsOfProperty(DatastoreService ds,
                                             String kind,
                                             String property) {
  
  // Start with unrestricted non-keys-only property query
  Query q = new Query(Query.PROPERTY_METADATA_KIND);
  
  // Limit to specified kind and property
  q.setAncestor(makePropertyKey(kind, property));
  
  // Get query result
  Entity propInfo = ds.prepare(q).asSingleEntity();
  
  // Return collection of property representations
  return (Collection<String>) propInfo.getProperty("property_representation");
}