App Engine predefinisce un indice semplice per ogni proprietà di un'entità.
Un'applicazione App Engine può definire ulteriori indici personalizzati in un
file di configurazione
dell'indice denominato datastore-indexes.xml
,
che viene generato nella directory /war/WEB-INF/appengine-generated
dell'applicazione
. Il server di sviluppo aggiunge automaticamente
suggerimenti a questo file quando rileva query che non possono essere eseguite con gli indici esistenti.
Puoi ottimizzare gli indici manualmente modificando il file prima di caricare l'applicazione.
Nota:Il meccanismo di query basato su indice supporta un'ampia gamma di query ed è adatto alla maggior parte delle applicazioni. Tuttavia, non supporta alcuni tipi di query comuni in altre tecnologie di database: in particolare, i join e le query di aggregazione non sono supportati nel motore di query Datastore. Consulta la pagina Query Datastore per i limiti relativi alle query Datastore.
Definizione e struttura dell'indice
Un indice è definito in un elenco di proprietà di un determinato tipo di entità, con un ordine corrispondente (crescente o decrescente) per ogni proprietà. Per l'utilizzo con le query di antenati, l'indice può anche includere facoltativamente gli antenati di un'entità.
Una tabella di indice contiene una colonna per ogni proprietà denominata nella definizione dell'indice. Ogni riga della tabella rappresenta un'entità in Datastore che è un potenziale risultato per le query basate sull' indice. Un'entità viene inclusa nell'indice solo se ha un valore indicizzato impostato per ogni proprietà utilizzata nell'indice; se la definizione dell'indice fa riferimento a una proprietà per la quale l'entità non ha un valore, l'entità non verrà visualizzata nell'indice e quindi non verrà mai restituita come risultato per qualsiasi query basata sull'indice.
Nota: Datastore distingue
tra un'entità che non possiede una proprietà e una che possiede la
proprietà con un valore nullo (null
). Se assegni esplicitamente un
valore nullo alla proprietà di un'entità, questa potrebbe essere inclusa nei risultati
di una query che fa riferimento a quella proprietà.
Nota: gli indici composti da più proprietà richiedono che ogni singola proprietà non sia impostata su Non indicizzato.
Le righe di una tabella indice vengono ordinate prima per antenato e poi per valori delle proprietà, nell'ordine specificato nella definizione dell'indice. L'indice perfetto per una query, che consente di eseguirla nel modo più efficiente, è definito in base alle seguenti proprietà, in ordine:
- Proprietà utilizzate nei filtri di uguaglianza
- Proprietà utilizzata in un filtro di disuguaglianza (di cui può essercene non più di uno)
- Proprietà utilizzate negli ordinamenti
In questo modo, tutti i risultati per ogni possibile esecuzione della query vengono visualizzati in righe consecutive della tabella. Datastore esegue una query utilizzando un indice perfetto nel seguente modo:
- Identifica l'indice corrispondente al tipo, alle proprietà di filtro, agli operatori di filtro e agli ordini di ordinamento della query.
- Esegue la scansione dall'inizio dell'indice alla prima entità che soddisfa tutte le condizioni di filtro della query.
- Continua a scansionare l'indice, restituendo ogni entità a turno, finché non
- trova un'entità che non soddisfa le condizioni del filtro oppure
- raggiunge la fine dell'indice oppure
- ha raccolto il numero massimo di risultati richiesti dalla query.
Ad esempio, considera la seguente query:
L'indice perfetto per questa query è una tabella di chiavi per le entità di tipo
Person
, con colonne per i valori delle proprietà
lastName
e
height
. L'indice
viene ordinato prima in ordine crescente per
lastName
e poi in ordine decrescente per
height
.
Per generare questi indici, configura gli indici nel seguente modo:
<?xml version="1.0" encoding="utf-8"?>
<datastore-indexes autoGenerate="false">
<datastore-index kind="Person" ancestor="false" source="manual">
<property name="lastName" direction="asc"/>
<property name="height" direction="desc"/>
</datastore-index>
</datastore-indexes>
Due query dello stesso modulo, ma con valori di filtro diversi, utilizzano lo stesso indice. Ad esempio, la seguente query utilizza lo stesso indice di quella precedente:
Anche le due query seguenti utilizzano lo stesso indice, nonostante le loro forme diverse:
e
Configurazione degli indici
Per impostazione predefinita, Datastore predefinisce automaticamente un indice per ogni
proprietà di ogni tipo di entità. Questi indici predefiniti sono sufficienti per eseguire
molte query semplici, come query di sola uguaglianza e query di disuguaglianza
semplici. Per tutte le altre query, l'applicazione deve definire gli indici necessari
in un file di configurazione
dell'indice
denominato datastore-indexes.xml
. Se l'applicazione tenta di eseguire una query
che non può essere eseguita con gli indici disponibili (predefiniti o
specificati nel file di configurazione dell'indice), la query non andrà a buon fine
e verrà visualizzato un DatastoreNeedIndexException
.
Datastore crea indici automatici per le query dei seguenti moduli:
- Query senza tipo che utilizzano solo filtri per predecessore e chiave
- Query che utilizzano solo filtri di uguaglianza e antenati
- Query che utilizzano solo filtri di disuguaglianza (che sono limitati a una singola proprietà)
- Query che utilizzano solo filtri di antenati, filtri di uguaglianza sulle proprietà e filtri di disuguaglianza sulle chiavi
- Query senza filtri e con un solo ordinamento in base a una proprietà, in ordine crescente o decrescente
Altre forme di query richiedono che gli indici vengano specificati nel file di configurazione dell'indice, tra cui:
- Query con filtri di antenati e disuguaglianza
- Query con uno o più filtri di disuguaglianza su una proprietà e uno o più filtri di uguaglianza su altre proprietà
- Query con un ordine di ordinamento delle chiavi in ordine decrescente
- Query con più criteri di ordinamento
Indici e proprietà
Ecco alcune considerazioni speciali da tenere presenti in merito agli indici e al loro rapporto con le proprietà delle entità in Datastore:
Proprietà con tipi di valori misti
Quando due entità hanno proprietà con lo stesso nome ma tipi di valori diversi, un indice della proprietà ordina le entità prima in base al tipo di valore e poi in base a un ordinamento secondario appropriato per ogni tipo. Ad esempio, se due entità hanno ciascuna una proprietà
denominata age
, una con un valore intero e una con un valore stringa, l'entità
con il valore intero precede sempre quella con il valore stringa quando viene ordinata
in base alla proprietà age
, indipendentemente dai valori della proprietà stessi.
Ciò è particolarmente importante nel caso di numeri interi e in virgola mobile, che vengono trattati come tipi separati da Datastore.
Poiché tutti i numeri interi vengono ordinati prima di tutti i numeri in virgola mobile, una proprietà con il valore
intero 38
viene ordinata prima di una con il valore in virgola mobile 37.5
.
Proprietà non indicizzate
Se sai che non dovrai mai filtrare o ordinare in base a una determinata proprietà, puoi indicare a Datastore di non gestire le voci di indice per quella proprietà dichiarandola non indicizzata. In questo modo si riduce il costo di esecuzione dell'applicazione diminuendo il numero di scritture di Datastore che deve eseguire. Un'entità con una proprietà non indicizzata si comporta come se la proprietà non fosse impostata: le query con un filtro o un ordinamento in base alla proprietà non indicizzata non corrisponderanno mai a quell'entità.
Nota:
se una proprietà viene visualizzata in un indice composto da più proprietà e impostandola su Non indicizzato, non verrà indicizzata nell'indice composto.
Ad esempio, supponiamo che un'entità abbia le proprietà a e b e
che tu voglia creare un indice in grado di soddisfare query come
WHERE a ="bike" and b="red"
. Supponiamo inoltre che le query WHERE a="bike"
e WHERE b="red"
non ti interessino.
Se imposti a su Non indicizzato e crei un indice per a e b, il datastore non creerà voci di indice per gli indici a e b e, di conseguenza, la query WHERE a="bike" and b="red"
non funzionerà. Affinché Datastore crei voci per gli indici a e b, sia a che b devono essere indicizzati.
Nell'API Java Datastore di basso livello, le proprietà sono definite come indicizzate o
non indicizzate in base all'entità, a seconda del metodo utilizzato per impostarle
(setProperty()
o
setUnindexedProperty()
):
Puoi modificare una proprietà indicizzata in non indicizzata reimpostandone il valore con
setUnindexedProperty()
o da non indicizzata a indicizzata reimpostandola con
setProperty()
.
Tieni presente, tuttavia, che la modifica di una proprietà da non indicizzata a indicizzata non influisce sulle entità esistenti che potrebbero essere state create prima della modifica. Le query che filtrano la proprietà non restituiranno queste entità esistenti, perché le entità non sono state scritte nell'indice della query al momento della creazione. Per rendere le entità accessibili alle query future, devi riscriverle in Datastore in modo che vengano inserite negli indici appropriati. ovvero devi eseguire le seguenti operazioni per ogni entità esistente:
- Recupera (ottieni) l'entità da Datastore.
- Scrivi (inserisci) di nuovo l'entità in Datastore.
Allo stesso modo, la modifica di una proprietà da indicizzata a non indicizzata influisce solo sulle entità scritte successivamente in Datastore. Le voci di indice per le entità esistenti con quella proprietà continueranno a esistere finché le entità non vengono aggiornate o eliminate. Per evitare risultati indesiderati, devi eliminare dal codice tutte le query che filtrano o ordinano in base alla proprietà (ora non indicizzata).
Limiti degli indici
Datastore impone limiti al numero e alle dimensioni complessive delle voci di indice che possono essere associate a una singola entità. Questi limiti sono elevati e la maggior parte delle applicazioni non è interessata. Tuttavia, ci sono circostanze in cui potresti riscontrare i limiti.
Come descritto sopra,
Datastore crea una voce in un indice predefinito per ogni
proprietà di ogni entità, tranne per stringhe di testo lunghe (Text
), stringhe di byte lunghe
(Blob
) ed entità incorporate
(EmbeddedEntity
)
e quelle che hai dichiarato esplicitamente come non indicizzate. La proprietà
può anche essere inclusa in altri indici personalizzati dichiarati nel
file di configurazione di datastore-indexes.xml
. Se un'entità non ha proprietà elenco, avrà al massimo una voce in ciascun indice personalizzato (per gli indici non predecessori) o una per ciascuno dei predecessori dell'entità (per gli indici dei predecessori). Ognuna di queste voci di indice va aggiornata
ogni volta che cambia il valore della proprietà.
Per una proprietà che ha un solo valore per ogni entità, ogni possibile valore deve essere memorizzato una sola volta per entità nell'indice predefinito della proprietà. Tuttavia, è possibile che un'entità con un numero elevato di proprietà a valore singolo superi il limite di dimensioni o di voci dell'indice. Allo stesso modo, un'entità che può avere più valori per la stessa proprietà richiede una voce di indice separata per ogni valore; anche in questo caso, se il numero di valori possibili è elevato, un'entità di questo tipo può superare il limite di voci.
La situazione peggiora nel caso di entità con più proprietà, ognuna delle quali può assumere più valori. Per ospitare un'entità di questo tipo, l'indice deve includere una voce per ogni combinazione possibile di valori delle proprietà. Gli indici personalizzati che fanno riferimento a più proprietà, ciascuna con più valori, possono "esplodere" in modo combinatorio, richiedendo un numero elevato di voci per un'entità con un numero relativamente piccolo di valori di proprietà possibili. Questi indici esplosivi possono aumentare notevolmente il costo della scrittura di un'entità in Datastore, a causa del gran numero di voci di indice che devono essere aggiornate, e possono anche facilmente far superare all'entità il limite di dimensioni o di voci di indice.
Considera la query
che fa sì che l'SDK suggerisca il seguente indice:
Questo indice richiederà un totale di|x|
*
|y|
*
|date|
voci per ogni
entità (dove |x|
indica il numero di valori associati all'entità per
la proprietà x
). Ad esempio, il seguente codice
crea un'entità con quattro valori per la proprietà x
, tre valori per la proprietà
y
e date
impostato sulla data corrente. Ciò richiederà 12 voci di indice, una
per ogni combinazione possibile di valori delle proprietà:
(1
, "red"
, <now>
)
(1
, "green"
, <now>
)
(1
, "blue"
, <now>
)
(2
, "red"
, <now>
)
(2
, "green"
, <now>
)
(2
, "blue"
, <now>
)
(3
, "red"
, <now>
)
(3
, "green"
, <now>
)
(3
, "blue"
, <now>
)
(4
, "red"
, <now>
)
(4
, "green"
, <now>
)
(4
, "blue"
, <now>
)
Quando la stessa proprietà viene ripetuta più volte, Datastore può rilevare gli indici esplosivi e suggerire un indice alternativo. Tuttavia, in tutte le altre circostanze (come la query definita in questo esempio), Datastore genererà uindice enormevo. In questo caso, puoi aggirare l'indice enormee configurando manualmente un indice nel file di configurazione dell'indice:
In questo modo, il numero di voci necessarie si riduce a sole(|x|
*
|date|
+
|y|
*
|date|)
, ovvero 7 voci anziché 12:
(1
, <now>
)
(2
, <now>
)
(3
, <now>
)
(4
, <now>
)
("red"
, <now>
)
("green"
, <now>
)
("blue"
, <now>
)
Qualsiasi operazione put che causerebbe il superamento del limite di dimensioni o di voci dell'indice non andrà a buon fine con un
IllegalArgumentException
. Il testo dell'eccezione descrive quale limite è stato superato ("Too many indexed properties"
o "Index entries too large"
) e quale indice personalizzato ne è la causa. Se crei un nuovo indice che supererebbe i limiti per qualsiasi entità al momento della creazione, le query sull'indice non andranno a buon fine e l'indice verrà visualizzato nello stato Error
nella console Google Cloud . Per risolvere gli indici nello stato Error
:
Rimuovi l'indice nello stato
Error
dal filedatastore-indexes.xml
.Esegui questo comando dalla directory in cui si trova
datastore-indexes.xml
per rimuovere l'indice da Datastore:gcloud datastore indexes cleanup datastore-indexes.xml
Risolvi la causa dell'errore. Ad esempio:
- Riformula la definizione dell'indice e le query corrispondenti.
- Rimuovi le entità che causano l'esplosione dell'indice.
Aggiungi di nuovo l'indice al file
datastore-indexes.xml
.Esegui questo comando dalla directory in cui si trova
datastore-indexes.xml
per creare l'indice in Datastore:gcloud datastore indexes create datastore-indexes.xml
Puoi evitare l'esplosione degli indici evitando query che richiedono un indice personalizzato utilizzando una proprietà di elenco. Come descritto sopra, sono incluse le query con più criteri di ordinamento o le query con un mix di filtri di uguaglianza e disuguaglianza.