クエリカーソルを使用したデータのページ設定

Cloud Firestore のクエリカーソルを使用すると、クエリで返されたデータを、クエリで定義したパラメータに従ってバッチに分割できます。

クエリカーソルは、クエリの開始点と終了点を定義することで、以下のことを行えます。

  • データのサブセットを返す。
  • クエリ結果にページを設定する。

ただし、クエリに特定の範囲を定義するには、単純なクエリで説明されている where() メソッドを使用する必要があります。

クエリへのシンプルなカーソルの追加

startAt() または startAfter() メソッドを使用して、クエリの開始点を定義します。startAt() メソッドは開始点を含み、startAfter() メソッドは開始点を除外します。

たとえば、クエリで startAt(A) を使用すると、アルファベット全体が返されます。代わりに startAfter(A) を使用すると、B-Z が返されます。

ウェブ
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]);

同様に、endAt() メソッドまたは endBefore() メソッドを使用して、クエリ結果の終了点を定義します。

ウェブ
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]);

ドキュメント スナップショットを使用したクエリカーソルの定義

また、ドキュメント スナップショットをクエリカーソルの開始点または終了点としてカーソル句に渡すこともできます。ドキュメント スナップショットの値は、クエリカーソルの値として使用されます。

たとえば、都市と人口のデータセットの「サンフランシスコ」ドキュメントのスナップショットを作成し、それを人口のクエリカーソルの開始点として使用します。このクエリは、ドキュメント スナップショットで定義されているように、サンフランシスコよりも人口が多いか、同程度のすべての都市を返します。

ウェブ
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

クエリのページ設定

クエリカーソルを limit() メソッドと組み合わせて、クエリにページを設定します。たとえば、バッチ内の最後のドキュメントを、次のバッチのカーソルの開始点として使用します。

ウェブ
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();

複数のカーソル条件の設定

カーソルの開始点または終了点の粒度を上げるには、カーソル句に複数の条件を指定します。これはデータセットに、カーソル句の最初の条件で複数の結果を返すフィールドが含まれる場合に特に便利です。複数の条件を使用して開始点または終了点を詳細に指定し、あいまいさを減らします。

たとえば、米国の「スプリングフィールド」という名前のすべての都市を含むデータセットには、「スプリングフィールド」で開始するクエリセットの複数の開始点が存在します。

都市
名前
スプリングフィールド マサチューセッツ
スプリングフィールド ミズーリ
スプリングフィールド ウィスコンシン

特定のスプリングフィールドで開始するには、カーソル句の二次条件として州を追加することができます。

ウェブ
// 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']);

フィードバックを送信...

ご不明な点がありましたら、Google のサポートページをご覧ください。