Metadati

Nota: gli sviluppatori che creano nuove applicazioni sono vivamente incoraggiati a utilizzare la libreria client NDB, che offre diversi vantaggi rispetto a questa libreria client, ad esempio la memorizzazione nella cache automatica delle entità tramite l'API Memcache. Se al momento utilizzi la libreria client DB precedente, leggi la guida alla migrazione da DB a NDB

Datastore fornisce accesso programmatico ad alcuni dei suoi metadati per supportare la metaprogrammazione, implementare funzioni amministrative di backend, semplificare la memorizzazione nella cache coerente e scopi simili. Puoi utilizzarlo, ad esempio, per creare un visualizzatore Datastore personalizzato per la tua applicazione. I metadati disponibili includono informazioni su gruppi di entità, spazi dei nomi, tipi di entità e proprietà utilizzati dalla tua applicazione, nonché le rappresentazioni delle proprietà per ogni proprietà.

La dashboard di Datastore nella console Google Cloud fornisce anche alcuni metadati sulla tua applicazione, ma i dati visualizzati sono diversi per alcuni aspetti importanti da quelli restituiti da queste funzioni.

  • Aggiornamento. La lettura dei metadati tramite l'API restituisce i dati attuali, mentre i dati della dashboard vengono aggiornati solo una volta al giorno.
  • Contenuti. Alcuni metadati nella dashboard non sono disponibili tramite le API e viceversa.
  • Velocità. Le query e i get dei metadati vengono fatturati nello stesso modo delle query e dei get di Datastore. Query dei metadati che recuperano informazioni su spazi dei nomi, tipi e proprietà sono generalmente lente da eseguire. Come regola generale, una query sui metadati che restituisce N entità richiede circa lo stesso tempo di N query ordinarie che ciascuna restituisce una singola entità. Inoltre, le query di rappresentazione delle strutture (query sulle strutture non solo con chiavi) sono più lente rispetto alle query sulle strutture solo con chiavi. I metadati dei gruppi di entità sono leggermente più veloci rispetto all'ottenimento di un'entità normale.

Funzioni di supporto

Le seguenti funzioni recuperano le informazioni sui metadati:

  • get_entity_group_version() restituisce un numero di versione per un gruppo di entità. Questo è utile per scoprire se qualsiasi entità del gruppo è cambiata dall'ultima volta che hai ricevuto il numero di versione.
  • get_namespaces() restituisce un elenco contenente i nomi di tutti gli spazi dei nomi di un'applicazione o di quelli in un intervallo specificato.
  • get_kinds() restituisce un elenco contenente i nomi di tutti i tipi di entità di un'applicazione o di quelli in un intervallo specificato.
  • get_properties_of_kind() restituisce un elenco contenente i nomi di tutte le proprietà indicizzate di un'applicazione (o quelle in un intervallo specificato) associate a un determinato tipo di entità. Le proprietà non indicizzate non sono incluse.
  • get_representations_of_kind() restituisce un dizionario contenente le rappresentazioni di tutte le proprietà indicizzate di un'applicazione o quelle in un intervallo specificato associato a un determinato tipo di entità. Il dizionario mappa il nome di ogni proprietà a un elenco delle rappresentazioni della proprietà. Le proprietà non indicizzate non sono incluse.

Metadati del gruppo di entità

Cloud Datastore fornisce l'accesso alla "versione" di un gruppo di entità, un numero strettamente positivo che aumenta a ogni modifica del gruppo di entità.

L'esempio seguente mostra come ottenere la versione di un gruppo di entità:

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)

Comportamento precedente

Nel comportamento precedente della versione del gruppo di entità, la versione del gruppo di entità aumenta solo in caso di modifiche al gruppo di entità. Il comportamento precedente dei metadati dei gruppi di entità potrebbe essere utilizzato, ad esempio, per mantenere una cache coerente di una query da predecessore complessa in un gruppo di entità.

Questo esempio memorizza nella cache i risultati della query (un conteggio dei risultati corrispondenti) e utilizza il comportamento precedente delle versioni del gruppo di entità per utilizzare il valore memorizzato nella cache se è aggiornato:

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() potrebbe restituire None per un gruppo di entità in cui non è stata mai eseguita la scrittura.

Le versioni dei gruppi di entità vengono ottenute chiamando get() su una pseudo-entità speciale che contiene una proprietà __version__. Per informazioni dettagliate, consulta la documentazione di riferimento su EntityGroup.

Query dei metadati

Se le funzioni di supporto descritte nella sezione precedente non soddisfano le tue esigenze, puoi inviare richieste di metadati più elaborate o flessibili con una query esplicita sui metadati. In Python, le classi del modello per queste query sono definite nel pacchetto google.appengine.ext.db.metadata. Questi modelli forniscono tipi di entità speciali riservati alle query sui metadati:

Classe modello Tipo di entità
Namespace __namespace__
Kind __kind__
Property __property__

Questi modelli e tipi non entreranno in conflitto con altri con gli stessi nomi che potrebbero già esistere nella tua applicazione. Eseguendo query su questi tipi speciali, puoi recuperare le entità contenenti i metadati desiderati.

Le entità restituite dalle query sui metadati vengono generate dinamicamente in base allo stato corrente di Datastore. Sebbene sia possibile creare istanze locali delle classi di modelli Namespace, Kind o Property, qualsiasi tentativo di archiviarle in Datastore non andrà a buon fine con un'eccezione BadRequestError.

Puoi eseguire query sui metadati utilizzando un oggetto query appartenente a una delle due classi:

  • Un oggetto Query restituito dal metodo della classe Namespace.all(), Kind.all() o Property.all() (ereditato dal metodo della superclasse Model.all())
  • Un oggetto GqlQuery per le query in stile GQL

L'esempio seguente restituisce i nomi di tutti i tipi di entità in un'applicazione:

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

Query sullo spazio dei nomi

Se la tua applicazione utilizza l'API Namespaces, puoi utilizzare una query sullo spazio dei nomi per trovare tutti gli spazi dei nomi utilizzati nelle entità dell'applicazione. In questo modo puoi eseguire attività come le funzioni amministrative su più spazi dei nomi.

Le query sullo spazio dei nomi restituiscono entità del tipo speciale __namespace__ il cui nome della chiave è il nome di uno spazio dei nomi. Un'eccezione è lo spazio dei nomi predefinito designato dalla stringa vuota "": poiché la stringa vuota non è un nome di chiave valido, questo spazio dei nomi è associato all'ID numerico 1. Le query di questo tipo supportano il filtro solo per gli intervalli della pseudoproprietà __key__ speciale, il cui valore è la chiave dell'entità. I risultati possono essere ordinati in base al valore __key__ crescente (ma non decrescente). Poiché le entità __namespace__ non hanno proprietà, sia le query solo chiavi sia quelle non solo chiavi restituiscono le stesse informazioni.

Le entità dello spazio dei nomi sono istanze della classe del modello google.appengine.ext.db.metadata.Namespace. La proprietà stringa namespace_name, calcolata dalla chiave dell'entità, restituisce il nome dello spazio dei nomi corrispondente. Se la chiave ha l'ID numerico 1, la proprietà restituisce la stringa vuota. Per facilitare le query, il modello Namespace fornisce i seguenti metodi di classe:

Ad esempio, di seguito è riportata l'implementazione della funzione di supporto get_namespaces(), che restituisce un elenco contenente i nomi di tutti gli spazi dei nomi di un'applicazione (o quelli nell' intervallo compreso tra due nomi specificati, start e end):

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]

Query sui tipi

Le query sui tipi restituiscono entità di tipo __kind__ il cui nome della chiave è il nome di un tipo di entità. Le query di questo tipo sono limitate implicitamente allo spazio dei nomi corrente e supportano l'applicazione di filtri solo per gli intervalli della pseudoproprietà __key__. I risultati possono essere ordinati in base al valore __key__ crescente (ma non decrescente). Poiché le entità __kind__ non hanno proprietà, sia le query basate solo su chiavi sia quelle non basate solo su chiavi restituiscono le stesse informazioni.

Le entità di tipo sono istanze della classe del modello google.appengine.ext.db.metadata.Kind. La proprietà stringa kind_name, calcolata dalla chiave dell'entità, restituisce il nome del tipo di entità corrispondente. Per facilitare le query, il modello Kind fornisce i seguenti metodi di classe:

Ad esempio, di seguito è riportata l'implementazione della funzione di supporto get_kinds(), che restituisce un elenco contenente i nomi di tutti i tipi di entità di un'applicazione (o quelli nell' intervallo compreso tra due nomi specificati, start e end):

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]

Il seguente esempio stampa tutti i tipi i cui nomi iniziano con una lettera minuscola:

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

Query sulle proprietà

Le query sulle proprietà restituiscono entità di tipo __property__ che indicano le proprietà associate a un tipo di entità (indipendentemente dal fatto che queste proprietà siano attualmente definite nel modello del tipo). L'entità che rappresenta la proprietà P di tipo K viene creata come segue:

  • La chiave dell'entità ha il tipo __property__ e il nome della chiave P.
  • La chiave dell'entità padre ha il tipo __kind__ e il nome della chiave K.

Le entità di proprietà sono istanze della classe del modello google.appengine.ext.db.metadata.Property. Le proprietà di stringa kind_name e property_name, calcolate dalla chiave dell'entità, restituiscono i nomi del tipo e della proprietà corrispondente. Il modello Property fornisce quattro metodi di classe per semplificare la creazione e l'esame delle chiavi __property__:

L'esempio seguente illustra questi metodi:

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"

Il comportamento di una query sulle proprietà dipende dal fatto che si tratti di una query solo chiavi o non solo chiavi (rappresentazione della proprietà), come descritto nelle sezioni seguenti.

Query sulle proprietà: solo chiavi

Le query sulle proprietà solo chiavi restituiscono una chiave per ogni proprietà indicizzata di un tipo di entità specificato. Le proprietà non indicizzate non sono incluse. L'esempio seguente stampa i nomi di tutti i tipi di entità di un'applicazione e le proprietà associate a ciascuna:

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))

Le query di questo tipo sono limitate implicitamente allo spazio dei nomi corrente e supportano il filtro solo per gli intervalli della pseudoproprietà __key__, dove le chiavi indicano entità __kind__ o __property__. I risultati possono essere ordinati in base al valore __key__ crescente (ma non decrescente). Il filtro viene applicato alle coppie tipo-proprietà, ordinate prima per tipo e poi per proprietà: ad esempio, supponiamo di avere un'entità con queste proprietà:

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

La query per restituire i dati della proprietà sarà simile alla seguente:

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))

La query riportata sopra restituirà quanto segue:

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

Tieni presente che i risultati non includono la proprietà name di tipo Employee e la proprietà title di tipo Manager, né le proprietà di tipi Account e Product, perché non rientrano nell'intervallo specificato per la query.

Le query sulle proprietà supportano anche i filtri degli antenati su una chiave __kind__ o __property__ per limitare i risultati della query a un singolo tipo o proprietà. Puoi utilizzarlo, ad esempio, per recuperare le proprietà associate a un determinato tipo di entità, come nell'esempio seguente:

(un'implementazione della funzione helper 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]

Query sulle proprietà: non solo chiavi (rappresentazione della proprietà)

Le query sulle proprietà non solo con chiavi, chiamate query di rappresentazione della proprietà, restituisce informazioni aggiuntive sulle rappresentazioni utilizzate da ogni coppia di tipo-proprietà. Le proprietà non indicizzate non sono incluse. L'entità restituita per la proprietà P di tipo K ha la stessa chiave di una corrispondente query solo chiavi, oltre a una proprietà property_representation aggiuntiva che restituisce le rappresentazioni della proprietà. Il valore di questa proprietà è un'istanza della classe StringListProperty contenente una stringa per ogni rappresentazione della proprietà P trovata in qualsiasi entità di tipo K.

Tieni presente che le rappresentazioni non sono uguali ai classi di proprietà; più classi di proprietà possono essere associate alla stessa rappresentazione. Ad esempio, StringProperty e PhoneNumberProperty utilizzano entrambi la rappresentazione STRING.

La seguente tabella mappa le classi di proprietà alle relative rappresentazioni:

Classe di proprietà Rappresentazione
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 Rappresentazione dell'elemento dell'elenco
StringListProperty Rappresentazione dell'elemento dell'elenco

Ad esempio, di seguito è riportata l'implementazione della funzione di supporto get_representations_of_kind(), che restituisce un dizionario contenente le rappresentazioni di tutte le proprietà indicizzate di un'applicazione (o quelle nell'intervallo compreso tra due nomi specificati, start e end) associate a un determinato tipo di entità. Il dizionario mappa il nome di ogni proprietà a un elenco delle sue rappresentazioni:

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