Entitäten, Attribute und Schlüssel

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.

Datenobjekte in Cloud Datastore werden als Entitäten bezeichnet. Eine Entität verfügt über ein oder mehrere benannte Attribute, von denen jedes einen oder mehrere Werte haben kann. Entitäten derselben Art müssen nicht dieselben Attribute haben und die Werte der Entitäten für ein bestimmtes Attribut müssen nicht alle denselben Datentyp haben. Falls erforderlich, kann eine Anwendung derartige Einschränkungen in einem eigenen Datenmodell festlegen und durchsetzen.

Datastore unterstützt viele Datentypen für Attributwerte. Diese umfassen unter anderem:

  • Ganzzahlen
  • Gleitkommazahlen
  • Strings
  • Datumsangaben
  • Binärdaten

Eine vollständige Liste der Typen finden Sie unter Attribute und Werttypen.

Jede Entität in Datastore hat einen Schlüssel, der sie eindeutig identifiziert. Der Schlüssel besteht aus den folgenden Komponenten:

  • Namespace der Entität, der die Mehrinstanzenfähigkeit ermöglicht
  • Die Art der Entität, mit der sie für Datastore-Abfragen kategorisiert wird
  • Kennung für die einzelne Entität, entweder
    • ein Schlüsselnamen-String
    • eine ganzzahlige numerische ID
  • Optionaler Ancestor-Pfad zur Entität innerhalb der Datastore-Hierarchie

Eine Anwendung kann eine einzelne Entität mithilfe des Entitätsschlüssels aus Datastore abrufen oder eine bzw. mehrere Entitäten mit einer Abfrage ermitteln, die auf den Schlüsseln oder Attributwerten der Entitäten basiert.

Das Python App Engine SDK enthält eine Datenmodellierungsbibliothek zur Darstellung von Datastore-Entitäten als Instanzen von Python-Klassen sowie zum Speichern und Abrufen dieser Instanzen in Cloud Datastore.

Datastore selbst erzwingt keine Einschränkungen für die Struktur von Entitäten, beispielsweise ob ein bestimmtes Attribut einen Wert eines bestimmten Typs hat. Diese Aufgabe bleibt der Anwendung und der Datenmodellierungsbibliothek überlassen.

Arten und Kennungen

Jede Datastore-Entität ist von einer bestimmten Art. Mit dieser kann die Entität für Abfragen kategorisiert werden. In einer Personalanwendung kann beispielsweise jeder Mitarbeiter eines Unternehmens mit einer Entität der Art Employee dargestellt werden. In der Python Datastore API wird die Art einer Entität durch ihre Modellklasse bestimmt, die Sie in Ihrer Anwendung als abgeleitete Klasse der Klasse db.Model der Datenmodellierungsbibliothek festlegen. Der Name der Modellklasse wird zur Art der zugehörigen Entitäten. Alle Artnamen, die mit zwei Unterstrichen (__) beginnen, sind reserviert und dürfen nicht verwendet werden.

Der folgende Beispielcode erstellt eine Entität der Art Employee, gibt ihre Attributwerte an und speichert die Entität in Datastore:

import datetime
from google.appengine.ext import db


class Employee(db.Model):
  first_name = db.StringProperty()
  last_name = db.StringProperty()
  hire_date = db.DateProperty()
  attended_hr_training = db.BooleanProperty()


employee = Employee(first_name='Antonio',
                    last_name='Salieri')

employee.hire_date = datetime.datetime.now().date()
employee.attended_hr_training = True

employee.put()

Die Klasse Employee deklariert vier Attribute für das Datenmodell: first_name, last_name, hire_date und attended_hr_training. Die Basisklasse Model gewährleistet, dass die Attribute von Employee-Objekten diesem Modell entsprechen: z. B. ein Versuch, dem Attribut hire_date einen Stringwert zuzuweisen, zu einem Laufzeitfehler führt, da das Datenmodell für hire_date wurde als db.DateProperty deklariert.

Neben einem Typ verfügt jede Entität über eine Kennung, die beim Erstellen der Entität zugewiesen wird. Weil sie zum Schlüssel der Entität gehört, ist die Kennung dauerhaft mit der Entität verknüpft und kann nicht geändert werden. Es gibt zwei Möglichkeiten der Zuweisung:

  • Die Anwendung kann ihren eigenen Schlüsselnamen-String für die Entität angeben.
  • Datastore kann der Entität automatisch eine ganzzahlige numerische ID zuweisen.

Wenn Sie einer Entität einen Schlüsselnamen zuweisen möchten, geben Sie beim Erstellen der Entität für den Modellklassenkonstruktor das benannte Argument key_name an:

# Create an entity with the key Employee:'asalieri'.
employee = Employee(key_name='asalieri')

Soll Cloud Datastore automatisch eine numerische ID zuweisen, lassen Sie das Argument key_name weg:

# Create an entity with a key such as Employee:8261.
employee = Employee()

Kennungen zuweisen

Datastore kann so konfiguriert werden, dass automatische IDs mithilfe von zwei verschiedenen Richtlinien für automatische IDs generiert werden:

  • Die Richtlinie default generiert eine zufällige Abfolge von bisher nicht verwendeten IDs, die näherungsweise gleichmäßig verteilt sind. Jede ID kann maximal 16 Dezimalstellen enthalten.
  • Die Richtlinie legacy erstellt eine Abfolge nicht aufeinanderfolgender IDs aus kleineren Ganzzahlen.

Wenn Sie die Entitäts-IDs für den Nutzer anzeigen möchten und/oder deren Reihenfolge wichtig ist, ist eine manuelle Zuordnung die beste Lösung.

Datastore generiert eine zufällige Abfolge von nicht verwendeten IDs, die annähernd gleichmäßig verteilt sind. Jede ID kann maximal 16 Dezimalstellen enthalten.

Ancestor-Pfade

Entitäten in Cloud Datastore sind hierarchisch organisiert, ähnlich der Verzeichnisstruktur eines Dateisystems. Wenn Sie eine Entität erstellen, können Sie optional eine weitere Entität als übergeordnetes Element angeben. Die neue Entität ist dann ein untergeordnetes Element der übergeordneten Entität. Im Gegensatz zu einem Dateisystem muss die übergeordnete Entität nicht tatsächlich vorhanden sein. Eine Entität ohne übergeordnetes Element wird als Stammentität bezeichnet. Die Verknüpfung zwischen einer Entität und ihrer übergeordneten Entität ist dauerhaft und kann nicht geändert werden, nachdem die Entität erstellt wurde. Cloud Datastore weist zwei Entitäten mit derselben übergeordneten Entität oder zwei Stammentitäten (Entitäten ohne übergeordnete Entität) niemals dieselbe numerische ID zu.

Alle übergeordneten Elemente einer Entität werden als ihre Ancestors bezeichnet und alle untergeordneten Entitäten sind ihre Nachfolger. Eine Stammentität und alle ihre Nachfolger gehören zu derselben Entitätengruppe. Die Abfolge der Entitäten, von einer Stammentität über die untergeordneten Elemente bis zu einer bestimmten Entität, bildet den Ancestor-Pfad. Der vollständige Schlüssel, der die Entität identifiziert, besteht aus einer Abfolge von Art/Kennungs-Paaren, die den Ancestor-Pfad angeben und mit dem Paar der Entität selbst enden:

[Person:GreatGrandpa, Person:Grandpa, Person:Dad, Person:Me]

Bei einer Stammentität ist der Ancestor-Pfad leer und der Schlüssel besteht ausschließlich aus der eigenen Art und der eigenen Kennung der Entität:

[Person:GreatGrandpa]

Dieses Konzept wird anhand des folgenden Diagramms veranschaulicht:

Zeigt die Beziehung der Stammentität zu den untergeordneten Entitäten in der Entitätengruppe an

Zur Angabe der übergeordneten Entität einer Entität verwenden Sie beim Erstellen der untergeordneten Entität das Argument parent für den Modellklassenkonstruktor. Der Wert dieses Arguments kann die übergeordnete Entität selbst oder ihr Schlüssel sein. Der Schlüssel lässt sich durch Aufrufen der Methode key() der übergeordneten Entität ermitteln. Im folgenden Beispiel wird eine Entität der Art Address erstellt. Außerdem werden zwei Möglichkeiten zum Angeben einer Entität der Art Employee als deren übergeordnete Entität gezeigt:

# Create Employee entity
employee = Employee()
employee.put()

# Set Employee as Address entity's parent directly...
address = Address(parent=employee)

# ...or using its key
e_key = employee.key()
address = Address(parent=e_key)

# Save Address entity to datastore
address.put()

Transaktionen und Entitätengruppen

Jeder Vorgang zum Erstellen, Aktualisieren und Löschen einer Entität findet im Rahmen einer Transaktion statt. Eine einzelne Transaktion kann eine beliebige Anzahl solcher Vorgänge umfassen. Um die Konsistenz der Daten zu wahren, sorgt die Transaktion dafür, dass entweder alle enthaltenen Vorgänge auf Datastore als Einheit angewendet werden oder überhaupt nicht, sofern einer der Vorgänge fehlgeschlagen ist. Darüber hinaus greifen alle strikt konsistenten Lesevorgänge (Ancestor-Abfragen oder -Abrufe) innerhalb derselben Transaktion auf einen konsistenten Snapshot der Daten zurück.

Wie oben bereits erwähnt besteht eine Entitätengruppe aus einer Reihe von Entitäten, die über einen Ancestor-Pfad mit einem gemeinsamen Stammelement verbunden sind. Die Anordnung von Daten in Entitätengruppen kann einschränken, welche Transaktionen ausgeführt werden können:

  • Alle Daten, auf die eine Transaktion zugreift, müssen in maximal 25 Entitätengruppen enthalten sein.
  • Wenn Sie Abfragen innerhalb einer Transaktion verwenden möchten, müssen die Daten so in Entitätengruppen angeordnet werden, dass Sie Ancestor-Filter angeben können, die mit den richtigen Daten übereinstimmen.
  • Es gibt einen Schreibdurchsatzgrenzwert von ca. einer Transaktion pro Sekunde innerhalb einer einzelnen Entitätengruppe. Diese Begrenzung ist vorhanden, weil Datastore eine synchrone Replikation ohne Master jeder Entitätengruppe über einen breiten geografischen Bereich hinweg durchführt, um hohe Verlässlichkeit und Fehlertoleranz zu ermöglichen.

In vielen Anwendungen kann es akzeptabel sein, für den Abruf einer breiten Ansicht nicht zusammengehöriger Daten Eventual Consistency zu verwenden, also eine entitätengruppenübergreifende Nicht-Ancestor-Abfrage, die gelegentlich etwas veraltete Daten zurückgeben kann. Anschließend wird dann strikte Konsistenz (eine Ancestor-Abfrage oder ein get einer einzelnen Entität) verwendet, um eine einzelne Gruppe von eng zusammengehörigen Daten anzusehen oder zu bearbeiten. In derartigen Anwendungen ist die Gruppierung von eng zusammengehörigen Daten in Entitätsgruppen im Allgemeinen ein guter Ansatz. Weitere Informationen finden Sie unter Für strikte Konsistenz strukturieren.

Attribute und Werttypen

Die mit einer Entität verknüpften Datenwerte bestehen aus einem oder mehreren Attributen. Jedes Attribut hat einen Namen und einen oder mehrere Werte. Ein Attribut kann Werte mit mehr als einem Typ haben und zwei Entitäten können Werte unterschiedlichen Typs für dasselbe Attribut haben. Attribute können indexiert oder nicht indexiert sein. Abfragen, die nach einem Attribut A sortieren oder filtern, ignorieren Entitäten, bei denen A nicht indexiert ist. Eine Entität kann höchstens 20.000 indexierte Attribute haben.

Die folgenden Werttypen werden unterstützt:

Werttyp Python-Typ(en) Sortierreihenfolge Hinweise
Ganzzahl int
long
Numerisch 64-Bit-Ganzzahl, signiert
Gleitkommazahl float Numerisch 64 Bit mit doppelter Genauigkeit,
IEEE 754
Boolesch bool False<True
Textstring (kurz) str
unicode
Unicode
(str wird als ASCII behandelt)
Bis zu 1.500 Byte
Textstring (lang) db.Text Keine Bis zu 1 Megabyte

Nicht indexiert
Bytestring (kurz) db.ByteString Bytereihenfolge Bis zu 1.500 Byte
Bytestring (lang) db.Blob Keine Bis zu 1 Megabyte

Nicht indexiert
Datum und Uhrzeit datetime.date
datetime.time
datetime.datetime
Chronologisch
Geografischer Punkt db.GeoPt Nach Breitengrad,
dann nach Längengrad
Postanschrift db.PostalAddress Unicode
Telefonnummer db.PhoneNumber Unicode
E-Mail-Adresse db.Email Unicode
Nutzer eines Google-Kontos users.User E-Mail-Adresse
in Unicode-Reihenfolge
Instant-Messaging-Handle db.IM Unicode
Link db.Link Unicode
Kategorie db.Category Unicode
Bewertung db.Rating Numerisch
Datastore-Schlüssel db.Key Nach Pfadelementen
(Art, Kennung,
Art, Kennung...)
Blobstore-Schlüssel blobstore.BlobKey Bytereihenfolge
Null NoneType Keine

Wichtig: Es wird dringend empfohlen, nicht UserProperty zu speichern, da diese die E-Mail-Adresse und die eindeutige ID des Nutzers enthält. Wenn ein Nutzer seine E-Mail-Adresse ändert und Sie das alte gespeicherte Objekt User mit dem neuen User-Wert vergleichen, würden sie nicht übereinstimmen.

Für Textstrings und nicht codierte Binärdaten (Bytestrings) unterstützt Datastore zwei Werttypen:

  • Kurze Strings (bis zu 1.500 Byte) werden indexiert und können in Filterbedingungen der Abfrage und in Sortierfolgen verwendet werden.
  • Lange Strings (bis zu 1 Megabyte) werden nicht indexiert und können nicht in Abfragefiltern und Sortierfolgen verwendet werden.
Hinweis: Der lange Bytestringtyp lautet in der Datastore API Blob. Dieser Typ steht nicht im Zusammenhang mit den Blobs in der Blobstore API.

Wenn eine Abfrage ein Attribut mit Werten verschiedener Typen enthält, verwendet Datastore eine deterministische Sortierung anhand der internen Darstellungen:

  1. Nullwerte
  2. Festkommazahlen
    • Ganzzahlen
    • Datums- und Uhrzeitwerte
    • Bewertungen
  3. Boolesche Werte
  4. Bytesequenzen
    • Bytestring
    • Unicode-String
    • Blobstore-Schlüssel
  5. Gleitkommazahlen
  6. Geografische Punkte
  7. Nutzer von Google-Konten
  8. Datastore-Schlüssel

Da lange Textstrings und lange Bytestrings nicht indexiert werden, ist keine Reihenfolge definiert.

Mit Entitäten arbeiten

Anwendungen können mit der Datastore API Entitäten erstellen, abrufen, aktualisieren und löschen. Wenn die Anwendung den vollständigen Schlüssel für eine Entität kennt (oder ihn aus dem übergeordneten Schlüssel, der Art und der Kennung ableiten kann), kann sie mit dem Schlüssel direkt mit der Entität arbeiten. Eine Anwendung kann den Schlüssel einer Entität auch als Ergebnis einer Datastore-Abfrage abrufen. Weitere Informationen finden Sie unter Datastore-Abfragen.

Entität erstellen

Zum Erstellen einer neuen Entität in Python müssen Sie eine Instanz einer Modellklasse anlegen, bei Bedarf deren Attribute angeben und ihre Methode put() aufrufen, um sie in Datastore zu speichern. Wenn Sie den Schlüsselnamen der Entität angeben möchten, übergeben Sie das Argument "key_name" an den Konstruktor:

employee = Employee(key_name='asalieri',
                    first_name='Antonio',
                    last_name='Salieri')

employee.hire_date = datetime.datetime.now().date()
employee.attended_hr_training = True

employee.put()

Wenn Sie keinen Schlüsselnamen angeben, generiert Datastore automatisch eine numerische ID für den Schlüssel der Entität:

employee = Employee(first_name='Antonio',
                    last_name='Salieri')

employee.hire_date = datetime.datetime.now().date()
employee.attended_hr_training = True

employee.put()

Entität abrufen

Wenn Sie eine Entität mit einem bestimmten Schlüssel abrufen möchten, übergeben Sie das Key-Objekt als Argument an die db.get()-Funktion. Sie können das Key-Objekt mit der Klassenmethode Key.from_path() generieren. Der vollständige Pfad ist eine Abfolge von Entitäten im Ancestor-Pfad, wobei jede Entität durch ihre Art (ein String) gefolgt von ihrer Kennung (Schlüsselname oder numerische ID) dargestellt wird:

address_k = db.Key.from_path('Employee', 'asalieri', 'Address', 1)
address = db.get(address_k)

db.get() gibt eine Instanz der entsprechenden Modellklasse zurück. Bestätigen Sie, dass Sie die Modellklasse für die abzurufende Entität importiert haben.

Entität aktualisieren

Wenn Sie eine vorhandene Entität aktualisieren möchten, ändern Sie die Attribute des Objekts und rufen dann die zugehörige Methode put() auf. Die bestehende Entität wird mit den Objektdaten überschrieben. Bei jedem Aufruf von put() wird das ganze Objekt an Datastore gesendet.

Zum Löschen eines Attributs löschen Sie das Attribut aus dem Python-Objekt:

del address.postal_code

Speichern Sie dann das Objekt.

Entität löschen

Sie können eine Entität unter Angabe ihres Schlüssels mit der Funktion db.delete() löschen.

address_k = db.Key.from_path('Employee', 'asalieri', 'Address', 1)
db.delete(address_k)

Es besteht auch die Möglichkeit, dafür die Methode delete() der Entität aufzurufen:

employee_k = db.Key.from_path('Employee', 'asalieri')
employee = db.get(employee_k)

# ...

employee.delete()

Batchvorgänge

Für die Funktionen db.put(), db.get() und db.delete() (und ihre asynchronen Entsprechungen db.put_async(), db.get_async() und db.delete_async()) können Listenargumente angegeben werden, um einen Vorgang für mehrere Entitäten in einem einzigen Datastore-Aufruf auszuführen:

# A batch put.
db.put([e1, e2, e3])

# A batch get.
entities = db.get([k1, k2, k3])

# A batch delete.
db.delete([k1, k2, k3])

Batch-Vorgänge ändern nichts an Ihren Kosten. Jeder Schlüssel in einem Batchvorgang wird unabhängig davon in Rechnung gestellt, ob der Schlüssel vorhanden ist oder nicht. Die Größe der Entitäten in einem Vorgang wirkt sich nicht auf die Kosten aus.

.

Entitäten im Bulk löschen

Wenn Sie eine große Anzahl von Entitäten löschen müssen, empfehlen wir mit Dataflow Entitäten im Bulk zu löschen.

Leere Liste verwenden

Für die NDB-Schnittstelle hat Datastore in der Vergangenheit eine leere Liste als ausgelassenes Attribut für statische und dynamische Attribute geschrieben. Zur Aufrechterhaltung der Abwärtskompatibilität bleibt dies das Standardverhalten. Wenn Sie dieses Verhalten global oder pro ListProperty-Basis überschreiben möchten, legen Sie für das Argument write_empty_list in der Attributklasse den Wert true fest. Die leere Liste wird dann in Datastore geschrieben und kann als leere Liste gelesen werden.

Für die DB-Schnittstelle waren Schreibvorgänge mit leeren Listen in der Vergangenheit bei dynamischen Attributen in keinem Fall zulässig. Wenn Sie solche Vorgänge ausführten, kam es zu einem Fehler. Dies bedeutet, dass kein Standardverhalten zwecks Abwärtskompatibilität für dynamische DB-Attribute beibehalten werden muss und Sie die leere Liste im dynamischen Modell ohne Änderungen einfach schreiben und lesen können.

Bei statischen DB-Attributen wurde die leere Liste jedoch als ausgelassenes Attribut geschrieben. Dieses Verhalten wird aus Gründen der Abwärtskompatibilität standardmäßig beibehalten. Wenn Sie leere Listen für statische DB-Attribute aktivieren möchten, legen Sie für das Argument write_empty_list in der Attributklasse den Wert true fest. Die leere Liste wird dann in Datastore geschrieben.