Nota:gli sviluppatori che creano nuove applicazioni sono fortemente incoraggiati a utilizzare la libreria client NDB, che offre diversi vantaggi rispetto a questa libreria client, ad esempio la memorizzazione automatica nella cache delle entità tramite l'API Memcache. Se al momento utilizzi la libreria client DB precedente, leggi la guida alla migrazione da DB a NDB.
Gli oggetti di dati in Datastore sono noti come entità. Un'entità ha una o più proprietà denominate, ognuna delle quali può avere uno o più valori. Le entità dello stesso tipo non devono avere le stesse proprietà e i valori di un'entità per una determinata proprietà non devono essere tutti dello stesso tipo di dati. Se necessario, un'applicazione può stabilire e applicare queste limitazioni nel propriomodello dei datii.
Datastore supporta diversi tipi di dati per i valori delle proprietà. Tra questi, figurano:
- Numeri interi
- Numeri in virgola mobile
- Stringhe
- Date
- Dati binari
Per un elenco completo dei tipi, consulta Proprietà e tipi di valori.
Ogni entità in Datastore ha una chiave che la identifica in modo univoco. La chiave è costituita dai seguenti componenti:
- Lo spazio dei nomi dell'entità, che consente il multitenancy
- Il tipo dell'entità, che la classifica ai fini delle query Datastore
- Un identificatore per la singola entità, che può essere
- una stringa key name
- un ID numerico intero
- Un percorso degli antenati facoltativo che individua l'entità all'interno della gerarchia Datastore
Un'applicazione può recuperare una singola entità da Datastore utilizzando la chiave dell'entità oppure può recuperare una o più entità eseguendo una query in base alle chiavi o ai valori delle proprietà delle entità.
L'SDK Python App Engine include una libreria di modellazione dei dati per rappresentare le entità Datastore come istanze di classi Python e per archiviare e recuperare queste istanze in Datastore.
Datastore non impone alcuna restrizione alla struttura delle entità, ad esempio se una determinata proprietà ha un valore di un tipo specifico. Questo compito è lasciato all'applicazione e alla libreria di modellazione dei dati.
Tipi e identificatori
Ogni entità Datastore è di un particolare tipo,che la classifica ai fini delle query: ad esempio, un'applicazione per le risorse umane potrebbe rappresentare ogni dipendente di un'azienda con un'entità di tipo Employee. Nell'API Python Datastore, il tipo di entità è determinato dalla classe modello, che definisci nella tua applicazione come sottoclasse della classe della libreria di modellazione dei dati db.Model. Il nome della classe del modello diventa il tipo delle entità che vi appartengono. Tutti i nomi dei tipi che iniziano con due trattini bassi (__) sono riservati e non possono essere utilizzati.
L'esempio seguente crea un'entità di tipo Employee, compila i valori delle proprietà e la salva 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()
La classe Employee dichiara quattro proprietà per il modello dei dati: first_name, last_name, hire_date e attended_hr_training. La superclasse Model garantisce che gli attributi degli oggetti Employee siano conformi a questo modello: ad esempio, un tentativo di assegnare un valore stringa all'attributo hire_date genererebbe un errore di runtime, poiché il modello dei dati per hire_date è stato dichiarato come db.DateProperty.
Oltre a un tipo, ogni entità ha un identificatore, assegnato al momento della creazione dell'entità. Poiché fa parte della chiave dell'entità, l'identificatore è associato in modo permanente all'entità e non può essere modificato. Può essere assegnato in due modi:
- La tua applicazione può specificare una propria stringa key name per l'entità.
- Puoi fare in modo che Datastore assegni automaticamente all'entità un ID numerico intero.
Per assegnare un nome di chiave a un'entità, fornisci l'argomento denominato key_name al costruttore della classe del modello quando crei l'entità:
# Create an entity with the key Employee:'asalieri'.
employee = Employee(key_name='asalieri')
Per fare in modo che Datastore assegni automaticamente un ID numerico, ometti l'argomento key_name:
# Create an entity with a key such as Employee:8261.
employee = Employee()
Assegnazione degli identificatori
Datastore può essere configurato per generare ID automatici utilizzando due diverse norme per gli ID automatici:
- Il criterio
defaultgenera una sequenza casuale di ID non utilizzati distribuiti in modo approssimativamente uniforme. Ogni ID può contenere fino a 16 cifre decimali. - Il criterio
legacycrea una sequenza di ID interi più piccoli non consecutivi.
Se vuoi mostrare gli ID entità all'utente e/o dipendere dal loro ordine, la cosa migliore da fare è utilizzare l'allocazione manuale.
Datastore genera una sequenza casuale di ID non utilizzati distribuiti in modo approssimativamente uniforme. Ogni ID può contenere fino a 16 cifre decimali.
Percorsi degli antenati
Le entità in Cloud Datastore formano uno spazio strutturato gerarchicamente simile alla struttura di directory di un file system. Quando crei un'entità, puoi facoltativamente designarne un'altra come genitore; la nuova entità è un elemento secondario dell'entità padre (tieni presente che, a differenza di un file system, l'entità genitore non deve necessariamente esistere). Un'entità senza un elemento principale è un'entità radice. L'associazione tra un'entità e la relativa entità principale è permanente e non può essere modificata dopo la creazione dell'entità. Cloud Datastore non assegnerà mai lo stesso ID numerico a due entità con lo stesso elemento principale o a due entità radice (quelle senza un elemento principale).
Il genitore di un'entità, il genitore del genitore e così via in modo ricorsivo sono i suoi antenati; i suoi figli, i figli dei figli e così via sono i suoi discendenti. Un'entità base e tutti i relativi discendenti appartengono allo stesso gruppo di entità. La sequenza di entità che inizia con un'entità radice e procede da genitore a figlio, fino a un'entità specifica, costituisce il percorso degli antenati dell'entità. La chiave completa che identifica l'entità è costituita da una sequenza di coppie tipo-identificatore che specificano il percorso degli antenati e terminano con quelli dell'entità stessa:
[Person:GreatGrandpa, Person:Grandpa, Person:Dad, Person:Me]
Per unentità base, il percorso degli antenati è vuoto e la chiave è costituita unicamente dal tipo e dall'identificatore dell'entità:
[Person:GreatGrandpa]
Questo concetto è illustrato nel seguente diagramma:
Per designare l'elemento principale di un'entità, utilizza l'argomento parent del costruttore della classe del modello quando crei l'entità secondaria. Il valore di questo argomento può essere l'entità padre stessa o la relativa chiave. Puoi ottenere la chiave chiamando il metodo key() dell'entità padre. L'esempio seguente crea un'entità di tipo Address e mostra due modi per designare un'entità Employee come entità padre:
# 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()
Transazioni e gruppi di entità
Ogni tentativo di creare, aggiornare o eliminare un'entità avviene nel contesto di una transazione. Una singola transazione può includere un numero qualsiasi di queste operazioni. Per mantenere la coerenza dei dati, la transazione garantisce che tutte le operazioni che contiene vengano applicate a Datastore come unità o, se una delle operazioni non va a buon fine, che nessuna di queste venga applicata. Inoltre, tutte le letture a elevata coerenza (query o recuperi degli antenati) eseguite all'interno della stessa transazione osservano uno snapshot coerente dei dati.
Come accennato in precedenza, un gruppo di entità è un insieme di entità collegate tramite la discendenza a un elemento radice comune. L'organizzazione dei dati in gruppi di entità può limitare le transazioni che possono essere eseguite:
- Tutti i dati a cui accede una transazione devono essere contenuti in un massimo di 25 gruppi di entità.
- Se vuoi utilizzare le query all'interno di una transazione, i tuoi dati devono essere organizzati in gruppi di entità in modo da poter specificare i filtri predecessori che corrispondano ai dati corretti.
- Esiste un limite di velocità effettiva di scrittura di circa una transazione al secondo all'interno di un singolo gruppo di entità. Questa limitazione esiste perché Datastore esegue la replica sincrona senza master di ogni gruppo di entità in un'ampia area geografica per fornire elevata affidabilità e tolleranza agli errori.
In molte applicazioni, è accettabile utilizzare la coerenza finale (ad es. una query non discendente che interessa più gruppi di entità, che a volte può restituire dati leggermente obsoleti) quando si ottiene una visione generale di dati non correlati e quindi utilizzare la elevata coerenza (una query da predecessore o una get di una singola entità) quando si visualizza o si modifica un singolo insieme di dati altamente correlati. In queste applicazioni, in genere è un buon approccio utilizzare un gruppo di entità separato per ogni insieme di dati strettamente correlati.
Per saperne di più, consulta la sezione Strutturazione per una coerenza elevata.
Proprietà e tipi di valori
I valori dei dati associati a un'entità sono costituiti da una o più proprietà. Ogni proprietà ha un nome e uno o più valori. Una proprietà può avere valori di più di un tipo e due entità possono avere valori di tipi diversi per la stessa proprietà. Le proprietà possono essere indicizzate o non indicizzate (le query che ordinano o filtrano in base a una proprietà P ignorano le entità in cui P non è indicizzata). Un'entità può avere al massimo 20.000 proprietà indicizzate.
Sono supportati i seguenti tipi di valori:
| Tipo di valore | Tipo/i Python | Ordinamento | Note |
|---|---|---|---|
| Numero intero | intlong |
Numerico | Numero intero a 64 bit, con segno |
| Numero in virgola mobile | float |
Numerico | Doppia precisione a 64 bit, IEEE 754 |
| Booleano | bool |
False<True |
|
| Stringa di testo (breve) | strunicode |
Unicode ( str trattato come ASCII) |
Fino a 1500 byte |
| Stringa di testo (lunga) | db.Text |
Nessuno | Fino a 1 megabyte Non indicizzato |
| Stringa di byte (breve) | db.ByteString |
Ordine dei byte | Fino a 1500 byte |
| Stringa di byte (lunga) | db.Blob |
Nessuno | Fino a 1 megabyte Non indicizzato |
| Data e ora | datetime.datedatetime.timedatetime.datetime |
Cronologica | |
| Punto geografico | db.GeoPt |
Per latitudine, poi per longitudine |
|
| Indirizzo postale | db.PostalAddress |
Unicode | |
| Numero di telefono | db.PhoneNumber |
Unicode | |
| Indirizzo email | db.Email |
Unicode | |
| Utente dell'Account Google | users.User |
Indirizzo email in ordine Unicode |
|
| Handle di messaggistica istantanea | db.IM |
Unicode | |
| Link | db.Link |
Unicode | |
| Categoria | db.Category |
Unicode | |
| Valutazione | db.Rating |
Numerico | |
| Chiave Datastore | db.Key |
Per elementi del percorso (tipo, identificatore, tipo, identificatore...) |
|
| Chiave Blobstore | blobstore.BlobKey |
Ordine dei byte | |
| Null | NoneType |
Nessuno |
Importante:ti consigliamo vivamente di non
memorizzare un UserProperty, poiché include l'indirizzo email
e l'ID univoco dell'utente. Se un utente cambia il proprio indirizzo email e confronti il vecchio valore User memorizzato con il nuovo valore User, non corrisponderanno.
Per le stringhe di testo e i dati binari non codificati (stringhe di byte), Datastore supporta due tipi di valori:
- Le stringhe brevi (fino a 1500 byte) vengono indicizzate e possono essere utilizzate nelle condizioni di filtro delle query e negli ordinamenti.
- Le stringhe lunghe (fino a 1 megabyte) non vengono indicizzate e non possono essere utilizzate nei filtri delle query e negli ordinamenti.
Blob nell'API Datastore. Questo tipo non è correlato ai BLOB utilizzati nell'API Blobstore.
Quando una query coinvolge una proprietà con valori di tipi misti, Datastore utilizza un ordinamento deterministico basato sulle rappresentazioni interne:
- Valori null
- Numeri in virgola fissa
- Numeri interi
- Date e ore
- Valutazioni
- Valori booleani
- Sequenze di byte
- Stringa di byte
- Stringa Unicode
- Chiavi Blobstore
- Numeri in virgola mobile
- Punti geografici
- Utenti con account Google
- Chiavi Datastore
Poiché le stringhe di testo lunghe e le stringhe di byte lunghe non vengono indicizzate, non hanno un ordinamento definito.
Utilizzo delle entità
Le applicazioni possono utilizzare l'API Datastore per creare, recuperare, aggiornare ed eliminare le entità. Se l'applicazione conosce la chiave completa di un'entità (o può derivarla dalla chiave, dal tipo e dall'identificatore padre), può utilizzarla per operare direttamente sull'entità. Un'applicazione può anche ottenere la chiave di un'entità come risultato di una query Datastore. Per ulteriori informazioni, consulta la pagina Query Datastore.
Creare un'entità
In Python, crei una nuova entità costruendo un'istanza di una classe modello, compilando le relative proprietà, se necessario, e chiamando il relativo metodo put() per salvarla in Datastore. Puoi specificare il nome della chiave dell'entità passando un argomento key_name al costruttore:
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()
Se non fornisci un nome della chiave, Datastore genererà automaticamente un ID numerico per la chiave dell'entità:
employee = Employee(first_name='Antonio',
last_name='Salieri')
employee.hire_date = datetime.datetime.now().date()
employee.attended_hr_training = True
employee.put()
Recupero di un'entità
Per recuperare un'entità identificata da una determinata chiave, passa l'oggetto Key come argomento alla funzione db.get(). Puoi generare l'oggetto Key utilizzando il metodo della classe Key.from_path().
Il percorso completo è una sequenza di entità nel percorso degli antenati, con ogni entità rappresentata dal suo tipo (una stringa) seguito dal suo identificatore (nome della chiave o ID numerico):
address_k = db.Key.from_path('Employee', 'asalieri', 'Address', 1)
address = db.get(address_k)
db.get() restituisce un'istanza della classe del modello appropriata. Assicurati di aver importato la classe del modello per l'entità recuperata.
Aggiornamento di un'entità
Per aggiornare un'entità esistente, modifica gli attributi dell'oggetto, quindi chiama il metodo put(). I dati dell'oggetto sovrascrivono l'entità esistente. L'intero oggetto viene inviato a Datastore a ogni chiamata a put().
Per eliminare una proprietà, elimina l'attributo dall'oggetto Python:
del address.postal_code
quindi salva l'oggetto.
Eliminazione di un'entità
Data la chiave di un'entità, puoi eliminarla con la funzione db.delete().
address_k = db.Key.from_path('Employee', 'asalieri', 'Address', 1)
db.delete(address_k)
o chiamando il metodo delete() dell'entità:
employee_k = db.Key.from_path('Employee', 'asalieri')
employee = db.get(employee_k)
# ...
employee.delete()
Operazioni batch
Le funzioni db.put(), db.get() e db.delete() (e le loro controparti asincrone db.put_async(), db.get_async() e db.delete_async()) possono accettare un argomento elenco per agire su più entità in una singola chiamata Datastore:
# 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])
Le operazioni collettive non modificano i costi. Ti verrà addebitato il costo di ogni chiave in un'operazione batch, indipendentemente dall'esistenza di ciascuna chiave. Le dimensioni delle entità coinvolte in un'operazione non influiscono sul costo.
Eliminazione di entità in blocco
Se devi eliminare un numero elevato di entità, ti consigliamo di utilizzare Dataflow per eliminare le entità in blocco.
Utilizzo di un elenco vuoto
Per l'interfaccia NDB, Datastore ha sempre scritto un elenco vuoto come proprietà omessa sia per le proprietà statiche che per quelle dinamiche. Per mantenere la compatibilità con le versioni precedenti, questo comportamento continua a essere quello predefinito. Per eseguire l'override di questa impostazione a livello globale o in base a ListProperty, imposta l'argomento write_empty_list sutrue nella classe Property. L'elenco vuoto viene quindi scritto in Datastore e può essere letto come elenco vuoto.
Per l'interfaccia DB, le scritture di elenchi vuoti non erano storicamente consentite se la proprietà era dinamica: se tentavi questa operazione, ricevevi un errore. Ciò significa che non è necessario conservare alcun comportamento predefinito per la compatibilità con le versioni precedenti delle proprietà dinamiche del database, pertanto puoi semplicemente scrivere e leggere l'elenco vuoto nel modello dinamico senza apportare modifiche.
Tuttavia, per le proprietà statiche del database, l'elenco vuoto è stato scritto come proprietà omessa e questo comportamento continua per impostazione predefinita per la compatibilità con le versioni precedenti.
Se vuoi attivare gli elenchi vuoti per le proprietà statiche del database, utilizza l'argomento
write_empty_list
per true nella classe Property; l'elenco vuoto viene quindi scritto in
Datastore.