Skip to content

Commit 167b14a

Browse files
author
Chris Elder
committed
FAB-2263 GetQueryResult should support index name
Motivation for this change: In CouchDB _find API, use_index parameter can specify which index to utilize for the query. Change the query wrapper to accept the "use-index" parameter. This change can be accomplished using existing JSON query formatting. - Change query wrapper to allow "use_index" correctly - Changes have been made to the marbles02 example to demonstrate queries using specific indexes. - Change Vagrantfile to add a "couchdb" alias to the vagrant host file. This will allow REST API calls to be consistent between vagrant and docker images. Change-Id: Ie54750ccc5ccf8aa01192168b60354f0984c5676 Signed-off-by: Chris Elder <[email protected]>
1 parent 4eec836 commit 167b14a

File tree

4 files changed

+83
-10
lines changed

4 files changed

+83
-10
lines changed

core/ledger/kvledger/txmgmt/statedb/statecouchdb/query_wrapper.go

+14-10
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@ import (
2222
"reflect"
2323
)
2424

25-
var dataWrapper = "data"
26-
var jsonQueryFields = "fields"
27-
var jsonQuerySelector = "selector"
25+
const dataWrapper = "data"
26+
const jsonQueryFields = "fields"
27+
const jsonQuerySelector = "selector"
28+
const jsonQueryUseIndex = "use_index"
2829

2930
var validOperators = []string{"$and", "$or", "$not", "$nor", "$all", "$elemMatch",
3031
"$lt", "$lte", "$eq", "$ne", "$gte", "$gt", "$exits", "$type", "$in", "$nin",
@@ -37,7 +38,8 @@ All fields in the selector must have "data." prepended to the field names
3738
Fields listed in fields key will have "data." prepended
3839
Fields in the sort key will have "data." prepended
3940
40-
Also, the query will be scoped to the chaincodeid if the contextID is supplied
41+
Also, the query will be scoped to the chaincodeid
42+
4143
In the example a contextID of "marble" is assumed.
4244
4345
Example:
@@ -67,13 +69,12 @@ func ApplyQueryWrapper(namespace, queryString string) (string, error) {
6769
//traverse through the json query and wrap any field names
6870
processAndWrapQuery(jsonQueryMap)
6971

70-
//if "fields" are specified in the query, the add the "_id", "version" and "chaincodeid" fields
72+
//if "fields" are specified in the query, then add the "_id", "version" and "chaincodeid" fields
7173
if jsonValue, ok := jsonQueryMap[jsonQueryFields]; ok {
7274
//check to see if this is an interface map
7375
if reflect.TypeOf(jsonValue).String() == "[]interface {}" {
7476

75-
//Add the "_id" and "version" fields, these are needed by default
76-
//Overwrite the query fields if the "_id" field has been added
77+
//Add the "_id", "version" and "chaincodeid" fields, these are needed by default
7778
jsonQueryMap[jsonQueryFields] = append(jsonValue.([]interface{}),
7879
"_id", "version", "chaincodeid")
7980
}
@@ -142,7 +143,7 @@ func setDefaultNamespaceInSelector(namespace string, jsonQueryMap map[string]int
142143
func processAndWrapQuery(jsonQueryMap map[string]interface{}) {
143144

144145
//iterate through the JSON query
145-
for _, jsonValue := range jsonQueryMap {
146+
for jsonKey, jsonValue := range jsonQueryMap {
146147

147148
//create a case for the data types found in the JSON query
148149
switch jsonValueType := jsonValue.(type) {
@@ -165,8 +166,11 @@ func processAndWrapQuery(jsonQueryMap map[string]interface{}) {
165166

166167
case string:
167168

168-
//This is a simple string, so wrap the field and replace in the array
169-
jsonValueType[itemKey] = fmt.Sprintf("%v.%v", dataWrapper, itemValue)
169+
//if this is not "use_index", the wrap the string
170+
if jsonKey != jsonQueryUseIndex {
171+
//This is a simple string, so wrap the field and replace in the array
172+
jsonValueType[itemKey] = fmt.Sprintf("%v.%v", dataWrapper, itemValue)
173+
}
170174

171175
case []interface{}:
172176

core/ledger/kvledger/txmgmt/statedb/statecouchdb/query_wrapper_test.go

+30
Original file line numberDiff line numberDiff line change
@@ -336,3 +336,33 @@ func TestQueryNoSelector(t *testing.T) {
336336
testutil.AssertEquals(t, strings.Count(wrappedQuery, "\"selector\":{\"chaincodeid\":\"ns1\"}"), 1)
337337

338338
}
339+
340+
//TestQueryWithUseDesignDoc tests query with index design doc specified
341+
func TestQueryWithUseDesignDoc(t *testing.T) {
342+
343+
rawQuery := []byte(`{"selector":{"owner":{"$eq":"jerry"}},"use_index":"_design/testDoc","limit": 10,"skip": 0}`)
344+
345+
wrappedQuery, err := ApplyQueryWrapper("ns1", string(rawQuery))
346+
347+
//Make sure the query did not throw an exception
348+
testutil.AssertNoError(t, err, "Unexpected error thrown when for query JSON")
349+
350+
//check to make sure the default selector is added
351+
testutil.AssertEquals(t, strings.Count(wrappedQuery, "\"use_index\":\"_design/testDoc\""), 1)
352+
353+
}
354+
355+
//TestQueryWithUseDesignDocAndIndexName tests query with index design doc and index name specified
356+
func TestQueryWithUseDesignDocAndIndexName(t *testing.T) {
357+
358+
rawQuery := []byte(`{"selector":{"owner":{"$eq":"jerry"}},"use_index":["_design/testDoc","testIndexName"],"limit": 10,"skip": 0}`)
359+
360+
wrappedQuery, err := ApplyQueryWrapper("ns1", string(rawQuery))
361+
362+
//Make sure the query did not throw an exception
363+
testutil.AssertNoError(t, err, "Unexpected error thrown when for query JSON")
364+
365+
//check to make sure the default selector is added
366+
testutil.AssertEquals(t, strings.Count(wrappedQuery, "\"use_index\":[\"_design/testDoc\",\"testIndexName\"]"), 1)
367+
368+
}

devenv/Vagrantfile

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ LOCALDEV = "/local-dev"
2020
$script = <<SCRIPT
2121
set -x
2222
23+
echo "127.0.0.1 couchdb" | tee -a /etc/hosts
24+
2325
export DOCKER_STORAGE_BACKEND="#{ENV['DOCKER_STORAGE_BACKEND']}"
2426
2527
cd #{SRCMOUNT}/devenv

examples/chaincode/go/marbles02/marbles_chaincode.go

+37
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,48 @@ under the License.
2929

3030
// ==== Query marbles ====
3131
// peer chaincode query -C myc1 -n marbles -c '{"Args":["readMarble","marble1"]}'
32+
// peer chaincode query -C myc1 -n marbles -c '{"Args":["getHistoryForMarble","marble1"]}'
3233

3334
// Rich Query (Only supported if CouchDB is used as state database):
3435
// peer chaincode query -C myc1 -n marbles -c '{"Args":["queryMarblesByOwner","tom"]}'
3536
// peer chaincode query -C myc1 -n marbles -c '{"Args":["queryMarbles","{\"selector\":{\"owner\":\"tom\"}}"]}'
3637

38+
//The following examples demonstrate creating indexes on CouchDB
39+
//Example hostname:port configurations
40+
//
41+
//Docker or vagrant environments:
42+
// http://couchdb:5984/
43+
//
44+
//Inside couchdb docker container
45+
// http://127.0.0.1:5984/
46+
47+
// Index for chaincodeid, docType, owner.
48+
// Note that docType and owner fields must be prefixed with the "data" wrapper
49+
// chaincodeid must be added for all queries
50+
//
51+
// Definition for use with Fauxton interface
52+
// {"index":{"fields":["chaincodeid","data.docType","data.owner"]},"ddoc":"indexOwnerDoc", "name":"indexOwner","type":"json"}
53+
//
54+
// example curl definition for use with command line
55+
// curl -i -X POST -H "Content-Type: application/json" -d "{\"index\":{\"fields\":[\"chaincodeid\",\"data.docType\",\"data.owner\"]},\"name\":\"indexOwner\",\"ddoc\":\"indexOwnerDoc\",\"type\":\"json\"}" http://hostname:port/myc1/_index
56+
//
57+
58+
// Index for chaincodeid, docType, owner, size (descending order).
59+
// Note that docType, owner and size fields must be prefixed with the "data" wrapper
60+
// chaincodeid must be added for all queries
61+
//
62+
// Definition for use with Fauxton interface
63+
// {"index":{"fields":[{"data.size":"desc"},{"chaincodeid":"desc"},{"data.docType":"desc"},{"data.owner":"desc"}]},"ddoc":"indexSizeSortDoc", "name":"indexSizeSortDesc","type":"json"}
64+
//
65+
// example curl definition for use with command line
66+
// curl -i -X POST -H "Content-Type: application/json" -d "{\"index\":{\"fields\":[{\"data.size\":\"desc\"},{\"chaincodeid\":\"desc\"},{\"data.docType\":\"desc\"},{\"data.owner\":\"desc\"}]},\"ddoc\":\"indexSizeSortDoc\", \"name\":\"indexSizeSortDesc\",\"type\":\"json\"}" http://hostname:port/myc1/_index
67+
68+
// Rich Query with index design doc and index name specified (Only supported if CouchDB is used as state database):
69+
// peer chaincode query -C myc1 -n marbles -c '{"Args":["queryMarbles","{\"selector\":{\"docType\":\"marble\",\"owner\":\"tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}"]}'
70+
71+
// Rich Query with index design doc specified only (Only supported if CouchDB is used as state database):
72+
// peer chaincode query -C myc1 -n marbles -c '{"Args":["queryMarbles","{\"selector\":{\"docType\":{\"$eq\":\"marble\"},\"owner\":{\"$eq\":\"tom\"},\"size\":{\"$gt\":0}},\"fields\":[\"docType\",\"owner\",\"size\"],\"sort\":[{\"size\":\"desc\"}],\"use_index\":\"_design/indexSizeSortDoc\"}"]}'
73+
3774
package main
3875

3976
import (

0 commit comments

Comments
 (0)