Paginar datos con cursores de consulta

Con los cursores de consulta de Cloud Firestore, puedes dividir los datos de una consulta en lotes, de acuerdo con los parámetros que defines en la consulta.

Los cursores de consulta definen los puntos de inicio y finalización para una consulta y te permiten hacer lo siguiente:

  • mostrar un subconjunto de los datos
  • paginar los resultados de consultas

Sin embargo, para definir un rango específico en una consulta, debes usar el método where() que se describe en la sección sobre consultas simples.

Agrega un cursor simple a una consulta

Usa los métodos startAt() o startAfter() para definir el punto de inicio de una consulta. El método startAt() incluye el punto de inicio, mientras que el método startAfter() lo excluye.

Por ejemplo, si usas startAt(A) en una consulta, el resultado incluye todo el alfabeto. En cambio, si usas startAfter(A), el resultado es el rango B-Z.

Web
citiesRef.orderBy("population").startAt(1000000)
Swift
// Get all cities with population over one million, ordered by population.
db.collection("cities")
    .order(by: "population")
    .start(at: [1000000])
Objective-C
// Get all cities with population over one million, ordered by population.
[[[db collectionWithPath:@"cities"]
    queryOrderedByField:@"population"]
    queryStartingAtValues:@[ @1000000 ]];
  
Android
// Get all cities with a population >= 1,000,000, ordered by population,
db.collection("cities")
        .orderBy("population")
        .startAt(1000000);
Java
Query query = cities.orderBy("population").startAt(4921000L);
Python
// Python snippet missing
Node.js
var startAt = db.collection('cities')
    .orderBy('population')
    .startAt(1000000);
Go
query := client.Collection("cities").OrderBy("population", firestore.Asc).StartAt(1000000)
PHP
$query = $citiesRef
    ->orderBy('population')
    ->startAt([1000000]);

De manera similar, usa los métodos endAt() o endBefore() para definir el punto de finalización de los resultados de la consulta.

Web
citiesRef.orderBy("population").endAt(1000000)
Swift
// Get all cities with population less than one million, ordered by population.
db.collection("cities")
    .order(by: "population")
    .end(at: [1000000])
Objective-C
// Get all cities with population less than one million, ordered by population.
[[[db collectionWithPath:@"cities"]
    queryOrderedByField:@"population"]
    queryEndingAtValues:@[ @1000000 ]];
  
Android
// Get all cities with a population <= 1,000,000, ordered by population,
db.collection("cities")
        .orderBy("population")
        .endAt(1000000);
Java
Query query = cities.orderBy("population").endAt(4921000L);
Python
cities_ref = db.collection(u'cities')
query_end_at = cities_ref.order_by(u'population').end_at({
    u'population': 1000000
})
Node.js
var endAt = db.collection('cities')
    .orderBy('population')
    .endAt(1000000);
Go
query := client.Collection("cities").OrderBy("population", firestore.Asc).EndAt(1000000)
PHP
$query = $citiesRef
    ->orderBy('population')
    ->endAt([1000000]);

Usa la instantánea de un documento para definir el cursor de consulta

También puedes pasar la instantánea de un documento a la cláusula del cursor como punto de inicio o de finalización del cursor de consulta. Los valores de la instantánea del documento sirven como valores del cursor de consulta.

Por ejemplo, toma una instantánea de un documento "San Francisco" en el conjunto de datos de ciudades y poblaciones. A continuación, usa la instantánea del documento como el punto de inicio para el cursor de consulta de población. El resultado de la consulta incluirá todas las ciudades con una población mayor o igual que la de San Francisco, como se define en la instantánea del documento.

Web
var citiesRef = db.collection("cities");

return citiesRef.doc("SF").get().then(function(doc) {
    // Get all cities with a population bigger than San Francisco
    var biggerThanSf = citiesRef
        .orderBy("population")
        .startAt(doc);

    // ...
});
Swift
db.collection("cities")
    .document("SF")
    .addSnapshotListener { (document, error) in
        guard let document = document else {
            print("Error retreving cities: \(error.debugDescription)")
            return
        }

        // Get all cities with a population greater than or equal to San Francisco.
        let sfSizeOrBigger = db.collection("cities")
            .order(by: "population")
            .start(atDocument: document)
}
Objective-C
[[[db collectionWithPath:@"cities"] documentWithPath:@"SF"]
    addSnapshotListener:^(FIRDocumentSnapshot *snapshot, NSError *error) {
      if (snapshot == nil) {
        NSLog(@"Error retreiving cities: %@", error);
        return;
      }
      // Get all cities with a population greater than or equal to San Francisco.
      FIRQuery *sfSizeOrBigger = [[[db collectionWithPath:@"cities"]
          queryOrderedByField:@"population"]
          queryStartingAtDocument:snapshot];
    }];
  
Android
// Get the data for "San Francisco"
db.collection("cities").document("SF")
        .get()
        .addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
            @Override
            public void onSuccess(DocumentSnapshot documentSnapshot) {
                // Get all cities with a population bigger than San Francisco.
                Query biggerThanSf = db.collection("cities")
                        .orderBy("population")
                        .startAt(documentSnapshot);

                // ...
            }
        });
Java
// Not supported in Java SDK
Python
# Not supported in Python SDK
Node.js
// Not supported in Node.js SDK
Go
// Not supported in Go SDK
PHP
// Not supported in PHP SDK

Pagina una consulta

Para paginar consultas, combina cursores de consulta con el método limit(). Por ejemplo, usa el último documento de un lote como el inicio de un cursor para el siguiente lote.

Web
var first = db.collection("cities")
        .orderBy("population")
        .limit(25);

return first.get().then(function (documentSnapshots) {
  // Get the last visible document
  var lastVisible = documentSnapshots.docs[documentSnapshots.docs.length-1];
  console.log("last", lastVisible);

  // Construct a new query starting at this document,
  // get the next 25 cities.
  var next = db.collection("cities")
          .orderBy("population")
          .startAfter(lastVisible)
          .limit(25);
});
Swift
// Construct query for first 25 cities, ordered by population
let first = db.collection("cities")
    .order(by: "population")
    .limit(to: 25)

first.addSnapshotListener { (snapshot, error) in
    guard let snapshot = snapshot else {
        print("Error retreving cities: \(error.debugDescription)")
        return
    }

    guard let lastSnapshot = snapshot.documents.last else {
        // The collection is empty.
        return
    }

    // Construct a new query starting after this document,
    // retrieving the next 25 cities.
    let next = db.collection("cities")
        .order(by: "population")
        .start(afterDocument: lastSnapshot)

    // Use the query for pagination.
    // ...
}
Objective-C
FIRQuery *first = [[[db collectionWithPath:@"cities"]
    queryOrderedByField:@"population"]
    queryLimitedTo:25];
[first addSnapshotListener:^(FIRQuerySnapshot *snapshot, NSError *error) {
  if (snapshot == nil) {
    NSLog(@"Error retreiving cities: %@", error);
    return;
  }
  if (snapshot.documents.count == 0) { return; }
  FIRDocumentSnapshot *lastSnapshot = snapshot.documents.lastObject;

  // Construct a new query starting after this document,
  // retreiving the next 25 cities.
  FIRQuery *next = [[[db collectionWithPath:@"cities"]
      queryOrderedByField:@"population"]
      queryStartingAfterDocument:lastSnapshot];
  // Use the query for pagination.
  // ...
}];
  
Android
// Construct query for first 25 cities, ordered by population
Query first = db.collection("cities")
        .orderBy("population")
        .limit(25);

first.get()
    .addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
        @Override
        public void onSuccess(QuerySnapshot documentSnapshots) {
            // ...

            // Get the last visible document
            DocumentSnapshot lastVisible = documentSnapshots.getDocuments()
                    .get(documentSnapshots.size() -1);

            // Construct a new query starting at this document,
            // get the next 25 cities.
            Query next = db.collection("cities")
                    .orderBy("population")
                    .startAfter(lastVisible)
                    .limit(25);

            // Use the query for pagination
            // ...
        }
    });
Java
// Not supported in Java SDK
Python
cities_ref = db.collection(u'cities')
first_query = cities_ref.order_by(u'population').limit(3)

# Get the last document from the results
docs = first_query.get()
last_doc = list(docs)[-1]

# Construct a new query starting at this document
# Note: this will not have the desired effect if
# multiple cities have the exact same population value
last_pop = last_doc.to_dict()[u'population']

next_query = (
    cities_ref
    .order_by(u'population')
    .start_after({
        u'population': last_pop
    })
    .limit(3)
)
# Use the query for pagination
# ...
Node.js
var first = db.collection('cities')
    .orderBy('population')
    .limit(3);

var paginate = first.get()
    .then((snapshot) => {
        // ...

        // Get the last document
        var last = snapshot.docs[snapshot.docs.length - 1];

        // Construct a new query starting at this document.
        // Note: this will not have the desired effect if multiple
        // cities have the exact same population value.
        var next = db.collection('cities')
            .orderBy('population')
            .startAfter(last.data().population)
            .limit(3);

        // Use the query for pagination
        // ...
    });
Go
cities := client.Collection("cities")

// Get the first 25 cities, ordered by population.
firstPage := cities.OrderBy("population", firestore.Asc).Limit(25).Documents(ctx)
docs, err := firstPage.GetAll()
if err != nil {
	return err
}

// Get the last document.
lastDoc := docs[len(docs)-1]

// Construct a new query to get the next 25 cities.
secondPage := cities.OrderBy("population", firestore.Asc).
	StartAfter(lastDoc.Data()["population"]).
	Limit(25)

// ...
PHP
$citiesRef = $db->collection('cities');
$firstQuery = $citiesRef->orderBy('population')->limit(3);

# Get the last document from the results
$documents = $firstQuery->documents();
$lastPopulation = 0;
foreach ($documents as $document) {
    $lastPopulation = $document['population'];
}

# Construct a new query starting at this document
# Note: this will not have the desired effect if multiple cities have the exact same population value
$nextQuery = $citiesRef->orderBy('population')->startAfter([$lastPopulation]);
$snapshot = $nextQuery->documents();

Configura condiciones múltiples para el cursor

Para agregar más detalle a los puntos de inicio y finalización del cursor, puedes especificar condiciones múltiples en la cláusula del cursor. Esto resulta particularmente útil si el conjunto de datos incluye campos en los que la primera condición de una cláusula de cursor mostraría varios resultados. Usa varias condiciones para especificar con mayor detalle los puntos de inicio y finalización y evitar ambigüedades.

Por ejemplo, en un conjunto de datos que contiene todas las ciudades llamadas "Springfield" en Estados Unidos, habría varios puntos de inicio para que un conjunto de consultas se inicie en "Springfield":

Ciudades
Nombre Estado
Springfield Massachusetts
Springfield Missouri
Springfield Wisconsin

Para comenzar en una Springfield específica, deberías agregar el estado como una condición secundaria en la cláusula del cursor.

Web
// Will return all Springfields
db.collection("cities")
   .orderBy("name")
   .orderBy("state")
   .startAt("Springfield")

// Will return "Springfield, Missouri" and "Springfield, Wisconsin"
db.collection("cities")
   .orderBy("name")
   .orderBy("state")
   .startAt("Springfield", "Missouri")
Swift
// Will return all Springfields
db.collection("cities")
    .order(by: "name")
    .order(by: "state")
    .start(at: ["Springfield"])

// Will return "Springfield, Missouri" and "Springfield, Wisconsin"
db.collection("cities")
    .order(by: "name")
    .order(by: "state")
    .start(at: ["Springfield", "Missouri"])
Objective-C
// Will return all Springfields
[[[[db collectionWithPath:@"cities"]
    queryOrderedByField:@"name"]
    queryOrderedByField:@"state"]
    queryStartingAtValues:@[ @"Springfield" ]];
// Will return "Springfield, Missouri" and "Springfield, Wisconsin"
[[[[db collectionWithPath:@"cities"]
   queryOrderedByField:@"name"]
   queryOrderedByField:@"state"]
   queryStartingAtValues:@[ @"Springfield", @"Missouri" ]];
  
Android
// Will return all Springfields
db.collection("cities")
        .orderBy("name")
        .orderBy("state")
        .startAt("Springfield");

// Will return "Springfield, Missouri" and "Springfield, Wisconsin"
db.collection("cities")
        .orderBy("name")
        .orderBy("state")
        .startAt("Springfield", "Missouri");
Java
// Will return all Springfields
Query query1 = db.collection("cities")
    .orderBy("name")
    .orderBy("state")
    .startAt("Springfield");

// Will return "Springfield, Missouri" and "Springfield, Wisconsin"
Query query2 = db.collection("cities")
    .orderBy("name")
    .orderBy("state")
    .startAt("Springfield", "Missouri");
Python
start_at_name = (
    db.collection(u'cities')
    .order_by(u'name')
    .order_by(u'state')
    .start_at({
        u'name': u'Springfield'
    })
)

start_at_name_and_state = (
    db.collection(u'cities')
    .order_by(u'name')
    .order_by(u'state')
    .start_at({
        u'name': u'Springfield',
        u'state': u'Missouri'
    })
)
Node.js
// Will return all Springfields
var startAtName = db.collection('cities')
    .orderBy('name')
    .orderBy('state')
    .startAt('Springfield');
// Will return 'Springfield, Missouri' and 'Springfield, Wisconsin'
var startAtNameAndState = db.collection('cities')
    .orderBy('name')
    .orderBy('state')
    .startAt('Springfield', 'Missouri');
Go
// Will return all Springfields.
client.Collection("cities").
	OrderBy("name", firestore.Asc).
	OrderBy("state", firestore.Asc).
	StartAt("Springfield")

// Will return Springfields where state comes after Wisconsin.
client.Collection("cities").
	OrderBy("name", firestore.Asc).
	OrderBy("state", firestore.Asc).
	StartAt("Springfield", "Wisconsin")
PHP
// Will return all Springfields
$query1 = $db
    ->collection('cities')
    ->orderBy('name')
    ->orderBy('state')
    ->startAt(['Springfield']);

// Will return "Springfield, Missouri" and "Springfield, Wisconsin"
$query2 = $db
    ->collection('cities')
    ->orderBy('name')
    ->orderBy('state')
    ->startAt(['Springfield', 'Missouri']);

Enviar comentarios sobre...

Si necesitas ayuda, visita nuestra página de asistencia.