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 ]];
Java
// Get all cities with a population >= 1,000,000, ordered by population, db.collection("cities") .orderBy("population") .startAt(1000000);
Kotlin+KTX
// 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
cities_ref = db.collection(u'cities') query_start_at = cities_ref.order_by(u'population').start_at({ u'population': 1000000 })
C++
// Get all cities with a population >= 1,000,000, ordered by population, db->Collection("cities") .OrderBy("population") .StartAt({FieldValue::Integer(1000000)});
Node.js
const startAtRes = await db.collection('cities') .orderBy('population') .startAt(1000000) .get();
Go
query := client.Collection("cities").OrderBy("population", firestore.Asc).StartAt(1000000)
PHP
$query = $citiesRef ->orderBy('population') ->startAt([1000000]);
Unity
Query query = citiesRef.OrderBy("Population").StartAt(1000000);
C#
Query query = citiesRef.OrderBy("Population").StartAt(1000000);
Ruby
query = cities_ref.order("population").start_at(1_000_000)
同様に、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 ]];
Java
// Get all cities with a population <= 1,000,000, ordered by population, db.collection("cities") .orderBy("population") .endAt(1000000);
Kotlin+KTX
// 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 })
C++
// Get all cities with a population <= 1,000,000, ordered by population, db->Collection("cities") .OrderBy("population") .EndAt({FieldValue::Integer(1000000)});
Node.js
const endAtRes = await db.collection('cities') .orderBy('population') .endAt(1000000) .get();
Go
query := client.Collection("cities").OrderBy("population", firestore.Asc).EndAt(1000000)
PHP
$query = $citiesRef ->orderBy('population') ->endAt([1000000]);
Unity
Query query = citiesRef.OrderBy("Population").EndAt(1000000);
C#
Query query = citiesRef.OrderBy("Population").EndAt(1000000);
Ruby
query = cities_ref.order("population").end_at(1_000_000)
ドキュメント スナップショットを使用したクエリカーソルの定義
また、ドキュメント スナップショットをクエリカーソルの開始点または終了点としてカーソル句に渡すこともできます。ドキュメント スナップショットの値は、クエリカーソルの値として使用されます。
たとえば、都市と人口のデータセットの「サンフランシスコ」ドキュメントのスナップショットを作成し、それを人口のクエリカーソルの開始点として使用します。このクエリは、ドキュメント スナップショットで定義されているように、サンフランシスコよりも人口が多いか、同程度のすべての都市を返します。
ウェブ
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]; }];
Java
// 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); // ... } });
Kotlin+KTX
// Get the data for "San Francisco" db.collection("cities").document("SF") .get() .addOnSuccessListener { documentSnapshot -> // Get all cities with a population bigger than San Francisco. val biggerThanSf = db.collection("cities") .orderBy("population") .startAt(documentSnapshot) // ... }
Java
// Fetch the snapshot with an API call, waiting for a maximum of 30 seconds for a result. ApiFuture<DocumentSnapshot> future = db.collection("cities").document("SF").get(); DocumentSnapshot snapshot = future.get(30, TimeUnit.SECONDS); // Construct the query Query query = db.collection("cities").orderBy("population").startAt(snapshot);
Python
doc_ref = db.collection(u'cities').document(u'SF') snapshot = doc_ref.get() start_at_snapshot = db.collection( u'cities').order_by(u'population').start_at(snapshot)
C++
db->Collection("cities").Document("SF").Get().OnCompletion( [db](const Future<DocumentSnapshot>& future) { if (future.error() == Error::kErrorOk) { const DocumentSnapshot& document_snapshot = *future.result(); Query bigger_than_sf = db->Collection("cities") .OrderBy("population") .StartAt({document_snapshot}); // ... } });
Node.js
const docRef = db.collection('cities').doc('SF'); const snapshot = await docRef.get(); const startAtSnapshot = db.collection('cities') .orderBy('population') .startAt(snapshot); await startAtSnapshot.limit(10).get();
Go
cities := client.Collection("cities") dsnap, err := cities.Doc("SF").Get(ctx) if err != nil { fmt.Println(err) } query := cities.OrderBy("population", firestore.Asc).StartAt(dsnap.Data()["population"]).Documents(ctx)
PHP
$citiesRef = $db->collection('cities'); $docRef = $citiesRef->document('SF'); $snapshot = $docRef->snapshot(); $query = $citiesRef ->orderBy('population') ->startAt($snapshot);
Unity
CollectionReference citiesRef = db.Collection("cities"); DocumentReference docRef = citiesRef.Document("SF"); docRef.GetSnapshotAsync().ContinueWith((snapshotTask) => { Query query = citiesRef.OrderBy("Population").StartAt(snapshotTask.Result); });
C#
CollectionReference citiesRef = db.Collection("cities"); DocumentReference docRef = citiesRef.Document("SF"); DocumentSnapshot snapshot = await docRef.GetSnapshotAsync(); Query query = citiesRef.OrderBy("Population").StartAt(snapshot);
Ruby
// Snippet not available
クエリのページ設定
クエリカーソルを 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. // ... }];
Java
// 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 // ... } });
Kotlin+KTX
// Construct query for first 25 cities, ordered by population val first = db.collection("cities") .orderBy("population") .limit(25) first.get() .addOnSuccessListener { documentSnapshots -> // ... // Get the last visible document val lastVisible = documentSnapshots.documents[documentSnapshots.size() - 1] // Construct a new query starting at this document, // get the next 25 cities. val next = db.collection("cities") .orderBy("population") .startAfter(lastVisible) .limit(25) // Use the query for pagination // ... }
Java
// Construct query for first 25 cities, ordered by population. CollectionReference cities = db.collection("cities"); Query firstPage = cities.orderBy("population").limit(25); // Wait for the results of the API call, waiting for a maximum of 30 seconds for a result. ApiFuture<QuerySnapshot> future = firstPage.get(); List<QueryDocumentSnapshot> docs = future.get(30, TimeUnit.SECONDS).getDocuments(); // Construct query for the next 25 cities. QueryDocumentSnapshot lastDoc = docs.get(docs.size() - 1); Query secondPage = cities.orderBy("population").startAfter(lastDoc).limit(25); future = secondPage.get(); docs = future.get(30, TimeUnit.SECONDS).getDocuments();
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.stream() 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 # ...
C++
// Construct query for first 25 cities, ordered by population Query first = db->Collection("cities").OrderBy("population").Limit(25); first.Get().OnCompletion([db](const Future<QuerySnapshot>& future) { if (future.error() != Error::kErrorOk) { // Handle error... return; } // Get the last visible document const QuerySnapshot& document_snapshots = *future.result(); std::vector<DocumentSnapshot> documents = document_snapshots.documents(); const DocumentSnapshot& last_visible = documents.back(); // Construct a new query starting at this document, // get the next 25 cities. Query next = db->Collection("cities") .OrderBy("population") .StartAfter(last_visible) .Limit(25); // Use the query for pagination // ... });
Node.js
const first = db.collection('cities') .orderBy('population') .limit(3); const snapshot = await first.get(); // Get the last document const 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. const 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();
Unity
CollectionReference citiesRef = db.Collection("cities"); Query firstQuery = citiesRef.OrderBy("Population").Limit(3); // Get the last document from the results firstQuery.GetSnapshotAsync().ContinueWith((querySnapshotTask) => { long lastPopulation = 0; foreach (DocumentSnapshot documentSnapshot in querySnapshotTask.Result.Documents) { lastPopulation = documentSnapshot.GetValue<long>("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 Query secondQuery = citiesRef.OrderBy("Population").StartAfter(lastPopulation); Task<QuerySnapshot> secondQuerySnapshot = secondQuery.GetSnapshotAsync();
C#
CollectionReference citiesRef = db.Collection("cities"); Query firstQuery = citiesRef.OrderBy("Population").Limit(3); // Get the last document from the results QuerySnapshot querySnapshot = await firstQuery.GetSnapshotAsync(); long lastPopulation = 0; foreach (DocumentSnapshot documentSnapshot in querySnapshot.Documents) { lastPopulation = documentSnapshot.GetValue<long>("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 Query secondQuery = citiesRef.OrderBy("Population").StartAfter(lastPopulation); QuerySnapshot secondQuerySnapshot = await secondQuery.GetSnapshotAsync();
Ruby
cities_ref = firestore.col collection_path first_query = cities_ref.order("population").limit(3) # Get the last document from the results. last_population = 0 first_query.get do |city| last_population = city.data[:population] end # 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. second_query = cities_ref.order("population").start_after(last_population) second_query.get do |city| puts "Document #{city.document_id} returned by paginated query cursor." end
複数のフィールドに基づいてカーソルを設定する
DocumentSnapshot ではなくフィールド値に基づいてカーソルを使用する場合は、フィールドを追加してカーソルの位置をより正確に指定できます。これは、データセットにカーソル フィールドの値がすべて同じ複数のドキュメントが含まれているため、カーソルの位置が不明確な場合に特に便利です。カーソルにフィールド値を追加して、開始点または終了点も指定し、あいまいさを減らすことができます。
たとえば、米国の「スプリングフィールド」という名前のすべての都市を含むデータセットには、「スプリングフィールド」で開始するクエリセットの複数の開始点が存在します。
都市 | |
---|---|
名前 | 州 |
スプリングフィールド | マサチューセッツ |
スプリングフィールド | ミズーリ |
スプリングフィールド | ウィスコンシン |
特定のスプリングフィールドで開始するには、カーソル句の二次条件として州を追加することができます。
ウェブ
// 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" ]];
Java
// 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");
Kotlin+KTX
// 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' }) )
C++
// This is not yet supported.
Node.js
// Will return all Springfields const startAtNameRes = await db.collection('cities') .orderBy('name') .orderBy('state') .startAt('Springfield') .get(); // Will return 'Springfield, Missouri' and 'Springfield, Wisconsin' const startAtNameAndStateRes = await db.collection('cities') .orderBy('name') .orderBy('state') .startAt('Springfield', 'Missouri') .get();
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']);
Unity
Query query1 = db.Collection("cities").OrderBy("Name").OrderBy("State").StartAt("Springfield"); Query query2 = db.Collection("cities").OrderBy("Name").OrderBy("State").StartAt("Springfield", "Missouri");
C#
Query query1 = db.Collection("cities").OrderBy("Name").OrderBy("State").StartAt("Springfield"); Query query2 = db.Collection("cities").OrderBy("Name").OrderBy("State").StartAt("Springfield", "Missouri");
Ruby
# Will return all Springfields query1 = firestore.col(collection_path).order("name").order("state").start_at("Springfield") # Will return "Springfield, Missouri" and "Springfield, Wisconsin" query2 = firestore.col(collection_path).order("name").order("state").start_at(["Springfield", "Missouri"])