Gli indici sono un fattore importante nelle prestazioni di un database. Proprio come l'indice di un libro che associa gli argomenti di un libro ai numeri di pagina, un indice di database associa gli elementi di un database alle loro posizioni nel database. Quando invii una query a un database, il database può utilizzare un indice per cercare rapidamente le posizioni degli elementi richiesti.
Questa pagina descrive i due tipi di indici utilizzati da Cloud Firestore, indici a campo singolo e indici compositi .
Un indice dietro ogni query
Se non esiste alcun indice per una query, la maggior parte dei database esegue la scansione dei contenuti elemento per elemento, un processo lento che rallenta ancora di più man mano che il database cresce. Cloud Firestore garantisce prestazioni elevate delle query utilizzando indici per tutte le query. Di conseguenza, le prestazioni delle query dipendono dalla dimensione del set di risultati e non dal numero di elementi nel database.
Meno gestione degli indici, più sviluppo di app
Cloud Firestore include funzionalità che riducono la quantità di tempo necessario da dedicare alla gestione degli indici. Gli indici richiesti per le query più elementari vengono creati automaticamente per te. Mentre utilizzi e testi la tua app, Cloud Firestore ti aiuta a identificare e creare gli indici aggiuntivi richiesti dalla tua app.
Tipi di indice
Cloud Firestore utilizza due tipi di indici: a campo singolo e compositi . Oltre al numero di campi indicizzati, gli indici a campo singolo e compositi differiscono nel modo in cui vengono gestiti.
Indici a campo singolo
Un indice a campo singolo memorizza una mappatura ordinata di tutti i documenti in una raccolta che contengono un campo specifico. Ogni voce in un indice a campo singolo registra il valore di un documento per un campo specifico e la posizione del documento nel database. Cloud Firestore utilizza questi indici per eseguire molte query di base. Puoi gestire gli indici a campo singolo configurando le impostazioni di indicizzazione automatica e le esenzioni dell'indice del tuo database.
Indicizzazione automatica
Per impostazione predefinita, Cloud Firestore mantiene automaticamente gli indici di campo singolo per ciascun campo in un documento e ciascun sottocampo in una mappa. Cloud Firestore utilizza le seguenti impostazioni predefinite per gli indici a campo singolo:
Per ogni campo non array e non mappa, Cloud Firestore definisce due indici a campo singolo con ambito raccolta , uno in modalità ascendente e uno in modalità discendente.
Per ogni campo della mappa, Cloud Firestore crea quanto segue:
- Un indice ascendente con ambito di raccolta per ogni sottocampo non array e non mappa.
- Un indice discendente con ambito di raccolta per ogni sottocampo non array e non mappa.
- Un indice contenente una matrice con ambito raccolta per ogni sottocampo della matrice.
- Cloud Firestore indicizza in modo ricorsivo ogni sottocampo della mappa.
Per ogni campo dell'array in un documento, Cloud Firestore crea e mantiene un indice contenente un array con ambito di raccolta.
Gli indici a campo singolo con ambito gruppo di raccolta non vengono mantenuti per impostazione predefinita.
Esenzioni per l'indice a campo singolo
Puoi esentare un campo dalle impostazioni di indicizzazione automatica creando un'esenzione dell'indice per campo singolo. Un'esenzione dall'indicizzazione sovrascrive le impostazioni dell'indice automatico a livello di database. Un'esenzione può abilitare un indice a campo singolo che altrimenti disabiliterebbero le impostazioni di indicizzazione automatica o disabilitare un indice a campo singolo che altrimenti abiliterebbe l'indicizzazione automatica. Per i casi in cui le esenzioni possono essere utili, consulta le migliori pratiche di indicizzazione .
Utilizza il valore del percorso del campo *
per aggiungere esenzioni dall'indice a livello di raccolta su tutti i campi in un gruppo di raccolte. Ad esempio, per comments
del gruppo di raccolta, imposta il percorso del campo su *
in modo che corrisponda a tutti i campi nel gruppo di raccolta comments
e disabilita l'indicizzazione di tutti i campi nel gruppo di raccolta. Puoi quindi aggiungere esenzioni per indicizzare solo i campi richiesti per le tue query. La riduzione del numero di campi indicizzati riduce i costi di archiviazione e può migliorare le prestazioni di scrittura.
Se crei un'esenzione dell'indice a campo singolo per un campo della mappa, i sottocampi della mappa ereditano tali impostazioni. È tuttavia possibile definire esenzioni per l'indice di campo singolo per sottocampi specifici. Se elimini un'esenzione per un sottocampo, il sottocampo erediterà le impostazioni di esenzione del genitore, se esistono, o le impostazioni a livello di database se non esistono esenzioni del genitore.
Per creare e gestire le esenzioni per gli indici a campo singolo, consulta Gestione degli indici in Cloud Firestore .
Indici compositi
Un indice composito memorizza una mappatura ordinata di tutti i documenti in una raccolta, in base a un elenco ordinato di campi da indicizzare.
Cloud Firestore utilizza indici compositi per supportare query non già supportate da indici a campo singolo.
Cloud Firestore non crea automaticamente indici compositi come fa per gli indici a campo singolo a causa dell'elevato numero di possibili combinazioni di campi. Cloud Firestore ti aiuta invece a identificare e creare gli indici compositi richiesti durante la creazione della tua app.
Se provi a eseguire la query precedente senza prima creare l'indice richiesto, Cloud Firestore restituisce un messaggio di errore contenente un collegamento che puoi seguire per creare l'indice mancante. Ciò accade ogni volta che si tenta una query non supportata da un indice. Puoi anche definire e gestire manualmente gli indici compositi utilizzando la console o la CLI di Firebase . Per ulteriori informazioni sulla creazione e la gestione degli indici compositi, vedere Gestione degli indici .
Modalità di indice e ambiti di query
Puoi configurare gli indici a campo singolo e quelli compositi in modo diverso, ma entrambi richiedono la configurazione delle modalità di indice e degli ambiti delle query per gli indici.
Modalità indice
Quando definisci un indice, selezioni una modalità indice per ciascun campo indicizzato. La modalità indice di ogni campo supporta clausole di query specifiche su quel campo. È possibile selezionare tra le seguenti modalità indice:
Modalità indice | Descrizione |
---|---|
ascendente_verso l'alto | Supporta < , <= , == , >= , > , != , in e not-in , clausole di query sul campo e supporta l'ordinamento dei risultati in ordine crescente in base al valore di questo campo. |
Freccia discendente_verso | Supporta < , <= , == , >= , > , != , in e not-in clausole di query sul campo e supporta l'ordinamento dei risultati in ordine decrescente in base al valore di questo campo. |
Array-contiene | Supporta le clausole di query array-contains e array-contains-any sul campo. |
Ambiti di query
Ogni indice ha come ambito una raccolta o un gruppo di raccolte. Questo è noto come ambito della query dell'indice:
- Ambito della raccolta
- Cloud Firestore crea indici con ambito di raccolta per impostazione predefinita. Questi indici supportano query che restituiscono risultati da una singola raccolta.
- Ambito del gruppo di raccolta
- Un gruppo di raccolte include tutte le raccolte con lo stesso ID raccolta. Per eseguire una query sul gruppo di raccolta che restituisce risultati filtrati o ordinati da un gruppo di raccolta, è necessario creare un indice corrispondente con ambito gruppo di raccolta.
Ordinamento predefinito e campo __name__
Oltre a ordinare i documenti in base alle modalità di indice specificate per ciascun campo (ascendente o discendente), gli indici applicano un ordinamento finale in base al campo __name__
di ciascun documento. Il valore del campo __name__
è impostato sul percorso completo del documento. Ciò significa che i documenti nel set di risultati con gli stessi valori di campo vengono ordinati in base al percorso del documento.
Per impostazione predefinita, il campo __name__
viene ordinato nella stessa direzione dell'ultimo campo ordinato nella definizione dell'indice. Per esempio:
Collezione | Campi indicizzati | Ambito della query |
---|---|---|
città | __name__ | nome, Collezione |
città | stato __name__ | , Collezione |
città | paese __name__ | , popolazione , Collezione |
Per ordinare i risultati in base alla direzione __name__
non predefinita, è necessario creare quell'indice.
Esempio di indicizzazione
Creando automaticamente indici a campo singolo per te, Cloud Firestore consente alla tua applicazione di supportare rapidamente le query di database più basilari. Gli indici a campo singolo consentono di eseguire query semplici basate sui valori dei campi e sui comparatori <
, <=
, ==
, >=
, >
e in
. Per i campi dell'array, consentono di eseguire query array-contains
e array-contains-any
.
Per illustrare, esaminare i seguenti esempi dal punto di vista della creazione dell'indice. Il seguente snippet crea alcuni documenti city
in una raccolta cities
e imposta i campi name
, state
, country
, capital
, population
e tags
per ciascun documento:
ragnatela
var citiesRef = db.collection("cities"); citiesRef.doc("SF").set({ name: "San Francisco", state: "CA", country: "USA", capital: false, population: 860000, regions: ["west_coast", "norcal"] }); citiesRef.doc("LA").set({ name: "Los Angeles", state: "CA", country: "USA", capital: false, population: 3900000, regions: ["west_coast", "socal"] }); citiesRef.doc("DC").set({ name: "Washington, D.C.", state: null, country: "USA", capital: true, population: 680000, regions: ["east_coast"] }); citiesRef.doc("TOK").set({ name: "Tokyo", state: null, country: "Japan", capital: true, population: 9000000, regions: ["kanto", "honshu"] }); citiesRef.doc("BJ").set({ name: "Beijing", state: null, country: "China", capital: true, population: 21500000, regions: ["jingjinji", "hebei"] });
Presupponendo le impostazioni di indicizzazione automatica predefinite, Cloud Firestore aggiorna un indice di campo singolo ascendente per campo non di matrice, un indice di campo singolo discendente per campo non di matrice e un indice di campo singolo contenente array per il campo di matrice. Ogni riga nella tabella seguente rappresenta una voce in un indice a campo singolo:
Collezione | Campo indicizzato | Ambito della query |
---|---|---|
città | nome | Collezione |
città | stato | Collezione |
città | paese | Collezione |
città | freccia_capitale | Collezione |
città | freccia_popolazione | Collezione |
città | nome | Collezione |
città | stato | Collezione |
città | Collezione | |
città | freccia_capitale | Collezione |
città | freccia_popolazione | Collezione |
città | regioni array-contains | Collezione |
Query supportate da indici a campo singolo
Utilizzando questi indici a campo singolo creati automaticamente, puoi eseguire query semplici come le seguenti:
ragnatela
const stateQuery = citiesRef.where("state", "==", "CA"); const populationQuery = citiesRef.where("population", "<", 100000); const nameQuery = citiesRef.where("name", ">=", "San Francisco");
Puoi anche creare query in
e di uguaglianza composta ( ==
):
ragnatela
citiesRef.where('country', 'in', ["USA", "Japan", "China"]) // Compound equality queries citiesRef.where("state", "==", "CO").where("name", "==", "Denver") citiesRef.where("country", "==", "USA") .where("capital", "==", false) .where("state", "==", "CA") .where("population", "==", 860000)
Se è necessario eseguire una query composta che utilizza un confronto di intervalli ( <
, <=
, >
o >=
) o se è necessario eseguire l'ordinamento in base a un campo diverso, è necessario creare un indice composito per tale query.
L'indice array-contains
consente di interrogare il campo dell'array regions
:
ragnatela
citiesRef.where("regions", "array-contains", "west_coast") // array-contains-any and array-contains use the same indexes citiesRef.where("regions", "array-contains-any", ["west_coast", "east_coast"])
Query supportate da indici compositi
Cloud Firestore utilizza indici compositi per supportare query composte non già supportate da indici a campo singolo. Ad esempio, avresti bisogno di un indice composito per le seguenti query:
ragnatela
citiesRef.where("country", "==", "USA").orderBy("population", "asc") citiesRef.where("country", "==", "USA").where("population", "<", 3800000) citiesRef.where("country", "==", "USA").where("population", ">", 690000) // in and == clauses use the same index citiesRef.where("country", "in", ["USA", "Japan", "China"]) .where("population", ">", 690000)
Queste query richiedono l'indice composito riportato di seguito. Poiché la query utilizza un'uguaglianza ( ==
o in
) per il campo country
, è possibile utilizzare una modalità di indice ascendente o discendente per questo campo. Per impostazione predefinita, le clausole di disuguaglianza applicano un ordinamento crescente in base al campo nella clausola di disuguaglianza.
Collezione | Campi indicizzati | Ambito della query |
---|---|---|
città | paese | (o ), popolazioneCollezione |
Per eseguire le stesse query ma con un ordinamento discendente, è necessario un indice composito aggiuntivo nella direzione discendente per population
:
ragnatela
citiesRef.where("country", "==", "USA").orderBy("population", "desc") citiesRef.where("country", "==", "USA") .where("population", "<", 3800000) .orderBy("population", "desc") citiesRef.where("country", "==", "USA") .where("population", ">", 690000) .orderBy("population", "desc") citiesRef.where("country", "in", ["USA", "Japan", "China"]) .where("population", ">", 690000) .orderBy("population", "desc")
Collezione | Campi indicizzati | Ambito della query |
---|---|---|
città | paese, popolazione | Collezione |
città | paese , popolazione | Collezione |
È inoltre necessario creare un indice composito per combinare una query array-contains
o array-contains-any
con clausole aggiuntive.
ragnatela
citiesRef.where("regions", "array-contains", "east_coast") .where("capital", "==", true) // array-contains-any and array-contains use the same index citiesRef.where("regions", "array-contains-any", ["west_coast", "east_coast"]) .where("capital", "==", true)
Collezione | Campi indicizzati | Ambito della query |
---|---|---|
città | array-contiene tag, | (o ) maiuscolaCollezione |
Query supportate dagli indici dei gruppi di raccolta
Per dimostrare un indice con ambito gruppo di raccolte, immagina di aggiungere una sottoraccolta landmarks
ad alcuni documenti city
:
ragnatela
var citiesRef = db.collection("cities"); citiesRef.doc("SF").collection("landmarks").doc().set({ name: "Golden Gate Bridge", category : "bridge" }); citiesRef.doc("SF").collection("landmarks").doc().set({ name: "Golden Gate Park", category : "park" }); citiesRef.doc("DC").collection("landmarks").doc().set({ name: "National Gallery of Art", category : "museum" }); citiesRef.doc("DC").collection("landmarks").doc().set({ name: "National Mall", category : "park" });
Utilizzando il seguente indice a campo singolo con ambito di raccolta, puoi eseguire query sulla raccolta di landmarks
di una singola città in base al campo category
:
Collezione | Campi indicizzati | Ambito della query |
---|---|---|
punti di riferimento | categoria | (o ).Collezione |
ragnatela
citiesRef.doc("SF").collection("landmarks").where("category", "==", "park") citiesRef.doc("SF").collection("landmarks").where("category", "in", ["park", "museum"])
Ora, immagina di essere interessato a interrogare i punti di riferimento in tutte le città. Per eseguire questa query sul gruppo di raccolte costituito da tutte le raccolte landmarks
, è necessario abilitare un indice a campo singolo landmarks
con ambito del gruppo di raccolte:
Collezione | Campi indicizzati | Ambito della query |
---|---|---|
punti di riferimento | categoria | (o ).Gruppo di raccolta |
Con questo indice abilitato, puoi interrogare il gruppo di raccolta landmarks
:
ragnatela
var landmarksGroupRef = db.collectionGroup("landmarks"); landmarksGroupRef.where("category", "==", "park") landmarksGroupRef.where("category", "in", ["park", "museum"])
Per eseguire una query del gruppo di raccolta che restituisce risultati filtrati o ordinati, è necessario abilitare un indice composito o a campo singolo corrispondente con ambito del gruppo di raccolta. Le query sui gruppi di raccolte che non filtrano né ordinano i risultati, tuttavia, non richiedono definizioni di indice aggiuntive.
Ad esempio, puoi eseguire la seguente query del gruppo di raccolta senza abilitare un indice aggiuntivo:
ragnatela
db.collectionGroup("landmarks").get()
Voci dell'indice
Gli indici configurati del tuo progetto e la struttura di un documento determinano il numero di voci di indice per un documento. Le voci dell'indice vengono conteggiate ai fini del limite di conteggio delle voci dell'indice .
Nell'esempio seguente vengono illustrate le voci di indice di un documento.
Documento
/cities/SF
city_name : "San Francisco"
temperatures : {summer: 67, winter: 55}
neighborhoods : ["Mission", "Downtown", "Marina"]
Indici a campo singolo
- nome_città ASC
- nome_città DESC
- temperature.estate ASC
- temperature.estate DESC
- temperature.inverno ASC
- temperature.inverno DESC
- quartieri Array Contiene (ASC e DESC)
Indici compositi
- nome_città ASC, quartieri ARRAY
- city_name DESC, quartieri ARRAY
Voci dell'indice
Questa configurazione di indicizzazione risulta nelle seguenti 18 voci di indice per il documento:
Indice | Dati indicizzati |
---|---|
Voci di indice a campo singolo | |
nome_città ASC | nome_città: "San Francisco" |
nome_città DESC | nome_città: "San Francisco" |
temperature.estate ASC | temperature.estate: 67 |
temperature.estate DESC | temperature.estate: 67 |
temperature.inverno ASC | temperature.inverno: 55 |
temperature.inverno DESC | temperature.inverno: 55 |
quartieri Array Contiene ASC | quartieri: "Missione" |
quartieri Array Contiene DESC | quartieri: "Missione" |
quartieri Array Contiene ASC | quartieri: "Centro" |
quartieri Array Contiene DESC | quartieri: "Centro" |
quartieri Array Contiene ASC | quartieri: "Marina" |
quartieri Array Contiene DESC | quartieri: "Marina" |
Voci dell'indice composito | |
nome_città ASC, quartieri ARRAY | city_name: "San Francisco", quartieri: "Mission" |
nome_città ASC, quartieri ARRAY | city_name: "San Francisco", quartieri: "Downtown" |
nome_città ASC, quartieri ARRAY | city_name: "San Francisco", quartieri: "Marina" |
city_name DESC, quartieri ARRAY | city_name: "San Francisco", quartieri: "Mission" |
city_name DESC, quartieri ARRAY | city_name: "San Francisco", quartieri: "Downtown" |
city_name DESC, quartieri ARRAY | city_name: "San Francisco", quartieri: "Marina" |
Indici e prezzi
Gli indici contribuiscono ai costi di archiviazione della tua applicazione. Per ulteriori informazioni su come vengono calcolate le dimensioni di archiviazione per gli indici, vedere Dimensioni della voce dell'indice .
Sfruttare la fusione degli indici
Sebbene Cloud Firestore utilizzi un indice per ogni query, non richiede necessariamente un indice per query. Per le query con più clausole di uguaglianza ( ==
) e, facoltativamente, una clausola orderBy
, Cloud Firestore può riutilizzare gli indici esistenti. Cloud Firestore può unire gli indici per semplici filtri di uguaglianza per creare gli indici compositi necessari per query di uguaglianza più grandi.
Puoi ridurre i costi di indicizzazione identificando le situazioni in cui puoi trarre vantaggio dall'unione degli indici. Ad esempio, immagina una raccolta restaurants
per un'app di valutazione dei ristoranti:
name : "Burger Thyme"
category : "burgers"
city : "San Francisco"
editors_pick : true
star_rating : 4
Ora, immagina che questa app utilizzi query come quelle seguenti. Tieni presente che l'app utilizza combinazioni di clausole di uguaglianza per category
, city
e editors_pick
ordinando sempre in ordine crescente star_rating
:
ragnatela
db.collection("restaurants").where("category", "==", "burgers") .orderBy("star_rating") db.collection("restaurants").where("city", "==", "San Francisco") .orderBy("star_rating") db.collection("restaurants").where("category", "==", "burgers") .where("city", "==", "San Francisco") .orderBy("star_rating") db.collection("restaurants").where("category", "==", "burgers") .where("city", "==" "San Francisco") .where("editors_pick", "==", true ) .orderBy("star_rating")
Potresti creare un indice per ogni query:
Collezione | Campi indicizzati | Ambito della query |
---|---|---|
ristoranti | categoria | , valutazione_stelleCollezione |
ristoranti | città, l'alto_classificazione | Collezione |
ristoranti | categoria | , città,Collezione |
ristoranti | categoria | , città, editors_pick, per stelle_ratingCollezione |
Come soluzione migliore, puoi ridurre il numero di indici sfruttando la capacità di Cloud Firestore di unire gli indici per clausole di uguaglianza:
Collezione | Campi indicizzati | Ambito della query |
---|---|---|
ristoranti | categoria | , valutazione_stelleCollezione |
ristoranti | città, l'alto_classificazione | Collezione |
ristoranti | editors_pick, star_rating | Collezione |
Non solo questo insieme di indici è più piccolo, ma supporta anche una query aggiuntiva:
ragnatela
db.collection("restaurants").where("editors_pick", "==", true) .orderBy("star_rating")
Limiti di indicizzazione
Agli indici si applicano i seguenti limiti. Per tutte le quote e i limiti, vedere Quote e limiti .
Limite | Dettagli |
---|---|
Numero massimo di indici compositi per un database |
|
Numero massimo di configurazioni a campo singolo per un database |
Una configurazione a livello di campo può contenere più configurazioni per lo stesso campo. Ad esempio, un'esenzione dall'indicizzazione di un campo singolo e una policy TTL sullo stesso campo contano come una configurazione di campo ai fini del limite. |
Numero massimo di voci di indice per ciascun documento | 40.000 Il numero di voci dell'indice è la somma dei seguenti valori per un documento:
Per vedere come Cloud Firestore trasforma un documento e un set di indici in voci di indice, consulta questo esempio di conteggio delle voci di indice . |
Numero massimo di campi in un indice composito | 100 |
Dimensione massima di una voce di indice | 7,5 KiB Per vedere come Cloud Firestore calcola la dimensione della voce di indice, consulta dimensione della voce di indice . |
Somma massima delle dimensioni delle voci di indice di un documento | 8 MB La dimensione totale è la somma dei seguenti valori per un documento: |
Dimensione massima di un valore di campo indicizzato | 1500 byte I valori dei campi superiori a 1500 byte vengono troncati. Le query che coinvolgono valori di campo troncati potrebbero restituire risultati incoerenti. |
Migliori pratiche di indicizzazione
Per la maggior parte delle app, puoi fare affidamento sull'indicizzazione automatica e sui collegamenti ai messaggi di errore per gestire i tuoi indici. Tuttavia, potresti voler aggiungere esenzioni per campo singolo nei seguenti casi:
Caso | Descrizione |
---|---|
Campi stringa di grandi dimensioni | Se disponi di un campo stringa che spesso contiene valori stringa lunghi che non utilizzi per le query, puoi ridurre i costi di archiviazione esentando il campo dall'indicizzazione. |
Velocità di scrittura elevate in una raccolta contenente documenti con valori sequenziali | Se indicizzi un campo che aumenta o diminuisce in sequenza tra i documenti di una raccolta, come un timestamp, la velocità di scrittura massima nella raccolta è di 500 scritture al secondo. Se non esegui query in base al campo con valori sequenziali, puoi esentare il campo dall'indicizzazione per aggirare questo limite. In un caso d'uso IoT con un'elevata velocità di scrittura, ad esempio, una raccolta contenente documenti con un campo timestamp potrebbe avvicinarsi al limite di 500 scritture al secondo. |
Campi TTL | Se utilizzi le policy TTL (time-to-live) , tieni presente che il campo TTL deve essere un timestamp. L'indicizzazione sui campi TTL è abilitata per impostazione predefinita e può influire sulle prestazioni a velocità di traffico più elevate. Come procedura consigliata, aggiungi esenzioni per campo singolo per i campi TTL. |
Campi di matrice o mappa di grandi dimensioni | I campi di matrice o mappa di grandi dimensioni possono raggiungere il limite di 40.000 voci di indice per documento. Se non stai eseguendo query in base a un array di grandi dimensioni o a un campo mappa, dovresti esentarlo dall'indicizzazione. |
Per ulteriori informazioni su come risolvere i problemi di indicizzazione (fanout dell'indice, errori INVALID_ARGUMENT
) consulta la pagina di risoluzione dei problemi .