Skip to content

Commit a864726

Browse files
Chris Elderdenyeart
authored andcommitted
[FAB-8740] State database Document pagination
This change documents pagination for rich query and range queries. Changes will applied to couchdb_as_state_database.html and the couchdb tutorial. Change-Id: I7d85c04e55b71f4c76be178c012eea4a25bb17f9 Signed-off-by: Chris Elder <chris.elder@us.ibm.com> Signed-off-by: joe-alewine <Joe.Alewine@ibm.com> Signed-off-by: David Enyeart <enyeart@us.ibm.com>
1 parent 0ceb6d7 commit a864726

File tree

2 files changed

+184
-1
lines changed

2 files changed

+184
-1
lines changed

docs/source/couchdb_as_state_database.rst

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ complex rich queries if needed in the future.
4646
Using CouchDB from Chaincode
4747
----------------------------
4848

49+
Chaincode queries
50+
~~~~~~~~~~~~~~~~~
51+
4952
Most of the `chaincode shim APIs <https://godoc.org/github.com/hyperledger/fabric/core/chaincode/shim#ChaincodeStubInterface>`__
5053
can be utilized with either LevelDB or CouchDB state database, e.g. ``GetState``, ``PutState``,
5154
``GetStateByRange``, ``GetStateByPartialCompositeKey``. Additionally when you utilize CouchDB as
@@ -63,6 +66,36 @@ syntax:
6366
6467
{"selector":{"docType":"marble","owner":<OWNER_ID>}}
6568
69+
.. couchdb-pagination:
70+
71+
CouchDB pagination
72+
^^^^^^^^^^^^^^^^^^
73+
74+
Fabric supports paging of query results for rich queries and range based queries.
75+
APIs supporting pagination allow the use of page size and bookmarks to be used for
76+
both range and rich queries.
77+
78+
If a pagesize is specified using the paginated query APIs (``GetStateByRangeWithPagination``,
79+
``GetStateByPartialCompositeKeyWithPagination()``, and ``GetQueryResultWithPagination()``),
80+
a set of results will be returned along with a bookmark. The bookmark can be used
81+
with a follow on query to receive the next "page" of results.
82+
83+
All chaincode queries are bound by ``totalQueryLimit`` (default 100000)
84+
from ``core.yaml``. This is the maximum number of results that chaincode
85+
will iterate through and return to the client, in order to avoid accidental
86+
or malicious long-running queries.
87+
88+
An example using pagination is included in the :doc:`couchdb_tutorial` tutorial.
89+
90+
.. note:: Regardless of whether chaincode uses paginated queries or not, the peer will
91+
query CouchDB in batches based on ``internalQueryLimit`` (default 1000)
92+
from ``core.yaml``. This behavior ensures reasonably sized result sets are
93+
passed between the peer and CouchDB, and is transparent to chaincode and
94+
requires no additional configuration.
95+
96+
CouchDB indexes
97+
~~~~~~~~~~~~~~~
98+
6699
Indexes in CouchDB are required in order to make JSON queries efficient and are required for
67100
any JSON query with a sort. Indexes can be packaged alongside chaincode in a
68101
``/META-INF/statedb/couchdb/indexes`` directory. Each index must be defined in its own
@@ -180,4 +213,4 @@ is to be changed after creation of the container.
180213
.. note:: CouchDB peer options are read on each peer startup.
181214

182215
.. Licensed under Creative Commons Attribution 4.0 International License
183-
https://creativecommons.org/licenses/by/4.0/
216+
https://creativecommons.org/licenses/by/4.0/

docs/source/couchdb_tutorial.rst

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ The tutorial will take you through the following steps:
1313
#. :ref:`cdb-add-index`
1414
#. :ref:`cdb-install-instantiate`
1515
#. :ref:`cdb-query`
16+
#. :ref:`cdb-pagination`
1617
#. :ref:`cdb-update-index`
1718
#. :ref:`cdb-delete-index`
1819

@@ -495,6 +496,155 @@ The query runs successfully and the index is leveraged with the following result
495496
496497
Query Result: [{"Key":"marble1", "Record":{"color":"blue","docType":"marble","name":"marble1","owner":"tom","size":35}}]
497498
499+
.. _cdb-pagination:
500+
501+
502+
Query the CouchDB State Database With Pagination
503+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
504+
505+
When large result sets are returned by CouchDB queries, a set of APIs is
506+
available which can be called by chaincode to paginate the list of results.
507+
Pagination provides a mechanism to partition the result set by
508+
specifying a ``pagesize`` and a start point -- a ``bookmark`` which indicates
509+
where to begin the result set. The client application iteratively invokes the
510+
chaincode that executes the query until no more results are returned. For more information refer to
511+
this `topic on pagination with CouchDB <http://hyperledger-fabric.readthedocs.io/en/master/couchdb_as_state_database.html#couchdb-pagination>`__.
512+
513+
514+
We will use the `Marbles sample <https://github.com/hyperledger/fabric-samples/blob/master/chaincode/marbles02/go/marbles_chaincode.go>`__
515+
function ``queryMarblesWithPagination`` to demonstrate how
516+
pagination can be implemented in chaincode and the client application.
517+
518+
* **queryMarblesWithPagination** --
519+
520+
Example of an **ad hoc rich query with pagination**. This is a query
521+
where a (selector) string can be passed into the function similar to the
522+
above example. In this case, a ``pageSize`` is also included with the query as
523+
well as a ``bookmark``.
524+
525+
In order to demonstrate pagination, more data is required. This example assumes
526+
that you have already added marble1 from above. Run the following commands in
527+
the peer container to create four more marbles owned by "tom", to create a
528+
total of five marbles owned by "tom":
529+
530+
:guilabel:`Try it yourself`
531+
532+
.. code:: bash
533+
534+
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C $CHANNEL_NAME -n marbles -c '{"Args":["initMarble","marble2","yellow","35","tom"]}'
535+
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C $CHANNEL_NAME -n marbles -c '{"Args":["initMarble","marble3","green","20","tom"]}'
536+
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C $CHANNEL_NAME -n marbles -c '{"Args":["initMarble","marble4","purple","20","tom"]}'
537+
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C $CHANNEL_NAME -n marbles -c '{"Args":["initMarble","marble5","blue","40","tom"]}'
538+
539+
In addition to the arguments for the query in the previous example,
540+
queryMarblesWithPagination adds ``pagesize`` and ``bookmark``. ``PageSize``
541+
specifies the number of records to return per query. The ``bookmark`` is an
542+
"anchor" telling couchDB where to begin the page. (Each page of results returns
543+
a unique bookmark.)
544+
545+
* ``queryMarblesWithPagination``
546+
Name of the function in the Marbles chaincode. Notice a `shim <https://godoc.org/github.com/hyperledger/fabric/core/chaincode/shim>`__
547+
``shim.ChaincodeStubInterface`` is used to access and modify the ledger. The
548+
``getQueryResultForQueryStringWithPagination()`` passes the queryString along
549+
with the pagesize and bookmark to the shim API ``GetQueryResultWithPagination()``.
550+
551+
.. code:: bash
552+
553+
func (t *SimpleChaincode) queryMarblesWithPagination(stub shim.ChaincodeStubInterface, args []string) pb.Response {
554+
555+
// 0
556+
// "queryString"
557+
if len(args) < 3 {
558+
return shim.Error("Incorrect number of arguments. Expecting 3")
559+
}
560+
561+
queryString := args[0]
562+
//return type of ParseInt is int64
563+
pageSize, err := strconv.ParseInt(args[1], 10, 32)
564+
if err != nil {
565+
return shim.Error(err.Error())
566+
}
567+
bookmark := args[2]
568+
569+
queryResults, err := getQueryResultForQueryStringWithPagination(stub, queryString, int32(pageSize), bookmark)
570+
if err != nil {
571+
return shim.Error(err.Error())
572+
}
573+
return shim.Success(queryResults)
574+
}
575+
576+
577+
The following example is a peer command which calls queryMarblesWithPagination
578+
with a pageSize of ``3`` and no bookmark specified.
579+
580+
.. tip:: When no bookmark is specified, the query starts with the "first"
581+
page of records.
582+
583+
:guilabel:`Try it yourself`
584+
585+
.. code:: bash
586+
587+
// Rich Query with index name explicitly specified and a page size of 3:
588+
peer chaincode query -C $CHANNEL_NAME -n marbles -c '{"Args":["queryMarblesWithPagination", "{\"selector\":{\"docType\":\"marble\",\"owner\":\"tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}","3",""]}'
589+
590+
The following response is received (carriage returns added for clarity), three
591+
of the five marbles are returned because the ``pagsize`` was set to ``3``:
592+
593+
.. code:: bash
594+
595+
[{"Key":"marble1", "Record":{"color":"blue","docType":"marble","name":"marble1","owner":"tom","size":35}},
596+
{"Key":"marble2", "Record":{"color":"yellow","docType":"marble","name":"marble2","owner":"tom","size":35}},
597+
{"Key":"marble3", "Record":{"color":"green","docType":"marble","name":"marble3","owner":"tom","size":20}}]
598+
[{"ResponseMetadata":{"RecordsCount":"3",
599+
"Bookmark":"g1AAAABLeJzLYWBgYMpgSmHgKy5JLCrJTq2MT8lPzkzJBYqz5yYWJeWkGoOkOWDSOSANIFk2iCyIyVySn5uVBQAGEhRz"}}]
600+
601+
.. note:: Bookmarks are uniquely generated by CouchDB for each query and
602+
represent a placeholder in the result set. Pass the
603+
returned bookmark on the subsequent iteration of the query to
604+
retrieve the next set of results.
605+
606+
The following is a peer command to call queryMarblesWithPagination with a
607+
pageSize of ``3``. Notice this time, the query includes the bookmark returned
608+
from the previous query.
609+
610+
:guilabel:`Try it yourself`
611+
612+
.. code:: bash
613+
614+
peer chaincode query -C $CHANNEL_NAME -n marbles -c '{"Args":["queryMarblesWithPagination", "{\"selector\":{\"docType\":\"marble\",\"owner\":\"tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}","3","g1AAAABLeJzLYWBgYMpgSmHgKy5JLCrJTq2MT8lPzkzJBYqz5yYWJeWkGoOkOWDSOSANIFk2iCyIyVySn5uVBQAGEhRz"]}'
615+
616+
The following response is received (carriage returns added for clarity). The
617+
last two records are retrieved:
618+
619+
.. code:: bash
620+
621+
[{"Key":"marble4", "Record":{"color":"purple","docType":"marble","name":"marble4","owner":"tom","size":20}},
622+
{"Key":"marble5", "Record":{"color":"blue","docType":"marble","name":"marble5","owner":"tom","size":40}}]
623+
[{"ResponseMetadata":{"RecordsCount":"2",
624+
"Bookmark":"g1AAAABLeJzLYWBgYMpgSmHgKy5JLCrJTq2MT8lPzkzJBYqz5yYWJeWkmoKkOWDSOSANIFk2iCyIyVySn5uVBQAGYhR1"}}]
625+
626+
The final command is a peer command to call queryMarblesWithPagination with
627+
a pageSize of ``3`` and with the bookmark from the previous query.
628+
629+
:guilabel:`Try it yourself`
630+
631+
.. code:: bash
632+
633+
peer chaincode query -C $CHANNEL_NAME -n marbles -c '{"Args":["queryMarblesWithPagination", "{\"selector\":{\"docType\":\"marble\",\"owner\":\"tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}","3","g1AAAABLeJzLYWBgYMpgSmHgKy5JLCrJTq2MT8lPzkzJBYqz5yYWJeWkmoKkOWDSOSANIFk2iCyIyVySn5uVBQAGYhR1"]}'
634+
635+
The following response is received (carriage returns added for clarity).
636+
No records are returned, indicating that all pages have been retrieved:
637+
638+
.. code:: bash
639+
640+
[]
641+
[{"ResponseMetadata":{"RecordsCount":"0",
642+
"Bookmark":"g1AAAABLeJzLYWBgYMpgSmHgKy5JLCrJTq2MT8lPzkzJBYqz5yYWJeWkmoKkOWDSOSANIFk2iCyIyVySn5uVBQAGYhR1"}}]
643+
644+
For an example of how a client application can iterate over
645+
the result sets using pagination, search for the ``getQueryResultForQueryStringWithPagination``
646+
function in the `Marbles sample <https://github.com/hyperledger/fabric-samples/blob/master/chaincode/marbles02/go/marbles_chaincode.go>`__.
647+
498648
.. _cdb-update-index:
499649
500650
Update an Index

0 commit comments

Comments
 (0)