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 _id
s 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 posso usar o IBM Cloudant Query para procurar?
Primeiro, você procura por todas as cidades dos EUA. Você está usando o IBM Cloudant Query, portanto, a operação é especificada como um bloco de JSON:
{
"selector": {
"$eq": {
"country": "US"
}
},
"limit": 5
}
Ao usar o ponto de extremidade da API ' /db/_find
, os resultados são passados para 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)
}
}
O exemplo do Go anterior requer o bloco de importação a seguir:
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"
}
A resposta inclui uma matriz de docs
e um bookmark
, que você usa para paginar por meio dos resultados na próxima solicitação. Quando você precisar da página dois dos resultados, repetirá a consulta transmitindo ao
IBM Cloudant o marcador da primeira resposta.
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"
}
Desta vez, você recebe as próximas cinco cidades e um novo marcador pronto para a próxima solicitação.
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.