ページネーションおよびブックマーク
ブックマークは、結果セットから結果の次ページをリリースするのに役立ちます。 ページ編集中に、さまざまな文書を効率的に反復処理します。
skip
/ limit
パターンを使用して結果セットを反復できますが、skip
の値が大きくなるにつれて徐々に遅くなります。
IBM Cloudantクエリと'IBM Cloudant検索はどちらも、結果セットから次のページの結果をアンロックするキーとしてブックマークを使用する。 この手法については、『 ブックマーク』という後のセクションで詳しく説明されています。 次の結果セットへのリクエストを形成するためにキー操作を必要としないので、管理がより複雑にならない。 最初の応答で受信したブックマークを 2 番目の要求に渡します。
では、大きな文書セットでの効率的なページ移動の方法を見てみましょう。
_all_docs
およびビューを使用したページング
もし'GET
や'POST
'$SERVICE_URL/$DATABASE/_all_docs
エンドポイントを使用してドキュメントを一括で取得した場合、'limit
や'skip
パラメータが表示されるかもしれません。 これらのパラメーターを使用することで、文書の数と、範囲内で開始点にしたいオフセットを定義することができます。 skip
/limit
パターンを使用して結果に対して反復処理を行うこともできますが、skip
の値が大きくなるにつれて徐々に処理速度が遅くなります。
_all_docs
エンドポイントとは
GET
と'POST
'$SERVICE_URL/$DATABASE/_all_docs
はIBM Cloudantデータベースのプライマリインデックス、つまり各ドキュメントの'_id
順番に保持するインデックスからデータをフェッチするために使用されます。 _all_docs
エンドポイントには多くのオプション・パラメーターがあり、それらのパラメーターによって、要求されるデータ範囲と、各文書の本文を返すかどうかが構成されます。
パラメーターが指定されていない場合、_all_docs
はデータベースのすべての文書をストリーミングし、文書の _id
および現行の _rev
トークンのみを返します。
curl -H "Authorization: Bearer $API_BEARER_TOKEN" -X GET "$SERVICE_URL/orders/_all_docs"
import com.ibm.cloud.cloudant.v1.Cloudant;
import com.ibm.cloud.cloudant.v1.model.AllDocsResult;
import com.ibm.cloud.cloudant.v1.model.PostAllDocsOptions;
Cloudant service = Cloudant.newInstance();
PostAllDocsOptions docsOptions =
new PostAllDocsOptions.Builder()
.db("orders")
.build();
AllDocsResult response =
service.postAllDocs(docsOptions).execute().getResult();
System.out.println(response);
const { CloudantV1 } = require('@ibm-cloud/cloudant');
const service = CloudantV1.newInstance({});
service.postAllDocs({
db: 'orders',
}).then(response => {
console.log(response.result);
});
from ibmcloudant.cloudant_v1 import CloudantV1
service = CloudantV1.new_instance()
response = service.post_all_docs(
db='orders',
).get_result()
print(response)
postAllDocsOptions := service.NewPostAllDocsOptions(
"orders",
)
allDocsResult, response, err := service.PostAllDocs(postAllDocsOptions)
if err != nil {
panic(err)
}
b, _ := json.MarshalIndent(allDocsResult, "", " ")
fmt.Println(string(b))
前の Go の例では、以下のインポート・ブロックが必要です。
import (
"encoding/json"
"fmt"
"github.com/IBM/cloudant-go-sdk/cloudantv1"
)
すべての Go の例では、service
オブジェクトを初期化する必要があります。 詳しくは、API 資料の認証セクションで例を参照してください。
{
"total_rows": 11,
"offset": 0,
"rows": [
{
"id": "4eee973603bf77f30b1f880ed83df76a",
"key": "4eee973603bf77f30b1f880ed83df76a",
"value": {
"rev": "1-3b5e6b73e57745787ad5627fe8f268c1"
}
},
{
"id": "4eee973603bf77f30b1f880ed83f469a",
"key": "4eee973603bf77f30b1f880ed83f469a",
"value": {
"rev": "1-967a00dff5e02add41819138abb3284d"
}
}
...
include_docs=true
が指定されている場合、さらに doc
属性が結果セットの各 "row" に追加され、そこに文書の本文が含まれます。
limit
パラメーター、startkey
パラメーター、および endkey
パラメーター
適度なサイズのページにして _all_docs
からのデータにアクセスするには、limit
パラメーターを指定して、返す文書の数を IBM Cloudant に指示する必要があります。
# get me 5 documents
GET $SERVICE_URL/$DATABASE/_all_docs?limit=5 HTTP/1.1
_id
または startkey
に 1 つ以上の値を指定することによって、文書 endkey
の範囲を制限することもできます。
# get me 5 documents from _id order00057 onwards
curl -H "Authorization: Bearer $API_BEARER_TOKEN" "$SERVICE_URL/orders/_all_docs?limit=5&startkey=\"order00057\"" \
# get me 5 documents between _id order00057 --> order00077
curl -H "Authorization: Bearer $API_BEARER_TOKEN" "$SERVICE_URL/orders/_all_docs?limit=5&startkey=\"order00057\"&endkey=\"order00077\"" \
# get me 5 documents up to _id order00077
curl -H "Authorization: Bearer $API_BEARER_TOKEN" "$SERVICE_URL/orders/_all_docs?limit=5&endkey=\"order00077\""
import com.ibm.cloud.cloudant.v1.Cloudant;
import com.ibm.cloud.cloudant.v1.model.AllDocsResult;
import com.ibm.cloud.cloudant.v1.model.PostAllDocsOptions;
Cloudant service = Cloudant.newInstance();
// get me 5 documents from _id order00057 onwards
PostAllDocsOptions docsOptions =
new PostAllDocsOptions.Builder()
.db("orders")
.startKey("order00057")
.limit(5)
.build();
AllDocsResult response1 =
service.postAllDocs(docsOptions).execute().getResult();
System.out.println(response1);
// get me 5 documents between _id order00057 --> order00077
PostAllDocsOptions docsOptions2 =
new PostAllDocsOptions.Builder()
.db("orders")
.startKey("order00057")
.endKey("order00077")
.limit(5)
.build();
AllDocsResult response2 =
service.postAllDocs(docsOptions2).execute().getResult();
System.out.println(response2);
// get me 5 documents up to _id order00077
PostAllDocsOptions docsOptions3 =
new PostAllDocsOptions.Builder()
.db("orders")
.endKey("order00077")
.limit(5)
.build();
AllDocsResult response3 =
service.postAllDocs(docsOptions3).execute().getResult();
System.out.println(response3);
const { CloudantV1 } = require('@ibm-cloud/cloudant');
const service = CloudantV1.newInstance({});
// get me 5 documents from _id order00057 onwards
service.postAllDocs({
db: 'orders',
startKey: 'order00057',
limit: 5
}).then(response1 => {
console.log(response1.result);
});
// get me 5 documents from _id order00057 onwards
service.postAllDocs({
db: 'orders',
startKey: 'order00057',
endKey: 'order00077',
limit: 5
}).then(response2 => {
console.log(response2.result);
});
// get me 5 documents up to _id order00077
service.postAllDocs({
db: 'orders',
endKey: 'order00077',
limit: 5
}).then(response3 => {
console.log(response3.result);
});
from ibmcloudant.cloudant_v1 import CloudantV1
service = CloudantV1.new_instance()
# get me 5 documents from _id order00057 onwards
response1 = service.post_all_docs(
db='orders',
start_key='order00057',
limit=5
).get_result()
print(response1)
# get me 5 documents between _id order00057 --> order00077
response2 = service.post_all_docs(
db='orders',
start_key='order00057',
end_key='order00077',
limit=5
).get_result()
print(response2)
# get me 5 documents up to _id order00077
response3 = service.post_all_docs(
db='orders',
end_key='order00077',
limit=5
).get_result()
print(response3)
// get me 5 documents from _id order00057 onwards
postAllDocsOptions1 := cloudantv1.PostAllDocsOptions{
Db: core.StringPtr("orders"),
StartKey: core.StringPtr("order00057"),
Limit: core.Int64Ptr(5),
}
allDocsResult1, response1, err := service.PostAllDocs(postAllDocsOptions1)
if err != nil {
panic(err)
}
b, _ := json.MarshalIndent(allDocsResult1, "", " ")
fmt.Println(string(b))
// get me 5 documents between _id order00057 --> order00077
postAllDocsOptions2 := cloudantv1.PostAllDocsOptions{
Db: core.StringPtr("orders"),
StartKey: core.StringPtr("order00057"),
EndKey: core.StringPtr("order00077"),
Limit: core.Int64Ptr(5),
}
allDocsResult2, response2, err := service.PostAllDocs(postAllDocsOptions2)
if err != nil {
panic(err)
}
b, _ := json.MarshalIndent(allDocsResult2, "", " ")
fmt.Println(string(b))
// get me 5 documents up to _id order00077
postAllDocsOptions3 := cloudantv1.PostAllDocsOptions{
Db: core.StringPtr("orders"),
EndKey: core.StringPtr("order00077"),
Limit: core.Int64Ptr(5),
}
allDocsResult3, response3, err := service.PostAllDocs(postAllDocsOptions3)
if err != nil {
panic(err)
}
b, _ := json.MarshalIndent(allDocsResult3, "", " ")
fmt.Println(string(b))
前の Go の例では、以下のインポート・ブロックが必要です。
import (
"encoding/json"
"fmt"
"github.com/IBM/cloudant-go-sdk/cloudantv1"
"github.com/IBM/go-sdk-core/core"
)
すべての Go の例では、service
オブジェクトを初期化する必要があります。 詳しくは、API 資料の認証セクションで例を参照してください。
この方法を使用すると、返されるデータ・セットのサイズおよび _id
フィールドの範囲を定義できますが、これはページネーションとまったく同じわけではありません。
JSON.stringify('order00077') === "order00077"
startkey
/endkey
値は、JSONエンコードされることが期待されるため、ダブルクォーテーションで囲まれています。
ページネーションのオプション
パフォーマンス上の理由から、大量のデータを表示する場合は、ページ編集の使用を検討する必要があります。 これらの例では、文書は 5 つのブロックで取り出されます。 ただし、実際のアプリケーションでは、ページ・サイズが異なる場合があり、文書サイズ、待ち時間の要求、メモリー使用量、およびその他のトレードオフによって異なります。
以下のセクションで説明されているオプションを使用できます。
オプション 1 - 1 つ余分に文書を取り出す
5 つの文書 (limit=5
) をフェッチする代わりに、5 + 1 (limit=6
) をフェッチしますが、6 番目の文書はユーザーに対して非表示にします。 6番目のドキュメントの「_id
」が、次のページの結果に対するリクエストの「startkey
」になる。
最初のリクエストの例を以下に示す:
curl -H "Authorization: Bearer $API_BEARER_TOKEN" "$SERVICE_URL/orders/_all_docs?limit=6"
import com.ibm.cloud.cloudant.v1.Cloudant;
import com.ibm.cloud.cloudant.v1.model.AllDocsResult;
import com.ibm.cloud.cloudant.v1.model.DocsResultRow;
import com.ibm.cloud.cloudant.v1.model.PostAllDocsOptions;
Cloudant service = Cloudant.newInstance();
int pageSize = 5;
PostAllDocsOptions.Builder docsOptionsBuilder =
new PostAllDocsOptions.Builder()
.db("orders")
.limit(pageSize + 1); // Fetch pageSize + 1 documents
AllDocsResult response =
service.postAllDocs(docsOptionsBuilder.build())
.execute()
.getResult();
while (response.getRows().size() > 1) {
List<DocsResultRow> responseDocuments = response.getRows();
// on the last page, show all documents:
if (responseDocuments.size() <= pageSize) {
System.out.println(responseDocuments);
} else { // otherwise, hide the last document:
System.out.println(responseDocuments.subList(0, pageSize));
}
// The startKey of the next request becomes the hidden document id:
docsOptionsBuilder
.startKey(responseDocuments
.get(responseDocuments.size() - 1)
.getId()
);
response =
service.postAllDocs(docsOptionsBuilder.build())
.execute()
.getResult();
}
const { CloudantV1 } = require('@ibm-cloud/cloudant');
const service = CloudantV1.newInstance({});
async function paginate(pageSize) {
let allDocsResult = (await service.postAllDocs({
db: 'orders',
limit: pageSize + 1
})).result;
while(allDocsResult.rows.length > 1) {
let documents = allDocsResult.rows;
// on the last page, show all documents:
if(documents.length <= pageSize) {
console.log(documents);
} else { // otherwise, hide the last document:
console.log(documents.slice(0, documents.length - 1))
}
allDocsResult = (await service.postAllDocs({
db: 'orders',
limit: pageSize + 1,
startKey: documents[documents.length - 1].id
})).result;
}
}
paginate(5)
import json
from ibmcloudant.cloudant_v1 import CloudantV1
service = CloudantV1.new_instance()
page_size = 5
response = service.post_all_docs(
db='orders',
limit=page_size+1, # Fetch page_size + 1 documents
).get_result()
while len(response["rows"]) > 1:
documents = response['rows']
# on the last page, show all documents:
if len(documents) <= page_size:
print(json.dumps(documents, indent=2))
else: # otherwise, hide the last document:
print(json.dumps(documents[0:-1], indent=2))
response = service.post_all_docs(
db='orders',
limit=page_size+1, # Fetch page_size + 1 documents
start_key=documents[-1]['id']
).get_result()
pageSize := core.Int64Ptr(5)
postAllDocsOptions := service.NewPostAllDocsOptions(
"orders",
)
postAllDocsOptions.SetLimit(*pageSize + 1)
allDocsResult, _, err := service.PostAllDocs(postAllDocsOptions)
if err != nil {
panic(err)
}
for len(viewResult.Rows) > 1 {
documents := allDocsResult.Rows
// on the last page, show all documents:
if int64(len(documents)) <= *pageSize {
b, err := json.MarshalIndent(documents, "", " ")
if err != nil {
panic(err)
}
fmt.Printf(string(b))
} else { // otherwise, hide the last document:
b, err := json.MarshalIndent(documents[0:*pageSize], "", " ")
if err != nil {
panic(err)
}
fmt.Printf(string(b))
}
// The startKey of the next request becomes the hidden document id:
postAllDocsOptions.SetStartKey(*documents[len(documents)-1].ID)
allDocsResult, _, err = service.PostAllDocs(postAllDocsOptions)
if err != nil {
panic(err)
}
}
次の最初の回答例をご覧ください:
{
"total_rows": 11,
"offset": 0,
"rows": [
{ "id": "4eee973603bf77f30b1f880ed83df76a" ....},
{ "id": "4eee973603bf77f30b1f880ed83f469a" ....},
{ "id": "65fa623a384648740ec1f39b495d591c" ....},
{ "id": "d7404903579d6d5880514c22ad983529" ....},
{ "id": "example" ....},
{ "id": "mydoc" ....} // <-- This is the 6th result we use as the startkey of the next request
]
}
次の2番目のリクエストの例をご覧ください:
curl -H "Authorization: Bearer $API_BEARER_TOKEN" "$SERVICE_URL/orders/_all_docs?limit=6&startkey=\"mydoc\""
次の2つ目の回答例を参照のこと:
{
"total_rows": 11,
"offset": 5,
"rows": [
{ "id": "mydoc" ....},
{ "id": "order00057" ....},
...
]
}
前の Go の例では、以下のインポート・ブロックが必要です。
import (
"encoding/json"
"fmt"
"github.com/IBM/cloudant-go-sdk/cloudantv1"
"github.com/IBM/go-sdk-core/core"
すべての Go の例では、service
オブジェクトを初期化する必要があります。 詳しくは、API 資料の認証セクションで例を参照してください。
このオプションは機能するが、'n
必要なのに'n+1
ドキュメントをフェッチしてしまう。
オプション 2 - \u0000 トリック
毎回、文書を n
個のみ取得することにした場合は、startkey
の値を計算する必要があります。この値は、the next ID after the last _id in the result set
を意味します。 例えば、結果の最初のページの最後の文書が「example」であった場合、次に「_all_docs
呼び出すときの「startkey
」は何でなければならないか?
example "であってはならない。そうでなければ、また同じ文書IDを得ることになる。 \u0000
をキー・ストリングの末尾に追加して、「次のキー」を示すことができます (\u0000
は Unicode のヌル文字です。これは、現状のまま URL に配置することも、パーセント・コード %00
を使用して配置することもできます)。 ).
最初のリクエストの例を以下に示す:
curl -H "Authorization: Bearer $API_BEARER_TOKEN" "$SERVICE_URL/orders/_all_docs?limit=5"
import com.ibm.cloud.cloudant.v1.Cloudant;
import com.ibm.cloud.cloudant.v1.model.AllDocsResult;
import com.ibm.cloud.cloudant.v1.model.DocsResultRow;
import com.ibm.cloud.cloudant.v1.model.PostAllDocsOptions;
Long pageSize = 5L;
PostAllDocsOptions.Builder docsOptionsBuilder =
new PostAllDocsOptions.Builder()
.db("orders")
.limit(pageSize); // Fetch pageSize documents
AllDocsResult response =
service.postAllDocs(docsOptionsBuilder.build())
.execute()
.getResult();
while (response.getRows().size() > 0) {
List<DocsResultRow> responseDocuments = response.getRows();
System.out.println(responseDocuments);
// The startKey of the next request becomes the last document id appended with `\u0000`
docsOptionsBuilder.startKey(
responseDocuments
.get(responseDocuments.size() - 1)
.getId() + '\u0000'
);
response =
service.postAllDocs(docsOptionsBuilder.build())
.execute()
.getResult();
}
const { CloudantV1 } = require('@ibm-cloud/cloudant');
const service = CloudantV1.newInstance({});
async function paginate(n) {
let allDocsResult = (await service.postAllDocs({
db: 'orders',
limit: n
})).result;
while (allDocsResult.rows.length > 0) {
let documents = allDocsResult.rows;
console.log(documents);
allDocsResult = (await service.postAllDocs({
db: 'orders',
limit: n,
startKey: documents[documents.length - 1].id + '\u0000'
})).result;
}
}
paginate(5)
import json
from ibmcloudant.cloudant_v1 import CloudantV1
service = CloudantV1.new_instance()
page_size = 5
response = service.post_all_docs(
db='orders',
limit=page_size,
).get_result()
while len(response["rows"]) > 0:
documents = response["rows"]
print(json.dumps(documents, indent=2))
response = service.post_all_docs(
db='orders',
limit=page_size+1, # Fetch page_size + 1 documents
start_key=documents[-1]["id"] + '\u0000'
).get_result()
pageSize := core.Int64Ptr(5)
postAllDocsOptions := service.NewPostAllDocsOptions(
"orders",
)
postAllDocsOptions.SetLimit(*pageSize)
allDocsResult, _, err := service.PostAllDocs(postAllDocsOptions)
if err != nil {
panic(err)
}
for len(allDocsResult.Rows) > 0 {
documents := allDocsResult.Rows
b, err := json.MarshalIndent(documents, "", " ")
if err != nil {
panic(err)
}
fmt.Printf(string(b))
// The startKeyDocId of the next request becomes the last document id appended with `\u0000`
postAllDocsOptions.SetStartKey(*documents[len(documents)-1].ID + "\u0000")
allDocsResult, _, err = service.PostAllDocs(postAllDocsOptions)
if err != nil {
panic(err)
}
}
次の最初の回答例をご覧ください:
{
"total_rows": 11,
"offset": 0,
"rows": [
{ "id": "4eee973603bf77f30b1f880ed83df76a" ....},
{ "id": "4eee973603bf77f30b1f880ed83f469a" ....},
{ "id": "65fa623a384648740ec1f39b495d591c" ....},
{ "id": "d7404903579d6d5880514c22ad983529" ....},
{ "id": "example" ....} // <-- append \u0000 to this to get the startkey of the next request
]
}
次の2番目のリクエストの例をご覧ください:
curl -H "Authorization: Bearer $API_BEARER_TOKEN" "$SERVICE_URL/orders/_all_docs?limit=5&startkey=\"example\u0000\""
次の2つ目の回答例を参照のこと:
{
"total_rows": 11,
"offset": 5,
"rows": [
{ "id": "mydoc" ....},
{ "id": "order00057" ....},
...
{ "id": "order00067" ....} <-- append \u0000 to this to get the startkey of the next request
]
}
前の Go の例では、以下のインポート・ブロックが必要です。
import (
"encoding/json"
"fmt"
"github.com/IBM/cloudant-go-sdk/cloudantv1"
"github.com/IBM/go-sdk-core/core"
)
すべての Go の例では、service
オブジェクトを初期化する必要があります。 詳しくは、API 資料の認証セクションで例を参照してください。
ビューのページネーション
MapReduceビュー、セカンダリインデックスは、'_all_docs
エンドポイントと同様の方法でクエリできるが、代わりに'GET
または'POST
'$SERVICE_URL/$DATABASE/_design/$DDOC/_view/$VIEW
エンドポイントを使用する。 MapReduce ビューは、ユーザー指定の JavaScript 関数から生成される key-value
のペアによって定義されます。 以下のように照会を定義できます。
- パラメーターなしで、ビューからのすべてのデータをスプールします。
include_docs=true
を指定して、文書本文を含めます。startkey
/endkey
を使用して、必要なキー範囲を選択します。ただし、この場合、キーのデータ・タイプがストリングではない可能性があります。
別の面倒な問題は、すべての _id
が固有である 1 次索引とは違って、2 次索引では同じキーを持つ複数の項目があり得るということです。 例えば、キー「"herbivore"
含むエントリーがたくさんある。 この状態では、startkey
/endkey
のみを使用したページネーションが難しくなるため、それを支援するための他のパラメーターとして startkey_docid
/endkey_docid
を使用できます。
最初のリクエストの例を以下に示す:
# get first page of animals by diet
curl -H "Authorization: Bearer $API_BEARER_TOKEN" "$SERVICE_URL/animaldb/_design/views101/_view/diet?limit=3&startkey=\"herbivore\"&endkey=\"herbivore\""
import com.ibm.cloud.cloudant.v1.Cloudant;
import com.ibm.cloud.cloudant.v1.model.PostViewOptions;
import com.ibm.cloud.cloudant.v1.model.ViewResult;
import com.ibm.cloud.cloudant.v1.model.ViewResultRow;
int pageSize = 3;
String diet = "herbivore";
PostViewOptions.Builder viewOptionsBuilder =
new PostViewOptions.Builder()
.db("animaldb")
.ddoc("views101")
.view("diet")
.limit(pageSize) // Fetch pageSize documents
.startKey(diet)
.endKey(diet);
ViewResult response =
service.postView(viewOptionsBuilder.build())
.execute()
.getResult();
while (response.getRows().size() > 0) {
List<ViewResultRow> responseDocuments = response.getRows();
System.out.println(responseDocuments);
// The startKeyDocId of the next request becomes the last document id appended with `\u0000`
viewOptionsBuilder.startKeyDocId(
responseDocuments
.get(responseDocuments.size() - 1)
.getId() + '\u0000'
);
response =
service.postView(viewOptionsBuilder.build())
.execute()
.getResult();
}
const { CloudantV1 } = require('@ibm-cloud/cloudant');
const service = CloudantV1.newInstance({});
async function paginate(pageSize) {
let diet = 'herbivore';
let requestParams = {
db: 'animaldb',
ddoc: 'views101',
view: 'diet',
limit: pageSize,
startKey: diet,
endKey: diet,
};
let viewResult = (await service.postView(requestParams)).result;
while (viewResult.rows.length > 0) {
let documents = viewResult.rows;
console.log(documents);
// The startKeyDocId of the next request becomes the last document id appended with `\u0000`
requestParams.startKeyDocId = documents[documents.length - 1].id + '\u0000';
viewResult = (await service.postView(requestParams)).result;
}
}
paginate(3)
import json
from ibmcloudant.cloudant_v1 import CloudantV1
service = CloudantV1.new_instance()
page_size = 3
diet = 'herbivore'
request_params = d = dict(
db='animaldb',
ddoc='views101',
view='diet',
limit=page_size,
start_key=diet,
end_key=diet,
)
response = service.post_view(**request_params).get_result()
while len(response["rows"]) > 0:
documents = response["rows"]
print(json.dumps(documents, indent=2))
# The startKeyDocId of the next request becomes the last document id appended with `\u0000`
request_params['start_key_doc_id'] = documents[-1]["id"] + '\u0000'
response = service.post_view(**request_params).get_result()
pageSize := core.Int64Ptr(5)
diet := "herbivore"
viewOptions := service.NewPostViewOptions(
"animaldb",
"views101",
"diet",
)
viewOptions.SetLimit(*pageSize)
viewOptions.SetStartKey(diet)
viewOptions.SetEndKey(diet)
viewResult, _, err := service.PostView(viewOptions)
if err != nil {
panic(err)
}
for len(viewResult.Rows) > 0 {
documents := viewResult.Rows
b, err := json.MarshalIndent(documents, "", " ")
if err != nil {
panic(err)
}
fmt.Printf(string(b))
// The startKeyDocId of the next request becomes the last document id appended with `\u0000`
viewOptions.SetStartKey(*documents[len(documents)-1].ID + "\u0000")
viewResult, _, err = service.PostView(viewOptions)
if err != nil {
panic(err)
}
}
次の最初の回答例をご覧ください:
{
"total_rows": 10,
"offset": 2,
"rows": [
{
"id": "elephant",
"key": "herbivore",
"value": 1
},
{
"id": "giraffe",
"key": "herbivore",
"value": 1
},
{
"id": "llama", // <-- append \u0000 to the startkey_docid to of the next request
"key": "herbivore",
"value": 1
}
]
}
次の2番目のリクエストの例をご覧ください:
# get next page of animals by diet
curl -H "Authorization: Bearer $API_BEARER_TOKEN" "$SERVICE_URL/animaldb/_design/views101/_view/diet?limit=3&startkey=\"herbivore\"&endkey=\"herbivore\"&startkey_docid=llama%00"
次の2つ目の回答例を参照のこと:
{
"total_rows": 10,
"offset": 5,
"rows": [
{
"id": "zebra",
"key": "herbivore",
"value": 1
}
]
}
前の Go の例では、以下のインポート・ブロックが必要です。
import (
"encoding/json"
"fmt"
"github.com/IBM/cloudant-go-sdk/cloudantv1"
"github.com/IBM/go-sdk-core/core"
)
すべての Go の例では、service
オブジェクトを初期化する必要があります。 詳しくは、API 資料の認証セクションで例を参照してください。
言い換えれば、2番目のリクエストは、結果の前のページ(llama)の最後のドキュメントIDに魔法の'\u0000
文字(URLでは'llama%00
になる)を加えた'startkey_docid
という値を持っている。
startkey_docid
パラメータは、'startkey
指定され、すべてのインデックスエントリがキーを共有している場合にのみ動作する。 キーが共有されていない場合、ページネーションは'startkey
/endkey
パラメーターのみの操作で実現できる。 また、startkey_docid
パラメーターは JSON エンコードされません。
ブックマーク
検索結果のセットを表示する Web アプリケーションを作成していると想定します。検索結果は、本、俳優、ストアの商品など、何でもかまいません。 ユーザーが検索結果をスクロールするのにつれて、一致項目の別のページが末尾に付加されていきます。 この動作は「無限スクロール」設計パターンと呼ばれます。 これにより、ユーザーはエンドレスに大きなデータ・セットを簡単にスクロールでき、各回でデータベースから取り出すデータのバッチ数は少なくて済みます。
IBM Cloudant ブックマークの仕組み
IBM Cloudant ブックマーク が作成されるのは、この種のアクセス・パターンのためです。 以下のように機能します。
- あなたのアプリケーションは'
find me the first 10 cities where the country is "US"
IBM Cloudantデータベースの検索を実行する。 - IBM Cloudantは、10個のIBM Cloudantドキュメントの配列と、ブックマーク(結果セットの次のドキュメントへのポインタを表す不透明なキー)を提供します。
- 次の結果セットが必要になると、検索が繰り返されます。 しかし、クエリは、最初のレスポンスのブックマークと一緒に、リクエストのIBM Cloudantに送られる。
- IBM Cloudant は、2 番目の文書セットおよび別のブックマークを応答で返します。このブックマークは結果の 3 ページ目を取得するために使用できます。
- これが繰り返されます。
次は、コードを使用してこの機能を実現する方法を見てみましょう。
IBM Cloudant Query を使用して検索する方法
まず、米国内のすべての都市の検索を実行します。 IBM Cloudant Query を使用するので、操作は次のように JSON のブロックとして指定されます。
{
"selector": {
"$eq": {
"country": "US"
}
},
"limit": 5
}
/db/_find
APIエンドポイントを使用すると、結果はIBM Cloudantに渡されます。
curl -X POST \
-H "Authorization: Bearer $API_BEARER_TOKEN" \
-H 'Content-type: application/json' \
-d '{"selector":{"country":{"$eq": "US"}},"limit":5}' \
"$SERVICE_URL/cities/_find"
import com.ibm.cloud.cloudant.v1.Cloudant;
import com.ibm.cloud.cloudant.v1.model.FindResult;
import com.ibm.cloud.cloudant.v1.model.PostFindOptions;
import java.util.Collections;
import java.util.Map;
Cloudant service = Cloudant.newInstance();
Map<String, Object> selector = Collections.singletonMap(
"country",
Collections.singletonMap("$eq", "US"));
PostFindOptions.Builder findOptions = new PostFindOptions.Builder()
.db("cities")
.selector(selector)
.limit(5);
FindResult response = service.postFind(findOptions.build()).execute().getResult();
while (response.getDocs().size() > 0) {
System.out.println(response.getDocs());
// The bookmark of the next request becomes the bookmark of this response
findOptions.bookmark(response.getBookmark());
response = service.postFind(findOptions.build()).execute().getResult();
}
const { CloudantV1 } = require('@ibm-cloud/cloudant');
const service = CloudantV1.newInstance({});
async function paginate(pageSize) {
let requestParams = {
db: 'cities',
selector: {country: {'$eq': 'US'}},
limit: pageSize,
}
let findResult = (await service.postFind(requestParams)).result;
while (findResult.docs.length > 0) {
let documents = findResult.docs;
console.log(documents);
// The bookmark of the next request becomes the bookmark of this response
requestParams.bookmark = findResult.bookmark;
findResult = (await service.postFind(requestParams)).result;
}
}
paginate(5)
request_params = dict(
db='cities',
selector={'country': {'$eq': 'US'}},
limit=5,
)
response = service.post_find(**request_params).get_result()
while len(response['docs']) > 0:
documents = response['docs']
print(json.dumps(documents, indent=2))
# The bookmark of the next request becomes the bookmark of this response
request_params['bookmark'] = response['bookmark']
response = service.post_find(**request_params).get_result()
findOptions := service.NewPostFindOptions(
"cities",
map[string]interface{}{
"country": map[string]bool{
"$eq": "US",
},
},
)
findOptions.SetLimit(5)
findResult, _, err := service.PostFind(findOptions)
if err != nil {
panic(err)
}
for len(findResult.Docs) > 0 {
documents := findResult.Docs
b, err := json.MarshalIndent(documents, "", " ")
if err != nil {
panic(err)
}
fmt.Printf(string(b))
// The bookmark of the next request becomes the bookmark of this response
findOptions.Bookmark = findResult.Bookmark
findResult, _, err = service.PostFind(findOptions)
if err != nil {
panic(err)
}
}
前の Go の例では、以下のインポート・ブロックが必要です。
import (
"encoding/json"
"fmt"
"github.com/IBM/cloudant-go-sdk/cloudantv1"
)
{
"docs":[
{"_id":"10104153","_rev":"1-32aab6258c65c5fc5af044a153f4b994","name":"Silver Lake","latitude":34.08668,"longitude":-118.27023,"country":"US","population":32890,"timezone":"America/Los_Angeles"},
{"_id":"10104154","_rev":"1-125f589bf4e39d8e119b4b7b5b18caf6","name":"Echo Park","latitude":34.07808,"longitude":-118.26066,"country":"US","population":43832,"timezone":"America/Los_Angeles"},
{"_id":"4046704","_rev":"1-2e4b7820872f108c077dab73614067da","name":"Fort Hunt","latitude":38.73289,"longitude":-77.05803,"country":"US","population":16045,"timezone":"America/New_York"},
{"_id":"4048023","_rev":"1-744baaba02218fd84b350e8982c0b783","name":"Bessemer","latitude":33.40178,"longitude":-86.95444,"country":"US","population":27456,"timezone":"America/Chicago"},
{"_id":"4048662","_rev":"1-e95c97013ece566b37583e451c1864ee","name":"Paducah","latitude":37.08339,"longitude":-88.60005,"country":"US","population":25024,"timezone":"America/Chicago"}
],
"bookmark": "g1AAAAA-eJzLYWBgYMpgSmHgKy5JLCrJTq2MT8lPzkzJBYqzmxiYWJiZGYGkOWDSyBJZAPCBD58"
}
応答には、docs
の配列と bookmark
が含まれています。このブックマークは次回の要求での結果のページネーションのために使用します。 結果の 2 ページ目が必要になったら、IBM Cloudant に最初の応答からのブックマークを渡して照会を繰り返します。
curl -X POST \
-H "Authorization: Bearer $API_BEARER_TOKEN" \
-H 'Content-type: application/json' \
-d '{"selector":{"country":{"$eq": "US"}},"limit":5,"bookmark":"g1AAAAA-eJzLYWBgYMpgSmHgKy5JLCrJTq2MT8lPzkzJBYqzmxiYWJiZGYGkOWDSyBJZAPCBD58"}' \
"$SERVICE_URL/cities/_find"
{
"docs":[
{"_id":"4049979","_rev":"1-1fa2591477c774a07c230571568aeb66","name":"Birmingham","latitude":33.52066,"longitude":-86.80249,"country":"US","population":212237,"timezone":"America/Chicago"},
{"_id":"4054378","_rev":"1-a750085697685e7bc0e49d103d2de59d","name":"Center Point","latitude":33.64566,"longitude":-86.6836,"country":"US","population":16921,"timezone":"America/Chicago"},
{"_id":"4058219","_rev":"1-9b4eb183c9cdf57c19be660ec600330c","name":"Daphne","latitude":30.60353,"longitude":-87.9036,"country":"US","population":21570,"timezone":"America/Chicago"},
{"_id":"4058553","_rev":"1-56100f7e7742028facfcc50ab6b07a04","name":"Decatur","latitude":34.60593,"longitude":-86.98334,"country":"US","population":55683,"timezone":"America/Chicago"},
{"_id":"4059102","_rev":"1-612ae37d982dc71eeecf332c1e1c16aa","name":"Dothan","latitude":31.22323,"longitude":-85.39049,"country":"US","population":65496,"timezone":"America/Chicago"}
],
"bookmark": "g1AAAAA-eJzLYWBgYMpgSmHgKy5JLCrJTq2MT8lPzkzJBYqzmxiYWhoaGIGkOWDSyBJZAO9qD40",
"warning": "no matching index found, create an index to optimize query time"
}
今回は、前に続く 5 つの都市と、次回の要求のために準備された新しいブックマークが得られました。
IBM Cloudant Search の仕組み
IBM Cloudant Search 照会でもページネーションは同じように機能します。 GETリクエストの場合はURLに、POSTリクエストの場合はJSONボディに'bookmark
パラメータを渡す。 以下の例を参照してください。
curl -H "Authorization: Bearer $API_BEARER_TOKEN" "$SERVICE_URL/cities/_search/search/_search/freetext?q=country:US&limit=5"
import com.ibm.cloud.cloudant.v1.Cloudant;
import com.ibm.cloud.cloudant.v1.model.PostSearchOptions;
import com.ibm.cloud.cloudant.v1.model.SearchResult;
Cloudant service = Cloudant.newInstance();
PostSearchOptions.Builder searchOptions = new PostSearchOptions.Builder()
.db("cities")
.ddoc("search")
.index("freetext")
.query("country:US");
.limit(5);
SearchResult response = service.postSearch(searchOptions.build()).execute().getResult();
while (response.getRows().size() > 0) {
System.out.println(response.getRows());
// The bookmark of the next request becomes the bookmark of this response
searchOptions.bookmark(response.getBookmark());
response = service.postSearch(searchOptions.build()).execute().getResult();
}
const { CloudantV1 } = require('@ibm-cloud/cloudant');
async function paginate(pageSize) {
let requestParams = {
db: 'cities',
ddoc: 'search',
index: 'freetext',
query: 'country:US',
limit: 5
};
let searchResult = (await service.postSearch(requestParams)).result;
while (searchResult.rows.length > 0) {
let documents = searchResult.rows;
console.log(documents);
// The bookmark of the next request becomes the bookmark of this response
requestParams.bookmark = searchResult.bookmark;
searchResult = (await service.postSearch(requestParams)).result;
}
}
paginate(5);
selector = {'country': {'$eq': 'US'}}
request_params = dict(
db='cities',
ddoc='search',
index='freetext',
query='country:US',
limit=5,
)
response = service.post_search(**request_params).get_result()
while len(response['rows']) > 0:
documents = response['rows']
print(json.dumps(documents, indent=2))
# The bookmark of the next request becomes the bookmark of this response
request_params['bookmark'] = response['bookmark']
response = service.post_search(**request_params).get_result()
searchOptions := service.NewPostSearchOptions(
"cities",
"search",
"freetext",
"country:US"
)
searchOptions.SetLimit(5)
searchResult, _, err := service.PostSearch(searchOptions)
if err != nil {
panic(err)
}
for len(searchResult.Rows) > 0 {
documents := searchResult.Rows
b, err := json.MarshalIndent(documents, "", " ")
if err != nil {
panic(err)
}
fmt.Printf(string(b))
// The bookmark of the next request becomes the bookmark of this response
searchOptions.Bookmark = searchResult.Bookmark
searchResult, _, err = service.PostSearch(searchOptions)
if err != nil {
panic(err)
}
}
前の Go の例では、以下のインポート・ブロックが必要です。
import (
"encoding/json"
"fmt"
"github.com/IBM/cloudant-go-sdk/cloudantv1"
)
{
"total_rows": 65,
"rows":[
{"_id":"10104153","_rev":"1-32aab6258c65c5fc5af044a153f4b994","name":"Silver Lake","latitude":34.08668,"longitude":-118.27023,"country":"US","population":32890,"timezone":"America/Los_Angeles"},
{"_id":"10104154","_rev":"1-125f589bf4e39d8e119b4b7b5b18caf6","name":"Echo Park","latitude":34.07808,"longitude":-118.26066,"country":"US","population":43832,"timezone":"America/Los_Angeles"},
{"_id":"4046704","_rev":"1-2e4b7820872f108c077dab73614067da","name":"Fort Hunt","latitude":38.73289,"longitude":-77.05803,"country":"US","population":16045,"timezone":"America/New_York"},
{"_id":"4048023","_rev":"1-744baaba02218fd84b350e8982c0b783","name":"Bessemer","latitude":33.40178,"longitude":-86.95444,"country":"US","population":27456,"timezone":"America/Chicago"},
{"_id":"4048662","_rev":"1-e95c97013ece566b37583e451c1864ee","name":"Paducah","latitude":37.08339,"longitude":-88.60005,"country":"US","population":25024,"timezone":"America/Chicago"}
],
"bookmark": "g1AAAAA-eJzLYWBgYMpgSmHgKy5JLCrJTq2MT8lPzkzJBYqzmxiYWJiZGYGkOWDSyBJZAPCBD58"
}
bookmark
要求パラメーターを使用して、最初の 5 つの市区町村と、次の要求の準備ができたブックマークを取得します。
curl -H "Authorization: Bearer $API_BEARER_TOKEN" "$SERVICE_URL/cities/_search/search/_search/freetext?q=country:US&limit=5&bookmark=g1AAAAA-eJzLYWBgYMpgSmHgKy5JLCrJTq2MT8lPzkzJBYqzmxiYWJiZGYGkOWDSyBJZAPCBD58"
{
"total_rows": 65,
"rows":[
{"_id":"4049979","_rev":"1-1fa2591477c774a07c230571568aeb66","name":"Birmingham","latitude":33.52066,"longitude":-86.80249,"country":"US","population":212237,"timezone":"America/Chicago"},
{"_id":"4054378","_rev":"1-a750085697685e7bc0e49d103d2de59d","name":"Center Point","latitude":33.64566,"longitude":-86.6836,"country":"US","population":16921,"timezone":"America/Chicago"},
{"_id":"4058219","_rev":"1-9b4eb183c9cdf57c19be660ec600330c","name":"Daphne","latitude":30.60353,"longitude":-87.9036,"country":"US","population":21570,"timezone":"America/Chicago"},
{"_id":"4058553","_rev":"1-56100f7e7742028facfcc50ab6b07a04","name":"Decatur","latitude":34.60593,"longitude":-86.98334,"country":"US","population":55683,"timezone":"America/Chicago"},
{"_id":"4059102","_rev":"1-612ae37d982dc71eeecf332c1e1c16aa","name":"Dothan","latitude":31.22323,"longitude":-85.39049,"country":"US","population":65496,"timezone":"America/Chicago"}
],
"bookmark": "g1AAAAA-eJzLYWBgYMpgSmHgKy5JLCrJTq2MT8lPzkzJBYqzmxiYWhoaGIGkOWDSyBJZAO9qD40",
}
詳しくは、照会パラメーターについての資料を参照してください。
MapReduce ビューはブックマークを受け入れるか
いいえ。 MapReduce ビューは bookmark
を受け入れません。 代わりに、以下のいずれかのトリックを使用して、結果をページ送りします。
結果の第 X ページに直接ジャンプできるか
いいえ。 ブックマークは、結果の前のページからのものである場合にのみ、 IBM Cloudant にとって意味があります。 結果の第 3 ページが必要な場合、まず第 1 ページと第 2 ページを取り出す必要があります。
正しくないブックマークを指定した場合
無効なブックマークを指定すると、IBM Cloudant の応答に HTTP 400 Bad Request { error: 'invalid_bookmark'}
が含まれます。 一連の検索の最初の検索にはブックマークは必要ないことに注意してください。
照会を変更した場合
結果の次ページを取得するには、同じ照会 (IBM Cloudant Query では同じセレクター、IBM Cloudant Search では同じ "q") を保持する必要があります。 照会を変更すると、応答で空の結果セットが返される可能性があります。