IBM Cloud Docs
Paginação e indicadores

Paginação e indicadores

Os marcadores ajudam a liberar a próxima página de resultados de um conjunto de resultados. Enquanto com a paginação, você iterava através de uma gama de documentos de forma eficiente.

É possível usar o padrão skip/limit para iterar por meio de um conjunto de resultados, mas ele fica progressivamente mais lento quanto maior o valor de skip.

IBM Cloudant Query e IBM Cloudant Search usam marcadores como a chave para desbloquear a próxima página de resultados de um conjunto de resultados. Essa prática é descrita na íntegra em uma seção posterior que é chamada de Favoritos. É menos complicado de gerenciar, pois não é necessária nenhuma manipulação de chave para formular a solicitação do próximo conjunto de resultados. Você transmite o marcador que foi recebido na primeira resposta para a segunda solicitação.

Agora, é possível ver um caminho melhor para a página através de um grande conjunto de documentos.

Paginação com _all_docs e visualizações

Se você usar o ponto de extremidade ' GET ou ' POST ' $SERVICE_URL/$DATABASE/_all_docs para buscar documentos em massa, poderá ver os parâmetros ' limit e ' skip. Ao usar esses parâmetros, é possível definir quantos documentos você gostaria e o deslocamento para o intervalo do qual você deseja iniciar. Usar o padrão skip/limit para iterar pelos resultados funciona, mas ele fica progressivamente mais lento quanto maior o valor de skip.

O que é o terminal _all_docs?

O ' GET e o ' POST ' $SERVICE_URL/$DATABASE/_all_docs são usados para buscar dados de um índice primário do banco de dados IBM Cloudant, ou seja, o índice que mantém o ' _id de cada documento em ordem. O terminal _all_docs leva um número de parâmetros opcionais que configuram o intervalo de dados que é solicitado e se deve retornar ou não o corpo de cada documento. Sem parâmetros fornecidos, _all_docs transmite todos os documentos de um banco de dados, retornando apenas o documento _id e seu token _rev atual.

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))

O exemplo do Go anterior requer o bloco de importação a seguir:

import (
   "encoding/json"
   "fmt"
   "github.com/IBM/cloudant-go-sdk/cloudantv1"
)

Todos os exemplos do Go requerem que o objeto service seja inicializado. Para obter mais informações, consulte a seção Autenticação da documentação da API para exemplos.

{
  "total_rows": 11,
  "offset": 0,
  "rows": [
    {
      "id": "4eee973603bf77f30b1f880ed83df76a",
      "key": "4eee973603bf77f30b1f880ed83df76a",
      "value": {
        "rev": "1-3b5e6b73e57745787ad5627fe8f268c1"
      }
    },
    {
      "id": "4eee973603bf77f30b1f880ed83f469a",
      "key": "4eee973603bf77f30b1f880ed83f469a",
      "value": {
        "rev": "1-967a00dff5e02add41819138abb3284d"
      }
    }
...

Se você fornecer include_docs=true, então outro atributo doc será incluído em cada "row" no conjunto de resultados que inclui o corpo do documento.

Os parâmetros limit, startkey e endkey

Para acessar dados de _all_docs em páginas razoavelmente dimensionadas, deve-se fornecer o parâmetro limit para informar ao IBM Cloudant quantos documentos retornar:

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

Também é possível limitar o intervalo de _ids de documentos que você deseja, fornecendo um ou mais valores para startkey ou 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))

O exemplo do Go anterior requer o bloco de importação a seguir:

import (
 "encoding/json"
 "fmt"
 "github.com/IBM/cloudant-go-sdk/cloudantv1"
 "github.com/IBM/go-sdk-core/core"
)

Todos os exemplos do Go requerem que o objeto service seja inicializado. Para obter mais informações, consulte a seção Autenticação da documentação da API para exemplos.

Essa prática significa que você define o tamanho do conjunto de dados e o intervalo do campo _id a ser retornado, mas isso não é exatamente o mesmo que a paginação.

Os valores ' startkey/endkey estão entre aspas duplas porque se espera que sejam codificados em JSON e ' JSON.stringify('order00077') === "order00077".

Opções de paginação

Por motivos de desempenho, se você estiver exibindo grandes quantias de dados, deverá considerar o uso de paginação. Nestes exemplos, documentos são buscados em blocos de cinco. No entanto, em um aplicativo real, o tamanho da página pode ser diferente e depende do tamanho do documento, demandas de latência, consumo de memória e outras compensações.

É possível usar as opções que estão descritas nas seções a seguir.

Opção 1 - Buscar um documento a mais

Em vez de buscar cinco documentos (limit=5), busque 5 + 1 (limit=6), mas oculte o sexto documento dos usuários. O ' _id do sexto documento se torna o ' startkey de sua solicitação para a próxima página de resultados.

Veja o exemplo a seguir de uma primeira solicitação:

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)
        }
}

Veja o exemplo a seguir de uma primeira resposta:

{
  "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
   ]
}    

Veja o exemplo a seguir de uma segunda solicitação:

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

Veja o exemplo a seguir de uma segunda resposta:

{
  "total_rows": 11,
  "offset": 5,
  "rows": [
    { "id": "mydoc" ....},
    { "id": "order00057" ....},
    ...
   ]
}

O exemplo do Go anterior requer o bloco de importação a seguir:

import (
 "encoding/json"
 "fmt"
 "github.com/IBM/cloudant-go-sdk/cloudantv1"
 "github.com/IBM/go-sdk-core/core"

Todos os exemplos do Go requerem que o objeto service seja inicializado. Para obter mais informações, consulte a seção Autenticação da documentação da API para exemplos.

Essa opção funciona, mas você acaba obtendo documentos " n+1 quando apenas " n é necessário.

Opção 2 - O truque \u0000

Se você está determinado a buscar apenas n documentos por vez, precisará calcular um valor de startkey, o que significa the next ID after the last _id in the result set. Por exemplo, se o último documento na primeira página de resultados for "example", qual deve ser o " startkey da próxima chamada para " _all_docs? Não pode ser "exemplo", caso contrário, você receberá o mesmo ID de documento novamente. Acontece que você pode anexar \u0000 até o final de uma string de chave para indicar a "próxima chave" (\u0000 é um caractere nulo Unicode, que pode ser colocado em uma URL no estado-está ou com o código de porcentagem %00). ).

Veja o exemplo a seguir de uma primeira solicitação:

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)
    }
}

Veja o exemplo a seguir de uma primeira resposta:

{
  "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
   ]
}    

Veja o exemplo a seguir de uma segunda solicitação:

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

Veja o exemplo a seguir de uma segunda resposta:

{
  "total_rows": 11,
  "offset": 5,
  "rows": [
    { "id": "mydoc" ....},
    { "id": "order00057" ....},
    ...
    { "id": "order00067" ....} <-- append \u0000 to this to get the startkey of the next request
   ]
}

O exemplo do Go anterior requer o bloco de importação a seguir:

import (
 "encoding/json"
 "fmt"
 "github.com/IBM/cloudant-go-sdk/cloudantv1"
 "github.com/IBM/go-sdk-core/core"
)

Todos os exemplos do Go requerem que o objeto service seja inicializado. Para obter mais informações, consulte a seção Autenticação da documentação da API para exemplos.

Paginação de visualizações

As exibições MapReduce, índices secundários, podem ser consultadas de forma semelhante ao ponto de extremidade ' _all_docs, mas com o ponto de extremidade ' GET ou ' POST ' $SERVICE_URL/$DATABASE/_design/$DDOC/_view/$VIEW. As visualizações MapReduce são definidas por pares de key-value que são produzidos por meio de funções JavaScript fornecidas pelo usuário. É possível definir sua consulta das maneiras a seguir:

  • Fazer spool de todos os dados por meio de uma visualização sem parâmetros.
  • Incluir corpos de documentos fornecendo include_docs=true.
  • Escolher o intervalo de chaves que são necessárias usando startkey/endkey, mas, nesse caso, o tipo de dados das chaves pode não ser uma sequência.

Outra complicação é que ao contrário do índice primário, em que cada _id é exclusivo, o índice secundário pode ter entradas com a mesma chave. Por exemplo, muitas entradas que incluem a chave " "herbivore". Essa situação torna a paginação complicada usando apenas startkey/endkey, portanto, é possível usar outros parâmetros para ajudar: startkey_docid/endkey_docid.

Veja o exemplo a seguir de uma primeira solicitação:

# 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)
    }
}

Veja o exemplo a seguir de uma primeira resposta:

{
  "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
    }
  ]
}

Veja o exemplo a seguir de uma segunda solicitação:

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

Veja o exemplo a seguir de uma segunda resposta:

{
  "total_rows": 10,
  "offset": 5,
  "rows": [
    {
      "id": "zebra",
      "key": "herbivore",
      "value": 1
    }
  ]
}

O exemplo do Go anterior requer o bloco de importação a seguir:

import (
 "encoding/json"
 "fmt"
 "github.com/IBM/cloudant-go-sdk/cloudantv1"
 "github.com/IBM/go-sdk-core/core"
)

Todos os exemplos do Go requerem que o objeto service seja inicializado. Para obter mais informações, consulte a seção Autenticação da documentação da API para exemplos.

Em outras palavras, a segunda solicitação tem um valor de ' startkey_docid que é o último ID de documento da página anterior de resultados (llama) mais o caractere mágico ' \u0000 (que se torna ' llama%00 no URL).

O parâmetro " startkey_docid funciona somente se um " startkey for fornecido e se todas as entradas de índice compartilharem uma chave. Se eles não compartilharem uma chave, a paginação poderá ser obtida somente com a manipulação dos parâmetros ' startkey/endkey. Além disso, o parâmetro startkey_docid não é codificado por JSON.

Marcadores

Imagine que você está criando um aplicativo da web que mostra um conjunto de resultados de procura, sejam eles livros, agentes ou produtos em sua loja. À medida que o usuário rola por meio dos resultados da procura, outra página de correspondências é anexada no final. Esse comportamento é conhecido como um padrão de design "rolagem infinita". Ele permite que o usuário role infinitamente um conjunto de dados grande com facilidade, enquanto busca lotes menores de dados do banco de dados, um de cada vez.

Como os favoritos do IBM Cloudant funcionam?

Os marcadores do IBM Cloudant são construídos para esse tipo de padrão de acesso. Aqui está como ele funciona:

  • Seu aplicativo executa uma pesquisa em um banco de dados IBM Cloudant, por exemplo, ' find me the first 10 cities where the country is "US".
  • IBM Cloudant fornece uma matriz de 10 documentos IBM Cloudant e um marcador, uma chave opaca que representa um ponteiro para os próximos documentos no conjunto de resultados.
  • Quando o próximo conjunto de resultados é necessário, a procura é repetida. No entanto, a consulta é enviada, com o marcador da primeira resposta, para IBM Cloudant na solicitação.
  • O IBM Cloudant responde com o segundo conjunto de documentos e outro favorito, que pode ser usado para obter uma terceira página de resultados.
  • Repita!

Agora, é possível ver como fazer isso com código.

Como o IBM Cloudant Search funciona?

A paginação funciona da mesma maneira para as consultas do IBM Cloudant Search. Passe o parâmetro " bookmark no URL para solicitações GET ou no corpo JSON para solicitações POST. Consulte o seguinte exemplo:

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)
    }
}

O exemplo do Go anterior requer o bloco de importação a seguir:

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

Você recebe as cinco primeiras cidades e um marcador pronto para a próxima solicitação com o parâmetro de solicitação 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",
}

Consulte a documentação sobre parâmetros de consulta para obter detalhes adicionais.

As visualizações do MapReduce aceitam favoritos?

Nº Visualizações MapReduce não aceitam um bookmark. Em vez disso, use um dos truques a seguir para página através de resultados:

Posso pular direto para a página X dos resultados?

Nº Os indicadores fazem sentido apenas para o IBM Cloudant se eles provêm da página de resultados anterior. Para obter a página 3 dos resultados, deve-se buscar primeiro as páginas 1 e 2.

O que acontecerá se eu fornecer um favorito incorreto?

O IBM Cloudant responderá com uma resposta HTTP 400 Bad Request { error: 'invalid_bookmark'} se você fornecer um favorito inválido. Lembre-se de que você não precisa de um favorito para a primeira procura em uma sequência.

O que acontecerá se eu mudar a consulta?

Deve-se manter a mesma consulta (o mesmo seletor em IBM Cloudant Query ou o mesmo "q" em IBM Cloudant Search) para obter a próxima página de resultados. Se você mudar a consulta, poderá obter um resultado vazio configurado em resposta.