Panoramica dell'indicizzazione
Gli indici sono un fattore importante nelle prestazioni di un database. Proprio come l'indice di un libro che mappa gli argomenti di un libro ai numeri di pagina, l'indice di un database mappa gli elementi di un database alle relative posizioni nel database. Quando invii una query a un database, il database può utilizzare un indice per cercare rapidamente le posizioni degli elementi richiesti.
In questa pagina vengono descritti i due tipi di indici utilizzati da Firestore, gli indici a campo singolo e gli indici composti.
Un indice dietro ogni query
Se per una query non esiste un indice, la maggior parte dei database esegue la scansione per trovare l'elemento dei contenuti, un processo lento che rallenta ancora di più man mano che il database cresce. 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 dell'indice, più sviluppo di app
Firestore include funzionalità che riducono la quantità di tempo necessario per gestire gli indici. Gli indici necessari per le query di base vengono creati automaticamente. Mentre utilizzi e testi la tua app, Firestore ti aiuta a identificare e creare indici aggiuntivi richiesti dall'app.
Tipi di indice
Firestore utilizza due tipi di indici: singolo campo e composito. Oltre al numero di campi indicizzati, gli indici a campo singolo e composti differiscono nel modo in cui vengono gestiti.
Indici a campo singolo
Un indice a campo singolo archivia 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. 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 database.
Indicizzazione automatica
Per impostazione predefinita, Firestore mantiene automaticamente gli indici a campo singolo per ogni campo in un documento e ogni sottocampo di una mappa. Firestore utilizza le seguenti impostazioni predefinite per gli indici a campo singolo:
Per ogni campo non array e non mappa, Firestore definisce due indici a campo singolo scope-scope, uno in modalità ascendente e uno in modalità discendente.
Per ogni campo mappa, Firestore crea quanto segue:
- Un indice crescente per l'ambito della raccolta per ogni sottocampo non array e non mappa.
- Un indice decrescente per l'ambito della raccolta per ogni sottocampo non array e non mappa.
- Un array di ambito raccolta contiene l'indice per ogni sottocampo dell'array.
- Firestore indicizza in modo ricorsivo ogni sottocampo di mappa.
Per ogni campo di un array in un documento, Firestore crea e mantiene un indice di array con ambito raccolta.
Gli indici a campo singolo con ambito a livello di gruppo di raccolte non vengono gestiti per impostazione predefinita.
Esenzioni dell'indice a campo singolo
Puoi escludere un campo dalle impostazioni di indicizzazione automatica creando un'esenzione per l'indice a campo singolo. Un'esenzione dell'indicizzazione esegue l'override delle impostazioni dell'indice automatico a livello di database. Un'esenzione può attivare un indice a campo singolo che altrimenti verrebbe disattivato o disattivato dalle impostazioni di indicizzazione automatica che altrimenti verrebbe attivato da un indice a campo singolo. Nei casi in cui le esenzioni possono essere utili, consulta le best practice per l'indicizzazione.
Se crei un'esenzione per l'indice a campo singolo per un campo mappa, i campi secondari della mappa ereditano queste impostazioni. Tuttavia, puoi definire esenzioni dall'indice a campo singolo per sottocampi specifici. Se elimini un'esenzione per un sottocampo, quest'ultimo erediterà le impostazioni di esenzione dell'elemento principale, se esistono, oppure le impostazioni a livello di database, se non esistono esenzioni padre.
Per creare e gestire le esenzioni a campo singolo, consulta Gestione degli indici in Firestore.
Indici composti
Un indice composito memorizza una mappatura ordinata di tutti i documenti in una raccolta, sulla base di un elenco ordinato di campi da indicizzare.
Firestore utilizza indici composti per supportare le query non già supportate dagli indici a campo singolo.
Firestore non crea automaticamente indici compositi come fa per gli indici a campo singolo a causa dell'elevato numero di possibili combinazioni di campi. Al loro posto, Firestore ti aiuta a identificare e creare gli indici composti durante la creazione della tua app.
Se provi a eseguire la query in alto senza prima creare l'indice richiesto, Firestore restituisce un messaggio di errore contenente un link che puoi seguire per creare l'indice mancante. Questo si verifica ogni volta che provi una query non supportata da un indice. Puoi anche definire e gestire manualmente gli indici composti utilizzando la console o l'interfaccia a riga di comando di Firebase. Per scoprire di più sulla creazione e la gestione degli indici composti, consulta l'articolo Gestione degli indici.
Modalità dell'indice e ambiti di query
Configura gli indici a campo singolo e composito in modo diverso, ma entrambi richiedono la configurazione delle modalità di indice e degli ambiti di query per gli indici.
Modalità indice
Quando definisci un indice, selezioni una modalità di indicizzazione per ogni campo indicizzato. La modalità indice di ogni campo supporta clausole di query specifiche in quel campo. Puoi selezionare una delle seguenti modalità di indice:
Modalità indice | Descrizione |
---|---|
crescente | Supporta le clausole di query sul campo < , <= , == , >= , > , != , in e not-in e supporta l'ordinamento dei risultati in ordine crescente in base a questo valore di campo. |
Decrescente | Supporta le clausole di query < , <= , == , >= , > , != , in e not-in sul campo e supporta l'ordinamento dei risultati in ordine decrescente in base a questo valore del campo. |
Array-contiene | Supporta le clausole di query array-contains e array-contains-any sul campo. |
Ambiti di query
Ogni ambito ha un ambito sia a una raccolta che a un gruppo di raccolte. Questo è noto come ambito di ricerca dell'indice:
- Ambito della raccolta
- Firestore crea gli indici con l'ambito della raccolta per impostazione predefinita. Questi indici supportano le query che restituiscono risultati da un'unica raccolta.
- Ambito del gruppo di raccolte
- Un gruppo di raccolte include tutte le raccolte con lo stesso ID raccolta. Per eseguire una query di un gruppo di raccolte che restituisce risultati filtrati o ordinati da un gruppo di raccolte, devi creare un indice corrispondente con l'ambito del gruppo di raccolte.
Ordine predefinito e campo __name__
Oltre a ordinare i documenti in base alle modalità di indicizzazione specificate per ciascun campo (crescente o decrescente) , gli indici applicano un ordinamento finale in base al campo __name__
di ogni 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 dei campi vengono ordinati per percorso documento.
Per impostazione predefinita, il campo __name__
è ordinato nella stessa direzione dell'ultimo campo ordinato nella definizione dell'indice. Ecco alcuni esempi:
Raccolta | Campi indicizzati | Ambito di query |
---|---|---|
cities | Nome __name__ |
, Raccolta |
cities | Stato __name__ |
, Raccolta |
cities | __name__ |
paese, popolazione, Raccolta |
Per ordinare i risultati in base alla direzione __name__
non predefinita, devi creare l'indice.
Esempio di indicizzazione
Creando automaticamente gli indici a campo singolo per te, Firestore consente alla tua applicazione di supportare rapidamente le query di database più basilari.
Gli indici a campo singolo consentono di eseguire query semplici sulla base dei valori dei campi
e dei comparatori <
, <=
, ==
, >=
, >
e in
. Per i campi dell'array, consentono di eseguire query array-contains
e array-contains-any
.
Per illustrare, esamina 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 ogni documento:
Web
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"] });
Presumendo le impostazioni predefinite di indicizzazione automatica, Firestore aggiorna un indice a campo singolo in ordine crescente per campo non array, un indice a campo singolo decrescente per campo non array e un indice a campo singolo con array per il campo array. Ogni riga della tabella seguente rappresenta una voce in un indice a campo singolo:
Raccolta | Campo indicizzato | Ambito di query |
---|---|---|
cities | Nome | Raccolta |
cities | Stato | Raccolta |
cities | paese | Raccolta |
cities | capitale | Raccolta |
cities | Popolazione di | Raccolta |
cities | Nome | Raccolta |
cities | Stato | Raccolta |
cities | paese | Raccolta |
cities | capitale | Raccolta |
cities | Popolazione di | Raccolta |
cities | array-contains regioni |
Raccolta |
Query supportate da indici a campo singolo
Utilizzando questi indici a campo singolo creati automaticamente, puoi eseguire semplici query come segue:
Web
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 uguaglianza composta (==
):
Web
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 devi eseguire una query composta che utilizza un confronto a intervallo (<
, <=
, >
o >=
) o se devi ordinare in base a un campo diverso, devi creare un indice composito per tale query.
L'indice array-contains
consente di eseguire query sul campo dell'array regions
:
Web
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 composti
Firestore utilizza indici composti per supportare le query composte non ancora supportate dagli indici a campo singolo. Ad esempio, ti servirà un indice composito per le seguenti query:
Web
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
, puoi utilizzare una modalità di indice in ordine crescente o decrescente per questo campo. Per impostazione predefinita, le clausole di disuguaglianza applicano un ordinamento crescente in base al campo nella clausola di disuguaglianza.
Raccolta | Campi indicizzati | Ambito di query |
---|---|---|
cities | (o ) paese, popolazione | Raccolta |
Per eseguire le stesse query ma con un ordinamento decrescente, hai bisogno di un indice composito aggiuntivo nella direzione discendente per population
:
Web
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")
Raccolta | Campi indicizzati | Ambito di query |
---|---|---|
cities | paese, popolazione | Raccolta |
città | paese, popolazione | Raccolta |
Devi inoltre creare un indice composito per combinare una query array-contains
o array-contains-any
con clausole aggiuntive.
Web
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)
Raccolta | Campi indicizzati | Ambito di query |
---|---|---|
cities | Tag array-contains, | (o )Raccolta |
Query supportate dagli indici dei gruppo di raccolte
Per dimostrare un indice con l'ambito del gruppo di raccolte, immagina di aggiungere una sottoraccolta landmarks
ad alcuni dei documenti city
:
Web
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 una query sulla raccolta landmarks
di una singola città in base al campo category
:
Raccolta | Campi indicizzati | Ambito di query |
---|---|---|
punti di riferimento | Categoria | (o )Raccolta |
Web
citiesRef.doc("SF").collection("landmarks").where("category", "==", "park") citiesRef.doc("SF").collection("landmarks").where("category", "in", ["park", "museum"])
A questo punto, immagina di essere interessato a eseguire query sui punti di riferimento di tutte le città.
Per eseguire questa query sul gruppo di raccolte costituito da tutte le raccolte landmarks
, devi abilitare un indice a campo singolo landmarks
con l'ambito del gruppo di raccolte:
Raccolta | Campi indicizzati | Ambito di query |
---|---|---|
punti di riferimento | Categoria | (o )Gruppo di raccolte |
Con questo indice abilitato, puoi eseguire una query sul gruppo di raccolte landmarks
:
Web
var landmarksGroupRef = db.collectionGroup("landmarks"); landmarksGroupRef.where("category", "==", "park") landmarksGroupRef.where("category", "in", ["park", "museum"])
Per eseguire una query di un gruppo di raccolte che restituisce risultati filtrati o ordinati, devi abilitare un indice a campo singolo o composito corrispondente con l'ambito del gruppo di raccolte. Le query dei gruppi di raccolte che non filtrano o non restituiscono i risultati, tuttavia, non richiedono definizioni di indice aggiuntive.
Ad esempio, puoi eseguire la seguente query su un gruppo di raccolte senza abilitare un indice aggiuntivo:
Web
db.collectionGroup("landmarks").get()
Voci di indice
Gli indici configurati dal tuo progetto e la struttura del documento determinano il numero di voci di indice di un documento. Le voci dell'indice vengono conteggiate per il limite di inserimento dell'indice.
L'esempio seguente mostra le voci di indice di un documento.
Documenti
/cities/SF
city_name : "San Francisco"
temperatures : {summer: 67, winter: 55}
neighborhoods : ["Mission", "Downtown", "Marina"]
Indici a campo singolo
- ASC nome_città
- nome_città_DESC
- temperature.sull'estate
- temperature.summer DESC
- temperature.ASC invernale
- temperature.inverno DESC
- L'array di quartieri contiene (ASC e DESC)
Indici composti
- città_nome ASC, quartieri ARRAY
- city_name DESC, quartieri ARRAY
Voci di indice
Questa configurazione di indicizzazione genera le seguenti 18 voci di indice per il documento:
Indice | Dati indicizzati |
---|---|
Voci dell'indice a campo singolo | |
ASC nome_città | city_name: "San Francisco" |
nome_città_DESC | city_name: "San Francisco" |
temperature.sull'estate | temperature.estate: 67 |
temperature.summer DESC | temperature.estate: 67 |
temperature.ASC invernale | temperature.inverno: 55 |
temperature.inverno DESC | temperature.inverno: 55 |
L'array di quartieri contiene ASC | quartieri: "Missione" |
L'array di quartieri contiene DESC | quartieri: "Missione" |
L'array di quartieri contiene ASC | quartieri: "Centro" |
L'array di quartieri contiene DESC | quartieri: "Centro" |
L'array di quartieri contiene ASC | quartieri: "Marina" |
L'array di quartieri contiene DESC | quartieri: "Marina" |
Voci dell'indice composito | |
città_nome ASC, quartieri ARRAY | city_name: "San Francisco", quartieri: "Mission" |
città_nome ASC, quartieri ARRAY | city_name: "San Francisco", quartieri: "Centro" |
città_nome 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: "Centro" |
city_name DESC, quartieri ARRAY | city_name: "San Francisco", quartieri: "Marina" |
Indici e prezzi
Gli indici contribuiscono ai costi di archiviazione dell'applicazione. Per scoprire di più su come vengono calcolate le dimensioni dello spazio di archiviazione per gli indici, consulta Dimensioni voce indice.
Sfruttare l'unione dell'indice
Sebbene Firestore utilizzi un indice per ogni query, non
richiede necessariamente un indice per query. Per query con più clausole di uguaglianza
(==
) e, facoltativamente, con una clausola orderBy
, Firestore può
riutilizzare gli indici esistenti. Firestore può unire gli indici per ottenere semplici filtri di uguaglianza al fine di creare gli indici composti necessari per le query sull'uguaglianza più importanti.
Puoi ridurre i costi di indicizzazione identificando le situazioni in cui puoi sfruttare
l'unione di indici. Ad esempio, immagina una raccolta restaurants
per un'app di valutazione di un ristorante:
ristoranti
hamburger
name : "Burger Thyme"
category : "burgers"
city : "San Francisco"
editors_pick : true
star_rating : 4
Ora immagina che questa app utilizzi query come quelle di seguito. Tieni presente che l'app utilizza combinazioni di clausole di uguaglianza per category
, city
e editors_pick
durante l'ordinamento in ordine crescente di star_rating
:
Web
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:
Raccolta | Campi indicizzati | Ambito di query |
---|---|---|
ristoranti | Categoria | , stella_valutazioneRaccolta |
ristoranti | città, stella_classificazione | Raccolta |
ristoranti | Categoria | , città, star_ratingRaccolta |
ristoranti | Categoria | , città, editor_pick, star_ratingRaccolta |
Come soluzione migliore, puoi ridurre il numero di indici sfruttando la capacità di Firestore di unire gli indici per le clausole di uguaglianza:
Raccolta | Campi indicizzati | Ambito di query |
---|---|---|
ristoranti | Categoria | , stella_valutazioneRaccolta |
ristoranti | città, stella_classificazione | Raccolta |
ristoranti | editor_pick, star_rating | Raccolta |
Questo insieme di indici non è solo più piccolo, ma supporta anche una query aggiuntiva:
Web
db.collection("restaurants").where("editors_pick", "==", true) .orderBy("star_rating")
Limiti di indicizzazione
I seguenti limiti sono applicabili agli indici. Per tutte le quote e i limiti, consulta Quote e limiti.
Limite | Dettagli |
---|---|
Numero massimo di indici composti per un database |
200 Puoi contattare l'assistenza per richiedere un aumento di questo limite. |
Numero massimo di configurazioni a campo singolo per un database |
200 Sono consentite un totale di 200 configurazioni a livello di campo. Una configurazione di un campo può contenere più configurazioni per lo stesso campo. Ad esempio, ai fini del limite, un'esenzione per l'indicizzazione a campo singolo e un criterio TTL sullo stesso campo vengono conteggiati come un'unica configurazione dei campi. |
Numero massimo di voci di indice per ogni documento |
40.000 Il numero di voci di indice corrisponde alla somma di quanto segue per un documento:
Per scoprire in che modo Firestore trasforma un documento e una serie di indici in voci di indice, vedi questo esempio di conteggio di voci di indice. |
Numero massimo di campi in un indice composito | 100 |
Dimensione massima di una voce di indice |
7,5 KiB Per scoprire in che modo Firestore calcola la dimensione di una voce di indice, vai alla pagina Dimensioni delle voci di indice. |
Somma massima delle dimensioni delle voci di indice di un documento |
8 MiB La dimensione totale corrisponde alla somma di quanto segue per un documento: |
Dimensione massima di un valore di campo indicizzato |
1500 byte I valori di campo superiori a 1500 byte vengono troncati. Le query che includono valori di campo troncati possono restituire risultati incoerenti. |
Best practice per l'indicizzazione
Per la maggior parte delle app, puoi fare affidamento sull'indicizzazione automatica e sui link dei messaggi di errore per gestire gli indici. Tuttavia, puoi aggiungere esenzioni a campo singolo nei seguenti casi:
Caso | Descrizione |
---|---|
Campi stringa di grandi dimensioni | Se hai un campo stringa che spesso contiene valori stringa lunghi che non utilizzi per le query, puoi ridurre i costi di archiviazione escludendo 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 modo sequenziale tra i documenti di una raccolta, ad esempio un timestamp, la frequenza di scrittura massima nella raccolta è 500 scritture al secondo. Se non esegui query basate sul campo con valori sequenziali, puoi escludere il campo dall'indicizzazione per bypassare questo limite. In un caso d'uso IoT con una frequenza di scrittura elevata, ad esempio, una raccolta contenente documenti con un campo timestamp potrebbe raggiungere il limite di 500 scritture al secondo. |
Campi TTL |
Se utilizzi criteri TTL (Time to Live), tieni presente che il campo TTL deve essere un timestamp. L'indicizzazione nei campi TTL è abilitata per impostazione predefinita e può influire sulle prestazioni a frequenze di traffico più elevate. Come best practice, aggiungi esenzioni a campo singolo per i tuoi campi TTL. |
Campi di grandi dimensioni o di mappe | I campi di grandi dimensioni o di mappe possono raggiungere il limite di 40.000 voci di indice per documento. Se non esegui query in base a un ampio array o a un campo mappa, devi esenti l'indicizzazione. |
Per ulteriori informazioni su come risolvere i problemi di indicizzazione (errore di fanout dell'indice, INVALID_ARGUMENT
errori), consulta la pagina per la risoluzione dei problemi.