Google Code offered in: English - Español - 日本語 - 한국어 - Português - Pусский - 中文(简体) - 中文(繁體)
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:
This page describes the following aspects of metadata queries:
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()); } }
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 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 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:
__property__
and key name P.__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.
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
Account
with properties
balance
company
Employee
with properties
name
ssn
Invoice
with properties
date
amount
Manager
with properties
name
title
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); } ListpropertiesOfKind(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; }
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"); }