Metadata

Hinweis: Entwicklern von neuen Anwendungen wird dringend empfohlen, die NDB-Clientbibliothek zu verwenden. Diese bietet im Vergleich zur vorliegenden Clientbibliothek verschiedene Vorteile, z. B. das automatische Caching von Entitäten über die Memcache API. Wenn Sie derzeit die ältere DB-Clientbibliothek verwenden, finden Sie weitere Informationen im Leitfaden zur Migration von DB- zu NDB-Clientbibliotheken.

Datastore bietet programmgesteuerten Zugriff auf einige seiner Metadaten, um die Metaprogrammierung zu unterstützen, Backend-Verwaltungsfunktionen zu implementieren, konsistentes Caching zu vereinfachen und ähnliche Zwecke zu erfüllen. Sie können beispielsweise einen benutzerdefinierten Datastore-Betrachter für Ihre Anwendung erstellen. Die verfügbaren Metadaten umfassen Informationen zu den Entitätsgruppen, Namespaces, Entitätsarten und Attributen, die Ihre Anwendung verwendet, sowie die Attributdarstellungen für die einzelnen Attribute.

Das Datastore-Dashboard in der Google Cloud Console stellt auch einige Metadaten zu Ihrer Anwendung bereit. Die hier angezeigten Daten unterscheiden sich jedoch in einigen wichtigen Bereichen von den Daten, die von diesen Funktionen zurückgegeben werden.

  • Aktualität. Wenn Metadaten mit der API gelesen werden, werden aktuelle Daten abgerufen. Daten im Dashboard werden nur einmal täglich aktualisiert.
  • Inhalt. Einige Metadaten im Dashboard sind über die APIs nicht verfügbar und umgekehrt.
  • Geschwindigkeit. Metadatenabrufe und -abfragen werden wie Datenspeicherabrufe und -abfragen abgerechnet. Die Ausführung von Metadatenabfragen, die Informationen zu Namespaces, Arten und Attributen abrufen, ist im Allgemeinen langsam. Als Faustregel gehen Sie davon aus, dass eine Metadatenabfrage, die n Entitäten zurückgibt, etwa genauso lange dauert wie n normale Abfragen, die jeweils eine einzelne Entität zurückgeben. Außerdem sind Attributdarstellungsabfragen (nicht ausschließlich schlüsselbasierte Attributabfragen) langsamer als ausschließlich schlüsselbasierte Attributabfragen. Abrufe von Metadaten aus Entitätsgruppen sind etwas schneller als Abrufe aus regulären Entitäten.

Hilfsfunktionen

Die folgenden Funktionen erhalten Metadateninformationen:

  • get_entity_group_version() ruft eine Versionsnummer für eine Entitätengruppe ab. Dies ist nützlich, um herauszufinden, ob sich eine Entität in der Gruppe seit dem letzten Abruf der Versionsnummer geändert hat.
  • get_namespaces() gibt eine Liste zurück, die die Namen aller Namespaces einer Anwendung oder innerhalb eines angegebenen Bereichs enthält.
  • get_kinds() gibt eine Liste zurück, die die Namen aller Entitätsarten einer Anwendung oder innerhalb eines angegebenen Bereichs enthält.
  • get_properties_of_kind() gibt eine Liste zurück, die die Namen aller indexierten Attribute einer Anwendung enthält, die mit einer bestimmten Entitätsart verknüpft sind. Nicht indexierte Attribute sind nicht enthalten.
  • get_representations_of_kind() gibt ein Wörterbuch zurück, das die Darstellungen für alle indexierten Attribute einer Anwendung oder für diejenigen in einem angegebenen Bereich enthält, die einer bestimmten Entitätsart zugeordnet sind. Das Wörterbuch ordnet den Namen jedes Atrributs einer Liste der Darstellungen des jeweiligen Attributs zu. Nicht indexierte Attribute sind nicht enthalten.

Metadaten aus Entitätengruppen

Cloud Datastore bietet Zugriff auf die "Version" einer Entitätengruppe, eine strikt positive Zahl, die sich bei jeder Änderung der Entitätengruppe garantiert erhöht.

Das folgende Beispiel zeigt, wie Sie die Version einer Entitätengruppe abrufen können:

from google.appengine.ext import db
from google.appengine.ext.db import metadata

class Simple(db.Model):
  x = db.IntegerProperty()

entity1 = Simple(x=11)
entity1.put()

# Print entity1's entity group version
print 'version', metadata.get_entity_group_version(entity1)

# Write to a different entity group
entity2 = Simple(x=22)
entity2.put()

# Will print the same version, as entity1's entity group has not changed
print 'version', metadata.get_entity_group_version(entity1)

# Change entity1's entity group by adding a new child entity
entity3 = Simple(x=33, parent=entity1.key())
entity3.put()

# Will print a higher version, as entity1's entity group has changed
print metadata.get_entity_group_version(entity1)

Legacy-Verhalten

Im Versionsverhalten der Legacy-Entitätengruppe wird die Version der Entitätengruppe nur bei Änderungen an der Entitätengruppe erhöht. Das Metadatenverhalten der Legacy-Entitätengruppen könnte beispielsweise dazu verwendet werden, einen konsistenten Cache einer komplexen Ancestor-Abfrage für eine Entitätengruppe beizubehalten.

In diesem Beispiel werden Abfrageergebnisse im Cache gespeichert (Anzahl der übereinstimmenden Ergebnisse) und das Legacy-Verhalten von Entitätengruppenversionen verwendet, um den im Cache gespeicherten Wert zu verwenden, wenn er aktuell ist:

from google.appengine.api import memcache
from google.appengine.ext import db
from google.appengine.ext.db import metadata

def count_entity_group(entity_group_key):
  """Count the entities in the specified entity group."""
  # Check if we have a cached version of the current entity group count
  cached = memcache.get(str(entity_group_key))
  if cached:
    (version, count) = cached
    # Is the cached value for the current version?
    if version == metadata.get_entity_group_version(entity_group_key):
      return count

  def tx():
    # Need to actually count entities. Using a transaction to get a consistent
    # count and entity group version.
    count = db.Query(keys_only=True).ancestor(entity_group_key).count(limit=5000)
    # Cache the count and the entity group version
    version = metadata.get_entity_group_version(entity_group_key)
    memcache.set(str(entity_group_key), (version, count))
    return count

  return db.run_in_transaction(tx)

get_entity_group_version() kann None für eine Entitätengruppe zurückgeben, in die noch nie geschrieben wurde.

Entitätengruppenversionen werden durch Aufrufe von get() für eine spezielle Pseudo-Entität abgerufen, die ein __version__-Attribut enthält. Einzelheiten finden Sie in der Referenzdokumentation zu EntityGroup.

Metadatenabfragen

Wenn die im vorherigen Abschnitt beschriebenen Hilfsfunktionen nicht Ihren Anforderungen entsprechen, können Sie über eine explizite Metadatenabfrage komplexere oder flexiblere Metadatenanforderungen erstellen. In Python sind die Modellklassen für solche Abfragen im Paket google.appengine.ext.db.metadata definiert. Diese Modelle stellen spezielle Entitätsarten bereit, die für Metadatenabfragen reserviert sind:

Modellklasse Entitätsart
Namespace __namespace__
Kind __kind__
Property __property__

Diese Modelle und Arten stehen nicht in Konflikt mit anderen desselben Namens, die möglicherweise bereits in Ihrer Anwendung vorhanden sind. Durch Abfragen dieser besonderen Arten können Sie Entitäten abrufen, die die gewünschten Metadaten enthalten.

Die von Metadatenabfragen zurückgegebenen Entitäten werden basierend auf dem aktuellen Status von Datastore dynamisch generiert. Sie können zwar lokale Instanzen der Modellklassen Namespace, Kind oder Property erstellen, aber jeder Versuch, sie im Datastore zu speichern, scheitert mit einer BadRequestError-Ausnahme.

Sie können Metadatenabfragen mit einem Abfrageobjekt ausführen, das einer der beiden folgenden Klassen angehört:

  • Ein Query-Objekt, das von der Klassenmethode Namespace.all(), Kind.all() oder Property.all() zurückgegeben wird (übernommen von der Superklassenmethode Model.all())
  • Ein GqlQuery-Objekt für Abfragen im GQL-Stil

Im folgenden Beispiel werden die Namen aller Entitätsarten in einer Anwendung zurückgegeben:

from google.appengine.ext import db
from google.appengine.ext.db.metadata import Kind

for k in Kind.all():
  print "kind: '%s'" % k.kind_name

Namespace-Abfragen

Wenn die Anwendung die Namespaces API verwendet, können Sie mit einer Namespace-Abfrage alle Namespaces ermitteln, die in den Entitäten der Anwendung verwendet werden. Auf diese Weise können Sie Aktivitäten, wie administrative Funktionen, über mehrere Namespaces hinweg ausführen.

Namespace-Abfragen geben Entitäten der speziellen Art __namespace__ zurück, deren Schlüsselname der Name eines Namespace ist. Eine Ausnahme bildet der Standard-Namespace, der mit dem leeren String "" angegeben wird. Weil der leere String kein gültiger Schlüsselname ist, wird dieser Namespace stattdessen mit der numerischen ID 1 eingegeben. Bei Abfragen dieses Typs wird die Filterung nur für Bereiche über dem speziellen Pseudoattribut __key__ unterstützt, dessen Wert der Schlüssel der Entität ist. Die Ergebnisse können nach aufsteigendem (nicht jedoch nach absteigendem) __key__-Wert sortiert werden. Weil __namespace__-Entitäten keine Attribute haben, geben sowohl Abfragen, die ausschließlich schlüsselbasiert sind, als auch Abfragen, die nicht ausschließlich schlüsselbasiert sind, dieselben Informationen zurück.

Namespace-Entitäten sind Instanzen der Modellklasse google.appengine.ext.db.metadata.Namespace. Das aus dem Schlüssel der Entität berechnete Stringattribut namespace_name gibt den Namen des entsprechenden Namespace zurück. (Wenn der Schlüssel die numerische ID 1 hat, gibt das Attribut einen leeren String zurück.) Das Namespace-Namespace-Modell stellt die folgenden Klassenmethoden bereit, um die Abfrage zu erleichtern:

Als Beispiel dient hier die Implementierung der Hilfsfunktion get_namespaces(), die eine Liste mit den Namen aller Namespaces einer Anwendung (oder jener im Bereich zwischen zwei angegebenen Namen, "start" und "end") zurückgibt:

from google.appengine.ext import db
from google.appengine.ext.db.metadata import Namespace

def get_namespaces(start=None, end=None):

  # Start with unrestricted namespace query
  q = Namespace.all()

  # Limit to specified range, if any
  if start is not None:
    q.filter('__key__ >=', Namespace.key_for_namespace(start))
  if end is not None:
    q.filter('__key__ <', Namespace.key_for_namespace(end))

  # Return list of query results
  return [ns.namespace_name for ns in q]

Artabfragen

Artabfragen geben Entitäten der Art __kind__ zurück, deren Schlüsselname der Name einer Entitätsart ist. Abfragen dieses Typs sind implizit auf den aktuellen Namespace begrenzt und unterstützen die Filterung nur für Bereiche über dem Pseudoattribut __key__. Die Ergebnisse können nach aufsteigendem (nicht jedoch nach absteigendem) __key__-Wert sortiert werden. Weil __kind__-Entitäten keine Attribute haben, geben sowohl ausschließlich schlüsselbasierte als auch nicht ausschließlich schlüsselbasierte Abfragen dieselben Informationen zurück.

Artentitäten sind Instanzen der Modellklasse google.appengine.ext.db.metadata.Kind. Das Stringattribut kind_name wird aus dem Schlüssel der Entität berechnet und gibt den Namen der entsprechenden Entitätsart zurück. Das Kind-Modell stellt die folgenden Klassenmethoden bereit, um die Abfrage zu erleichtern:

Als Beispiel dient hier die Implementierung der Hilfsfunktion get_kinds(), die eine Liste mit den Namen aller Namespaces einer Anwendung (oder jener im Bereich zwischen zwei angegebenen Namen, start und end) zurückgibt:

from google.appengine.ext import db
from google.appengine.ext.db.metadata import Kind

def get_kinds(start=None, end=None):

  # Start with unrestricted kind query
  q = Kind.all()

  # Limit to specified range, if any
  if start is not None and start != '':
    q.filter('__key__ >=', Kind.key_for_kind(start))
  if end is not None:
    if end == '':
      return []        # Empty string is not a valid kind name, so can't filter
    q.filter('__key__ <', Kind.key_for_kind(end))

  # Return list of query results
  return [k.kind_name for k in q]

Im folgenden Beispiel werden alle Arten ausgegeben, deren Namen mit einem Kleinbuchstaben beginnen:

from google.appengine.ext import db
from google.appengine.ext.db.metadata import Kind

# Start with unrestricted kind query
q = Kind.all()

# Limit to lowercase initial letters
q.filter('__key__ >=', Kind.key_for_kind('a'))
endChar = chr(ord('z') + 1)                        # Character after 'z'
q.filter('__key__ <', Kind.key_for_kind(endChar))

# Print query results
for k in q:
  print k.kind_name

Attributabfragen

Attributabfragen geben Entitäten der Art __property__ zurück. Diese kennzeichnen die Attribute, die mit einer Entitätsart verknüpft sind, und zwar unabhängig davon, ob diese Attribute aktuell im Modell dieser Art definiert sind. Die Entität zur Darstellung von Attribut P der Art K wird so erstellt:

  • Der Entitätsschlüssel hat die Art __property__ und den Schlüsselnamen P.
  • Der Schlüssel der übergeordneten Entität ist vom Typ __kind__ und hat den Schlüsselnamen K.

Attributentitäten sind Instanzen der Modellklasse google.appengine.ext.db.metadata.Property. Die Stringattribute kind_name und property_namewerden aus dem Schlüssel der Entität berechnet und geben den Namen der entsprechenden Art und des entsprechenden Attributs zurück. Das Property-Modell bietet vier Klassenmethoden, um das Erstellen und Untersuchen von __property__-Schlüsseln zu erleichtern:

  • Property.key_for_kind()erstellt einen übergeordneten __kind__-Schlüssel für __property__-Schlüssel einer angegebenen Entitätsart.
  • Property.key_for_property() erstellt einen Schlüssel __property__ für eine angegebene Art und ein angegebenes Attribut.
  • Property.key_to_kind() gibt den mit einem __property__-Schlüssel verknüpften Typnamen zurück.
  • Property.key_to_property() gibt den Attributnamen zurück, der mit einem __property__-Schlüssel verknüpft ist (oder None, wenn der Schlüssel nur eine Art angibt).

Das folgende Beispiel veranschaulicht diese Methoden:

from google.appengine.ext import db
from google.appengine.ext.db.metadata import Property

class Employee(db.Model):
  name = db.StringProperty()
  ssn = db.IntegerProperty()

employee_key = Property.key_for_kind("Employee")
employee_name_key = Property.key_for_property("Employee", "Name")

Property.key_to_kind(employee_key)           # Returns "Employee"
Property.key_to_property(employee_name_key)  # Returns "Name"

Das Verhalten einer Attributabfrage hängt davon ab, ob es sich um eine ausschließlich schlüsselbasierte oder um eine nicht ausschließlich schlüsselbasierte (Attributsdarstellung) Abfrage handelt, wie in den folgenden Unterabschnitten beschrieben.

Attributabfragen: ausschließlich schlüsselbasiert

Ausschließlich schlüsselbasierte Attributabfragen geben für jedes indexierte Attribut einer angegebenen Entitätsart einen Schlüssel zurück. Nicht indexierte Attribute werden nicht einbezogen. Im folgenden Beispiel werden die Namen aller Entitätsarten einer Anwendung und die mit jeder Art verknüpften Attribute ausgegeben:

from google.appengine.ext import db
from google.appengine.ext.db.metadata import Property

# Create unrestricted keys-only property query
q = Property.all(keys_only=True)

# Print query results
for p in q:
  print "%s: %s" % (Property.key_to_kind(p), Property.key_to_property(p))

Abfragen dieser Art sind implizit auf den aktuellen Namespace beschränkt und unterstützen nur das Filtern für Bereiche oberhalb des Pseudoattributs __key__, wobei die Schlüssel __kind__- oder __property__-Entitäten kennzeichnen. Die Ergebnisse können nach aufsteigendem (nicht jedoch nach absteigendem) __key__-Wert sortiert werden. Die Filterung wird auf Art/Attribut-Paare angewendet, die zuerst nach Art und dann nach Attribut sortiert sind. Angenommen, Sie haben eine Entität mit den folgenden Attributen:

  • Art Account mit Attributen
    • balance
    • company
  • Art Employee mit Attributen
    • name
    • ssn
  • Art Invoice mit Attributen
    • date
    • amount
  • Art Manager mit Attributen
    • name
    • title
  • Art Product mit Attributen
    • description
    • price

Die Abfrage zum Zurückgeben der Attributdaten würde so aussehen:

from google.appengine.ext import db
from google.appengine.ext.db.metadata import Property

# Start with unrestricted keys-only property query
q = Property.all(keys_only=True)

# Limit range
q.filter('__key__ >=', Property.key_for_property("Employee", "salary"))
q.filter('__key__ <=', Property.key_for_property("Manager", "salary"))

# Print query results
for p in q:
  print "%s: %s" % (Property.key_to_kind(p), Property.key_to_property(p))

Bei der oben aufgeführten Abfrage würde Folgendes zurückgegeben:

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

Beachten Sie, dass die Ergebnisse weder das name-Attribut der Art Employee und das title-Attribut der Art Manager noch die Attribute der Arten Account und Product enthalten, weil sie außerhalb des für die Abfrage angegebenen Bereichs liegen.

Attributabfragen unterstützen auch die Ancestor-Filterung nach einem __kind__- oder __property__-Schlüssel, um die Abfrageergebnisse auf eine einzige Art oder ein einziges Attribut zu beschränken. Sie können diese Filterung beispielsweise verwenden, um die Attribute abzurufen, die einer bestimmten Entitätsart zugeordnet sind, wie im folgenden Beispiel gezeigt:

(eine Implementierung der Hilfsfunktion get_properties_of_kind())

from google.appengine.ext import db
from google.appengine.ext.db.metadata import Property

def get_properties_of_kind(kind, start=None, end=None):

  # Start with unrestricted keys-only property query
  q = Property.all(keys_only=True)

  # Limit to specified kind
  q.ancestor(Property.key_for_kind(kind))

  # Limit to specified range, if any
  if start is not None and start != '':
    q.filter('__key__ >=', Property.key_for_property(kind, start))
  if end is not None:
    if end == '':
      return []     # Empty string is not a valid property name, so can't filter
    q.filter('__key__ <', Property.key_for_property(kind, end))

  # Return list of query results
  return [Property.key_to_property(p) for p in q]

Attributabfragen: nicht ausschließlich schlüsselbasiert (Attributdarstellung)

Nicht ausschließlich schlüsselbasierte Attributabfragen, die als Attributdarstellungsabfragen bezeichnet werden, liefern zusätzliche Informationen zu den Darstellungen, die für jedes Art/Attribut-Paar verwendet werden. Nicht indexierte Attribute werden nicht einbezogen. Die Entität, die für das Attribut P der Art K zurückgegeben wird, hat denselben Schlüssel wie für eine entsprechende ausschließlich schlüsselbasierte Abfrage sowie ein weiteres property_representation-Attribut, das die Darstellungen des Attributs zurückgibt. Der Wert dieses Attributs ist eine Instanz der KlasseStringListProperty, das alle Darstellungen des Attributs von P in einer Entität der Art K enthält.

Beachten Sie, dass Darstellungen nicht mit Attributklassen identisch sind. Mehrere Attributklassen können derselben Darstellung zugeordnet sein. Beispiel: StringProperty und PhoneNumberProperty verwenden beide die STRING-Darstellung.

Die folgende Tabelle enthält die Zuordnungen von Attributklassen zu ihren Darstellungen:

Attributklasse Darstellung
IntegerProperty INT64
FloatProperty DOUBLE
BooleanProperty BOOLEAN
StringProperty STRING
ByteStringProperty STRING
DateProperty INT64
TimeProperty INT64
DateTimeProperty INT64
GeoPtProperty POINT
PostalAddressProperty STRING
PhoneNumberProperty STRING
EmailProperty STRING
UserProperty USER
IMProperty STRING
LinkProperty STRING
CategoryProperty STRING
RatingProperty INT64
ReferenceProperty
SelfReferenceProperty
REFERENCE
blobstore.BlobReferenceProperty STRING
ListProperty Listet die Darstellung des Elements auf
StringListProperty Listet die Darstellung des Elements auf

Hier sehen Sie als Beispiel die Implementierung der Hilfsfunktion get_representations_of_kind(), das ein Wörterbuch zurückgibt, das die Darstellungen für alle indexierten Attribute einer Anwendung enthält, oder für diejenigen, die sich im Bereich zwischen zwei angegebenen Namen befinden, start und end, die einer bestimmten Entitätsart zugeordnet sind. Das Wörterbuch ordnet den Namen jedes Attributs einer Liste der Darstellungen des jeweiligen Attributs zu.

from google.appengine.ext import db
from google.appengine.ext.db.metadata import Property

def get_representations_of_kind(kind, start=None, end=None):

  # Start with unrestricted non-keys-only property query
  q = Property.all()

  # Limit to specified kind
  q.ancestor(Property.key_for_kind(kind))

  # Limit to specified range, if any
  if start is not None and start != '':
    q.filter('__key__ >=', Property.key_for_property(kind, start))
  if end is not None:
    if end == '':
      return []     # Empty string is not a valid property name, so can't filter
    q.filter('__key__ <', Property.key_for_property(kind, end))

  # Initialize result dictionary
  result = {}

  # Add query results to dictionary
  for p in q:
    result[p.property_name] = p.property_representation

  # Return dictionary
  return result