IBM Cloud Docs
分页和书签

分页和书签

书签有助于发布结果集的下一页结果。 使用分页时,您可以高效地迭代一系列文档。

您可以使用 skip/limit 模式 对结果集进行迭代,但它的速度会随着 skip 的值越大而逐渐变慢。

IBM Cloudant 查询IBM Cloudant 搜索 都使用书签作为从结果集解锁下一页结果的键。 在称为 书签 的后续部分中完整描述了此实践。 由于无需密钥操作来制定下一个结果集的请求,因此管理起来不太复杂。 将第一个响应中接收到的书签传递到第二个请求。

现在,您可以看到一种更好的方法来浏览大型文档集。

使用 _all_docs 和视图进行页面调度

如果使用 GETPOST $SERVICE_URL/$DATABASE/_all_docs 端点批量访存文档,那么可能会看到 limitskip 参数。 通过使用这些参数,您可以定义要使用的文档数以及要从其开始的范围内的偏移量。 使用 skip/limit 模式对结果进行迭代会起作用,但它的速度会逐渐慢于 skip 的值越大。

什么是 _all_docs 端点?

GETPOST $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 属性添加到包含文档主体的结果集中的每个“行”。

limitstartkeyendkey 参数

要在合理大小的页面中从 _all_docs 访问数据,必须提供 limit 参数以告知 IBM Cloudant 要返回的文档数:

# get me 5 documents
GET $SERVICE_URL/$DATABASE/_all_docs?limit=5 HTTP/1.1

您还可以通过向 startkeyendkey 提供一个或多个值来限制所需的文档 _id 的范围。

# 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 字段的范围,但这与分页不同。

startkey/endkey 值使用双引号,因为它们应该为 JSON 编码和 JSON.stringify('order00077') === "order00077"

分页选项

出于性能原因,如果要显示大量数据,那么必须考虑使用分页。 在这些示例中,将以 5 个块的形式访存文档。 但是,在实际应用程序中,页面大小可能不同,并取决于文档大小,等待时间需求,内存消耗和其他权衡。

您可以使用以下各节所述的选项。

选项 1-访存一个文档过多

访存 5 + 1 (limit=6) 而不是访存五个文档 (limit=5),但对用户隐藏第六个文档。 第六个文档的 _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
   ]
}    

请参阅下面的第二个请求示例:

curl -H "Authorization: Bearer $API_BEARER_TOKEN" "$SERVICE_URL/orders/_all_docs?limit=6&startkey=\"mydoc\""

请参阅下面的第二个回复示例:

{
  "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_docsstartkey 必须是什么? 它不能是“示例”,否则您将再次获取相同的文档标识。 结果是,您可以将 \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
   ]
}    

请参阅下面的第二个请求示例:

curl -H "Authorization: Bearer $API_BEARER_TOKEN" "$SERVICE_URL/orders/_all_docs?limit=5&startkey=\"example\u0000\""

请参阅下面的第二个回复示例:

{
  "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 文档的 认证部分 以获取示例。

视图分页

可以通过与 _all_docs 端点类似的方式查询 MapReduce 视图 (辅助索引),但改为使用 GETPOST $SERVICE_URL/$DATABASE/_design/$DDOC/_view/$VIEW 端点。 MapReduce 视图由用户提供的 JavaScript 函数生成的 key-value 对定义。 您可以通过以下方式定义查询:

  • 在没有参数的情况下假脱机视图中的所有数据。
  • 通过提供 include_docs=true 来包含文档主体。
  • 选择使用 startkey/endkey 所需的键范围,但在这种情况下,键的数据类型可能不是字符串。

另一个复杂情况是,与每个 _id 都唯一的主索引不同,辅助索引可能具有具有相同键的条目。 例如,包含键 "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
    }
  ]
}

请参阅下面的第二个请求示例:

# 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"

请参阅下面的第二个回复示例:

{
  "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 文档的 认证部分 以获取示例。

换言之,第二个请求的值为 startkey_docid,即上一页 results (llama) 中的最后一个文档标识加上 magic \u0000 字符 (在 URL 中变为 llama%00 )。

仅当提供了 startkey 并且所有索引条目共享一个键时,startkey_docid 参数才有效。 如果它们不共享密钥,那么只能通过处理 startkey/endkey 参数来实现分页。 此外,startkey_docid 参数不是 JSON 编码的。

书签

假设您正在创建一个 Web 应用程序,该应用程序显示一组搜索结果,无论这些结果是书籍,参与者还是商店中的产品。 当用户滚动搜索结果时,将在末尾追加另一个匹配页面。 这种行为被称为“无限滚动”设计模式。 它允许用户轻松地无休止地滚动一个大数据集,而他们每次只从数据库中访存较小批次的数据。

IBM Cloudant 书签如何工作?

为其构建 IBM Cloudant 书签 的这种访问模式。 以下是其工作方式:

  • 应用程序在 IBM Cloudant 数据库上运行搜索,例如 find me the first 10 cities where the country is "US"
  • IBM Cloudant 提供了由 10 个 IBM Cloudant 文档和 书签组成的数组,这是一个不透明的键,用于表示指向结果集中下一个文档的指针。
  • 当需要下一组结果时,将重复搜索。 但是,将查询与来自第一个响应的书签一起发送到请求中的 IBM Cloudant。
  • IBM Cloudant 回复第二组文档和另一个书签,可用于获取第三页结果。
  • 重复!

现在,您可以了解如何使用代码执行此操作。

IBM Cloudant 搜索如何工作?

对于 IBM Cloudant 搜索 查询,分页工作方式相同。 在 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 请求参数来准备前五个城市和书签以用于下一个请求。

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 查询中的相同选择器或 IBM Cloudant 搜索中的相同 "q") 以获取下一页结果。 如果更改查询,那么您可能会在应答中获取空的结果集。