Recuperare i dati con le operazioni della pipeline Firestore

Contesto

Le operazioni della pipeline forniscono una nuova interfaccia di query per Cloud Firestore che supporta funzionalità di query avanzate ed espressioni complesse. Introduce molte nuove funzioni, tra cui min(...), max(...), substring(...), regex_match(...) e array_contains_all(...), e fasi per poter eseguire trasformazioni complesse.

Per iniziare

Per installare e inizializzare gli SDK client, consulta le istruzioni nelle guide seguenti:

Sintassi

Le sezioni seguenti forniscono una panoramica della sintassi per le operazioni della pipeline.

Concetti

Una differenza notevole rispetto alle operazioni della pipeline è l'introduzione di un ordinamento esplicito delle "fasi". Ciò consente di esprimere query più complesse. Tuttavia, si tratta di una deviazione notevole dall'interfaccia di query esistente che utilizza le operazioni di base, in cui l'ordinamento delle fasi era implicito. Considera il seguente esempio di operazioni della pipeline:

Web

const pipeline = db.pipeline()
  // Step 1: Start a query with collection scope
  .collection("cities")
  // Step 2: Filter the collection
  .where(field("population").greaterThan(100000))
  // Step 3: Sort the remaining documents
  .sort(field("name").ascending())
  // Step 4: Return the top 10. Note applying the limit earlier in the
  // pipeline would have unintentional results.
  .limit(10);
Swift
let pipeline = db.pipeline()
  // Step 1: Start a query with collection scope
  .collection("cities")
  // Step 2: Filter the collection
  .where(Field("population").greaterThan(100000))
  // Step 3: Sort the remaining documents
  .sort([Field("name").ascending()])
  // Step 4: Return the top 10. Note applying the limit earlier in the pipeline would have
  // unintentional results.
  .limit(10)

Kotlin

val pipeline = db.pipeline()
    // Step 1: Start a query with collection scope
    .collection("cities")
    // Step 2: Filter the collection
    .where(field("population").greaterThan(100000))
    // Step 3: Sort the remaining documents
    .sort(field("name").ascending())
    // Step 4: Return the top 10. Note applying the limit earlier in the pipeline would have
    // unintentional results.
    .limit(10)

Java

Pipeline pipeline = db.pipeline()
    // Step 1: Start a query with collection scope
    .collection("cities")
    // Step 2: Filter the collection
    .where(field("population").greaterThan(100000))
    // Step 3: Sort the remaining documents
    .sort(field("name").ascending())
    // Step 4: Return the top 10. Note applying the limit earlier in the pipeline would have
    // unintentional results.
    .limit(10);
Python
from google.cloud.firestore_v1.pipeline_expressions import Field

pipeline = (
    client.pipeline()
    .collection("cities")
    .where(Field.of("population").greater_than(100_000))
    .sort(Field.of("name").ascending())
    .limit(10)
)

Inizializzazione

Le operazioni della pipeline hanno una sintassi molto familiare derivante dalle query Cloud Firestore esistenti. Per iniziare, inizializza una query scrivendo quanto segue:

Web

const { getFirestore } = require("firebase/firestore");
const { execute } = require("firebase/firestore/pipelines");
const database = getFirestore(app, "enterprise");
const pipeline = database.pipeline();
Swift
let firestore = Firestore.firestore(database: "enterprise")
let pipeline = firestore.pipeline()

Kotlin

val firestore = Firebase.firestore("enterprise")
val pipeline = firestore.pipeline()

Java

FirebaseFirestore firestore = FirebaseFirestore.getInstance("enterprise");
PipelineSource pipeline = firestore.pipeline();
Python
firestore_client = firestore.client(default_app, "your-new-enterprise-database")
pipeline = firestore_client.pipeline()

Struttura

Esistono alcuni termini importanti da comprendere quando crei operazioni della pipeline: fasi, espressioni e funzioni e wrapper delle sottoquery.

Esempio che mostra fasi ed espressioni in una query

Fasi:una pipeline può essere costituita da una o più fasi. A livello logico, questi rappresentano la serie di passaggi (o fasi) eseguiti per eseguire la query.

Espressioni:spesso gli stadi accettano un'espressione che ti consente di esprimere query più complesse. L'espressione può essere semplice e costituita da una singola funzione come eq("a", 1). Puoi anche esprimere espressioni più complesse nidificando espressioni come and(eq("a", 1), eq("b", 2)).

Wrapper per le sottoquery:funzioni come array() e scalar() ti consentono di incorporare una pipeline nidificata come espressione all'interno di una fase.

Campi / Costanti / Variabili

Le operazioni della pipeline supportano espressioni complesse. Pertanto, è importante distinguere se un valore rappresenta un campo, una costante o una variabile.

Mentre i campi si riferiscono ai dati all'interno dei documenti e le costanti consentono di specificare qualsiasi valore come argomento di un'espressione, le variabili consentono di definire e utilizzare valori temporanei con ambito di esecuzione della query anziché dei documenti in fase di elaborazione. Di seguito viene fornita una panoramica di questi concetti. Per saperne di più su come leggere e scrivere variabili durante l'esecuzione delle query, consulta la fase let(...).

Campi Costanti Variabili
Purpose accedere ai campi o memorizzarli nei documenti specificare un valore fisso utilizzare valori temporanei durante l'esecuzione della pipeline
Utilizzo dell'SDK field("name") constant("val") variable("name")
Ambito locale al documento corrente globale globale a pipeline e pipeline secondarie
Undefined Reference restituisce absent N/D genera un errore di runtime

Esempi:

Web

const pipeline = db.pipeline()
  .collection("cities")
  .where(field("name").equal(constant("Toronto")));
Swift
let pipeline = db.pipeline()
  .collection("cities")
  .where(Field("name").equal(Constant("Toronto")))

Kotlin

val pipeline = db.pipeline()
    .collection("cities")
    .where(field("name").equal(constant("Toronto")))

Java

Pipeline pipeline = db.pipeline()
    .collection("cities")
    .where(field("name").equal(constant("Toronto")));
Python
from google.cloud.firestore_v1.pipeline_expressions import Field, Constant

pipeline = (
    client.pipeline()
    .collection("cities")
    .where(Field.of("name").equal(Constant.of("Toronto")))
)

Fasi

Fasi di input

La fase di input rappresenta la prima fase di una query. Definisce l'insieme iniziale di documenti su cui esegui la query. Per le operazioni della pipeline, questo è in gran parte simile alle query esistenti, in cui la maggior parte delle query inizia con una fase collection(...) o collection_group(...). Le due nuove fasi di input sono database() e documents(...), dove database() consente di restituire tutti i documenti nel database, mentre documents(...) si comporta in modo identico a una lettura batch.

Web

let results;

// Return all restaurants in San Francisco
results = await execute(db.pipeline().collection("cities/sf/restaurants"));

// Return all restaurants
results = await execute(db.pipeline().collectionGroup("restaurants"));

// Return all documents across all collections in the database (the entire database)
results = await execute(db.pipeline().database());

// Batch read of 3 documents
results = await execute(db.pipeline().documents([
  doc(db, "cities", "SF"),
  doc(db, "cities", "DC"),
  doc(db, "cities", "NY")
]));
Swift
var results: Pipeline.Snapshot

// Return all restaurants in San Francisco
results = try await db.pipeline().collection("cities/sf/restaurants").execute()

// Return all restaurants
results = try await db.pipeline().collectionGroup("restaurants").execute()

// Return all documents across all collections in the database (the entire database)
results = try await db.pipeline().database().execute()

// Batch read of 3 documents
results = try await db.pipeline().documents([
  db.collection("cities").document("SF"),
  db.collection("cities").document("DC"),
  db.collection("cities").document("NY")
]).execute()

Kotlin

var results: Task<Pipeline.Snapshot>

// Return all restaurants in San Francisco
results = db.pipeline().collection("cities/sf/restaurants").execute()

// Return all restaurants
results = db.pipeline().collectionGroup("restaurants").execute()

// Return all documents across all collections in the database (the entire database)
results = db.pipeline().database().execute()

// Batch read of 3 documents
results = db.pipeline().documents(
    db.collection("cities").document("SF"),
    db.collection("cities").document("DC"),
    db.collection("cities").document("NY")
).execute()

Java

Task<Pipeline.Snapshot> results;

// Return all restaurants in San Francisco
results = db.pipeline().collection("cities/sf/restaurants").execute();

// Return all restaurants
results = db.pipeline().collectionGroup("restaurants").execute();

// Return all documents across all collections in the database (the entire database)
results = db.pipeline().database().execute();

// Batch read of 3 documents
results = db.pipeline().documents(
    db.collection("cities").document("SF"),
    db.collection("cities").document("DC"),
    db.collection("cities").document("NY")
).execute();
Python
# Return all restaurants in San Francisco
results = client.pipeline().collection("cities/sf/restaurants").execute()

# Return all restaurants
results = client.pipeline().collection_group("restaurants").execute()

# Return all documents across all collections in the database (the entire database)
results = client.pipeline().database().execute()

# Batch read of 3 documents
results = (
    client.pipeline()
    .documents(
        client.collection("cities").document("SF"),
        client.collection("cities").document("DC"),
        client.collection("cities").document("NY"),
    )
    .execute()
)

Come per tutte le altre fasi, l'ordine dei risultati di queste fasi di input non è stabile. Un operatore sort(...) deve sempre essere aggiunto se è richiesto un ordinamento specifico.

Dove

La fase where(...) funge da operazione di filtro standard sui documenti generati dalla fase precedente e rispecchia per lo più la sintassi "where" esistente per le query esistenti. Qualsiasi documento per cui una determinata espressione restituisce un valore diverso da true viene filtrato dai documenti restituiti.

È possibile concatenare più istruzioni where(...) e utilizzarle come espressione and(...). Ad esempio, le seguenti due query sono logicamente equivalenti e possono essere utilizzate in modo intercambiabile.

Web

let results;

results = await execute(db.pipeline().collection("books")
  .where(field("rating").equal(5))
  .where(field("published").lessThan(1900))
);

results = await execute(db.pipeline().collection("books")
  .where(and(field("rating").equal(5), field("published").lessThan(1900)))
);
Swift
var results: Pipeline.Snapshot

results = try await db.pipeline().collection("books")
  .where(Field("rating").equal(5))
  .where(Field("published").lessThan(1900))
  .execute()

results = try await db.pipeline().collection("books")
  .where(Field("rating").equal(5) && Field("published").lessThan(1900))
  .execute()

Kotlin

var results: Task<Pipeline.Snapshot>

results = db.pipeline().collection("books")
    .where(field("rating").equal(5))
    .where(field("published").lessThan(1900))
    .execute()

results = db.pipeline().collection("books")
    .where(Expression.and(field("rating").equal(5),
      field("published").lessThan(1900)))
    .execute()

Java

Task<Pipeline.Snapshot> results;

results = db.pipeline().collection("books")
    .where(field("rating").equal(5))
    .where(field("published").lessThan(1900))
    .execute();

results = db.pipeline().collection("books")
    .where(Expression.and(
        field("rating").equal(5),
        field("published").lessThan(1900)
    ))
    .execute();
Python
from google.cloud.firestore_v1.pipeline_expressions import And, Field

results = (
    client.pipeline()
    .collection("books")
    .where(Field.of("rating").equal(5))
    .where(Field.of("published").less_than(1900))
    .execute()
)

results = (
    client.pipeline()
    .collection("books")
    .where(And(Field.of("rating").equal(5), Field.of("published").less_than(1900)))
    .execute()
)

Selezionare / aggiungere e rimuovere campi

select(...), add_fields(...) e remove_fields(...) ti consentono di modificare i campi restituiti da una fase precedente. Queste tre sono generalmente chiamate fasi di stile di proiezione.

select(...) e add_fields(...) ti consentono di specificare il risultato di un'espressione in un nome di campo fornito dall'utente. select(...) restituirà solo i documenti con i nomi dei campi specificati, mentre add_fields(...) estende lo schema della fase precedente (sovrascrivendo potenzialmente i valori con nomi di campi identici).

remove_fields(...) consente di specificare un insieme di campi da rimuovere dalla fase precedente. La specifica di nomi di campo che non esistono non ha effetto.

Consulta la sezione Limita i campi da restituire di seguito, ma in generale l'utilizzo di una fase di questo tipo per limitare il risultato ai soli campi necessari nel client è utile per ridurre i costi e la latenza per la maggior parte delle query.

Aggregazione / Valori univoci

La fase aggregate(...) consente di eseguire una serie di aggregazioni sui documenti di input. Per impostazione predefinita, tutti i documenti vengono aggregati, ma è possibile fornire un argomento grouping facoltativo, che consente di aggregare i documenti di input in bucket diversi.

Web

const results = await execute(db.pipeline()
  .collection("books")
  .aggregate(
    field("rating").average().as("avg_rating")
  )
  .distinct(field("genre"))
);
Swift
let results = try await db.pipeline()
  .collection("books")
  .aggregate([
    Field("rating").average().as("avg_rating")
  ], groups: [
    Field("genre")
  ])
  .execute()

Kotlin

val results = db.pipeline()
    .collection("books")
    .aggregate(
        AggregateStage
            .withAccumulators(AggregateFunction.average("rating").alias("avg_rating"))
            .withGroups(field("genre"))
    )
    .execute()

Java

Task<Pipeline.Snapshot> results = db.pipeline()
    .collection("books")
    .aggregate(AggregateStage
        .withAccumulators(
            AggregateFunction.average("rating").alias("avg_rating"))
        .withGroups(field("genre")))
    .execute();
Python
from google.cloud.firestore_v1.pipeline_expressions import Field

results = (
    client.pipeline()
    .collection("books")
    .aggregate(
        Field.of("rating").average().as_("avg_rating"), groups=[Field.of("genre")]
    )
    .execute()
)

Quando groupings non è specificato, questa fase produce un solo documento, altrimenti viene generato un documento per ogni combinazione univoca di valori groupings.

La fase distinct(...) è un operatore di aggregazione semplificato che consente di generare solo groupings unici senza accumulatori. Si comporta in modo identico a quello di aggregate(...) per tutti gli altri aspetti. L'esempio seguente mostra:

Web

const results = await execute(db.pipeline()
  .collection("books")
  .distinct(
    field("author").toUpper().as("author"),
    field("genre")
  )
);
Swift
let results = try await db.pipeline()
  .collection("books")
  .distinct([
    Field("author").toUpper().as("author"),
    Field("genre")
  ])
  .execute()

Kotlin

val results = db.pipeline()
    .collection("books")
    .distinct(
        field("author").toUpper().alias("author"),
        field("genre")
    )
    .execute()

Java

Task<Pipeline.Snapshot> results = db.pipeline()
    .collection("books")
    .distinct(
        field("author").toUpper().alias("author"),
        field("genre")
    )
    .execute();
Python
from google.cloud.firestore_v1.pipeline_expressions import Field

results = (
    client.pipeline()
    .collection("books")
    .distinct(Field.of("author").to_upper().as_("author"), "genre")
    .execute()
)

Functions

Le funzioni sono un componente di base per la creazione di espressioni e query complesse. Per un elenco completo di funzioni con esempi, consulta il riferimento alle funzioni. Ti ricordiamo brevemente la struttura di una query tipica:

Esempio che mostra fasi e funzioni in una query

Molte fasi accettano espressioni che contengono una o più funzioni. L'utilizzo più comune delle funzioni si trova nelle fasi where(...) e select(...). Esistono due tipi principali di funzioni con cui dovresti avere familiarità:

Web

let results;

// Type 1: Scalar (for use in non-aggregation stages)
// Example: Return the min store price for each book.
results = await execute(db.pipeline().collection("books")
  .select(field("current").logicalMinimum(field("updated")).as("price_min"))
);

// Type 2: Aggregation (for use in aggregate stages)
// Example: Return the min price of all books.
results = await execute(db.pipeline().collection("books")
  .aggregate(field("price").minimum().as("min_price"))
);
Swift
var results: Pipeline.Snapshot

// Type 1: Scalar (for use in non-aggregation stages)
// Example: Return the min store price for each book.
results = try await db.pipeline().collection("books")
  .select([
    Field("current").logicalMinimum(["updated"]).as("price_min")
  ])
  .execute()

// Type 2: Aggregation (for use in aggregate stages)
// Example: Return the min price of all books.
results = try await db.pipeline().collection("books")
  .aggregate([Field("price").minimum().as("min_price")])
  .execute()

Kotlin

var results: Task<Pipeline.Snapshot>

// Type 1: Scalar (for use in non-aggregation stages)
// Example: Return the min store price for each book.
results = db.pipeline().collection("books")
    .select(
        field("current").logicalMinimum("updated").alias("price_min")
    )
    .execute()

// Type 2: Aggregation (for use in aggregate stages)
// Example: Return the min price of all books.
results = db.pipeline().collection("books")
    .aggregate(AggregateFunction.minimum("price").alias("min_price"))
    .execute()

Java

Task<Pipeline.Snapshot> results;

// Type 1: Scalar (for use in non-aggregation stages)
// Example: Return the min store price for each book.
results = db.pipeline().collection("books")
    .select(
        field("current").logicalMinimum("updated").alias("price_min")
    )
    .execute();

// Type 2: Aggregation (for use in aggregate stages)
// Example: Return the min price of all books.
results = db.pipeline().collection("books")
    .aggregate(AggregateFunction.minimum("price").alias("min_price"))
    .execute();
Python
from google.cloud.firestore_v1.pipeline_expressions import Field

# Type 1: Scalar (for use in non-aggregation stages)
# Example: Return the min store price for each book.
results = (
    client.pipeline()
    .collection("books")
    .select(
        Field.of("current").logical_minimum(Field.of("updated")).as_("price_min")
    )
    .execute()
)

# Type 2: Aggregation (for use in aggregate stages)
# Example: Return the min price of all books.
results = (
    client.pipeline()
    .collection("books")
    .aggregate(Field.of("price").minimum().as_("min_price"))
    .execute()
)

Limiti

Per la maggior parte, la versione Enterprise non impone limiti alla forma della query. In altre parole, non sei limitato a un numero ridotto di valori in una query IN o OR. Esistono invece due limiti principali di cui devi essere a conoscenza:

  • Scadenza: 60 secondi (identica alla versione Standard).
  • Utilizzo della memoria:limite di 128 MiB per la quantità di dati materializzati durante l'esecuzione della query.

Errori

Potresti riscontrare query non riuscite per diversi motivi. Ecco un link agli errori comuni e all'azione associata che puoi intraprendere:

Codice di errore Azione
DEADLINE_EXCEEDED La query che stai eseguendo supera il limite di 60 secondi e richiede un'ulteriore ottimizzazione. Per suggerimenti, consulta la sezione Rendimento. Se non riesci a individuare la causa principale del problema, contatta il team.
RESOURCE_EXHAUSTED La query che stai eseguendo supera i limiti di memoria e richiede un'ulteriore ottimizzazione. Per suggerimenti, consulta la sezione Rendimento. Se non riesci a individuare la causa principale del problema, contatta il team.
INTERNAL Contatta il team per ricevere assistenza.

Prestazioni

I database della versione Enterprise non richiedono la presenza di un indice. Ciò significa che una query può presentare una latenza maggiore rispetto alle query esistenti, che altrimenti non sarebbero riuscite immediatamente a causa di un errore di indice mancante FAILED_PRECONDITION. Per migliorare il rendimento delle operazioni della pipeline, puoi seguire alcuni passaggi.

Crea indici

Indice utilizzato

Query Explain consente di identificare se la query viene pubblicata da un indice o se viene eseguita un'operazione meno efficiente, come una scansione della tabella. Se la query non viene servita completamente da un indice, puoi crearne uno seguendo le istruzioni.

Creazione di indici

Per creare gli indici, puoi seguire la documentazione esistente sulla gestione degli indici. Prima di creare un indice, acquisisci familiarità con le best practice generali per gli indici in Cloud Firestore. Per assicurarti che la query possa utilizzare gli indici, segui le best practice per creare indici con i campi nel seguente ordine:

  1. Tutti i campi che verranno utilizzati nei filtri di uguaglianza (in qualsiasi ordine)
  2. Tutti i campi in base ai quali verrà eseguito l'ordinamento (nello stesso ordine)
  3. Campi che verranno utilizzati nei filtri di intervallo o di disuguaglianza in ordine decrescente di selettività del vincolo di query

Ad esempio, per la seguente query

Web

const results = await execute(db.pipeline()
  .collection("books")
  .where(field("published").lessThan(1900))
  .where(field("genre").equal("Science Fiction"))
  .where(field("rating").greaterThan(4.3))
  .sort(field("published").descending())
);
Swift
let results = try await db.pipeline()
  .collection("books")
  .where(Field("published").lessThan(1900))
  .where(Field("genre").equal("Science Fiction"))
  .where(Field("rating").greaterThan(4.3))
  .sort([Field("published").descending()])
  .execute()

Kotlin

val results = db.pipeline()
    .collection("books")
    .where(field("published").lessThan(1900))
    .where(field("genre").equal("Science Fiction"))
    .where(field("rating").greaterThan(4.3))
    .sort(field("published").descending())
    .execute()

Java

Task<Pipeline.Snapshot> results = db.pipeline()
    .collection("books")
    .where(field("published").lessThan(1900))
    .where(field("genre").equal("Science Fiction"))
    .where(field("rating").greaterThan(4.3))
    .sort(field("published").descending())
    .execute();
Python
from google.cloud.firestore_v1.pipeline_expressions import Field

results = (
    client.pipeline()
    .collection("books")
    .where(Field.of("published").less_than(1900))
    .where(Field.of("genre").equal("Science Fiction"))
    .where(Field.of("rating").greater_than(4.3))
    .sort(Field.of("published").descending())
    .execute()
)

L'indice consigliato è un indice dell'ambito della raccolta su books per (genre [...], published DESC, avg_rating DESC).

Densità dell'indice

Cloud Firestore supporta gli indici sparsi e non sparsi. Per ulteriori informazioni, consulta Densità dell'indice.

Query coperte + indici secondari

Cloud Firestore può saltare il recupero dell'intero documento e restituire solo i risultati dell'indice se tutti i campi restituiti sono presenti in un indice secondario. Ciò in genere comporta un miglioramento significativo della latenza (e dei costi). Utilizzando la query di esempio riportata di seguito:

Web

const results = await execute(db.pipeline()
  .collection("books")
  .where(field("category").like("%fantasy%"))
  .where(field("title").exists())
  .where(field("author").exists())
  .select(field("title"), field("author"))
);
Swift
let results = try await db.pipeline()
  .collection("books")
  .where(Field("category").like("%fantasy%"))
  .where(Field("title").exists())
  .where(Field("author").exists())
  .select([Field("title"), Field("author")])
  .execute()

Kotlin

val results = db.pipeline()
    .collection("books")
    .where(field("category").like("%fantasy%"))
    .where(field("title").exists())
    .where(field("author").exists())
    .select(field("title"), field("author"))
    .execute()

Java

Task<Pipeline.Snapshot> results = db.pipeline()
    .collection("books")
    .where(field("category").like("%fantasy%"))
    .where(field("title").exists())
    .where(field("author").exists())
    .select(field("title"), field("author"))
    .execute();
Python
from google.cloud.firestore_v1.pipeline_expressions import Field

results = (
    client.pipeline()
    .collection("books")
    .where(Field.of("category").like("%fantasy%"))
    .where(Field.of("title").exists())
    .where(Field.of("author").exists())
    .select("title", "author")
    .execute()
)

Se il database ha già un indice dell'ambito della raccolta su books per (category [...], title [...], author [...]), può evitare di recuperare qualsiasi elemento dai documenti principali. In questo caso, l'ordine nell'indice non ha importanza, [...] viene utilizzato per indicarlo.

Limitare i campi da restituire

Per impostazione predefinita, una query Cloud Firestore restituisce tutti i campi di un documento, analogamente a un'istruzione SELECT * nei sistemi relazionali. Se invece la tua applicazione ha bisogno solo di un sottoinsieme dei campi, le fasi select(...) o restrict(...) possono essere utilizzate per eseguire il push di questo filtro lato server. In questo modo si riducono sia le dimensioni della risposta (diminuendo il costo del traffico in uscita della rete) sia la latenza.

Strumenti per la risoluzione dei problemi

Spiegazione query

Query Explain ti consente di visualizzare le metriche di esecuzione e i dettagli sugli indici utilizzati.

Metriche

Le operazioni della pipeline sono completamente integrate con le metriche Cloud Firestore esistenti.

Problemi noti / limitazioni

Indici specializzati

Le operazioni della pipeline non supportano ancora i tipi di indice array-contains e vector esistenti. Anziché rifiutare semplicemente queste query, Cloud Firestore tenterà di utilizzare altri indici ascending e descending esistenti. Si prevede che durante l'anteprima privata le operazioni della pipeline con espressioni array_contains o find_nearest saranno più lente rispetto alle loro equivalenti esistenti.

Impaginazione

Il supporto per la paginazione semplice di un insieme di risultati non è supportato durante l'anteprima privata. Questo problema può essere risolto concatenando le fasi equivalenti where(...) e sort(...) come mostrato di seguito.

Web

// Existing pagination via `startAt()`
const q =
  query(collection(db, "cities"), orderBy("population"), startAt(1000000));

// Private preview workaround using pipelines
const pageSize = 2;
const pipeline = db.pipeline()
  .collection("cities")
  .select("name", "population", "__name__")
  .sort(field("population").descending(), field("__name__").ascending());

// Page 1 results
let snapshot = await execute(pipeline.limit(pageSize));

// End of page marker
const lastDoc = snapshot.results[snapshot.results.length - 1];

// Page 2 results
snapshot = await execute(
  pipeline
    .where(
      or(
        and(
          field("population").equal(lastDoc.get("population")),
          field("__name__").greaterThan(lastDoc.ref)
        ),
        field("population").lessThan(lastDoc.get("population"))
      )
    )
    .limit(pageSize)
);
Swift
// Existing pagination via `start(at:)`
let query = db.collection("cities").order(by: "population").start(at: [1000000])

// Private preview workaround using pipelines
let pipeline = db.pipeline()
  .collection("cities")
  .where(Field("population").greaterThanOrEqual(1000000))
  .sort([Field("population").descending()])

Kotlin

// Existing pagination via `startAt()`
val query = db.collection("cities").orderBy("population").startAt(1000000)

// Private preview workaround using pipelines
val pipeline = db.pipeline()
    .collection("cities")
    .where(field("population").greaterThanOrEqual(1000000))
    .sort(field("population").descending())

Java

// Existing pagination via `startAt()`
Query query = db.collection("cities").orderBy("population").startAt(1000000);

// Private preview workaround using pipelines
Pipeline pipeline = db.pipeline()
    .collection("cities")
    .where(field("population").greaterThanOrEqual(1000000))
    .sort(field("population").descending());
Python
from google.cloud.firestore_v1.pipeline_expressions import Field

# Existing pagination via `start_at()`
query = (
    client.collection("cities")
    .order_by("population")
    .start_at({"population": 1_000_000})
)

# Private preview workaround using pipelines
pipeline = (
    client.pipeline()
    .collection("cities")
    .where(Field.of("population").greater_than_or_equal(1_000_000))
    .sort(Field.of("population").descending())
)

Assistenza per l'emulatore

L'emulatore non supporta le operazioni della pipeline.

Supporto in tempo reale e offline

Le operazioni della pipeline non dispongono ancora di funzionalità in tempo reale e offline.

Passaggi successivi