From 167b14aa03a66805c6a6be6091c3fa2925f9ac7d Mon Sep 17 00:00:00 2001 From: Chris Elder Date: Wed, 22 Feb 2017 14:46:02 -0500 Subject: [PATCH] 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 --- .../statedb/statecouchdb/query_wrapper.go | 24 +++++++----- .../statecouchdb/query_wrapper_test.go | 30 +++++++++++++++ devenv/Vagrantfile | 2 + .../go/marbles02/marbles_chaincode.go | 37 +++++++++++++++++++ 4 files changed, 83 insertions(+), 10 deletions(-) diff --git a/core/ledger/kvledger/txmgmt/statedb/statecouchdb/query_wrapper.go b/core/ledger/kvledger/txmgmt/statedb/statecouchdb/query_wrapper.go index 9d77af83960..d73f4eba1ce 100644 --- a/core/ledger/kvledger/txmgmt/statedb/statecouchdb/query_wrapper.go +++ b/core/ledger/kvledger/txmgmt/statedb/statecouchdb/query_wrapper.go @@ -22,9 +22,10 @@ import ( "reflect" ) -var dataWrapper = "data" -var jsonQueryFields = "fields" -var jsonQuerySelector = "selector" +const dataWrapper = "data" +const jsonQueryFields = "fields" +const jsonQuerySelector = "selector" +const jsonQueryUseIndex = "use_index" var validOperators = []string{"$and", "$or", "$not", "$nor", "$all", "$elemMatch", "$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 Fields listed in fields key will have "data." prepended Fields in the sort key will have "data." prepended -Also, the query will be scoped to the chaincodeid if the contextID is supplied +Also, the query will be scoped to the chaincodeid + In the example a contextID of "marble" is assumed. Example: @@ -67,13 +69,12 @@ func ApplyQueryWrapper(namespace, queryString string) (string, error) { //traverse through the json query and wrap any field names processAndWrapQuery(jsonQueryMap) - //if "fields" are specified in the query, the add the "_id", "version" and "chaincodeid" fields + //if "fields" are specified in the query, then add the "_id", "version" and "chaincodeid" fields if jsonValue, ok := jsonQueryMap[jsonQueryFields]; ok { //check to see if this is an interface map if reflect.TypeOf(jsonValue).String() == "[]interface {}" { - //Add the "_id" and "version" fields, these are needed by default - //Overwrite the query fields if the "_id" field has been added + //Add the "_id", "version" and "chaincodeid" fields, these are needed by default jsonQueryMap[jsonQueryFields] = append(jsonValue.([]interface{}), "_id", "version", "chaincodeid") } @@ -142,7 +143,7 @@ func setDefaultNamespaceInSelector(namespace string, jsonQueryMap map[string]int func processAndWrapQuery(jsonQueryMap map[string]interface{}) { //iterate through the JSON query - for _, jsonValue := range jsonQueryMap { + for jsonKey, jsonValue := range jsonQueryMap { //create a case for the data types found in the JSON query switch jsonValueType := jsonValue.(type) { @@ -165,8 +166,11 @@ func processAndWrapQuery(jsonQueryMap map[string]interface{}) { case string: - //This is a simple string, so wrap the field and replace in the array - jsonValueType[itemKey] = fmt.Sprintf("%v.%v", dataWrapper, itemValue) + //if this is not "use_index", the wrap the string + if jsonKey != jsonQueryUseIndex { + //This is a simple string, so wrap the field and replace in the array + jsonValueType[itemKey] = fmt.Sprintf("%v.%v", dataWrapper, itemValue) + } case []interface{}: diff --git a/core/ledger/kvledger/txmgmt/statedb/statecouchdb/query_wrapper_test.go b/core/ledger/kvledger/txmgmt/statedb/statecouchdb/query_wrapper_test.go index 859dd3e1439..ba46907715c 100644 --- a/core/ledger/kvledger/txmgmt/statedb/statecouchdb/query_wrapper_test.go +++ b/core/ledger/kvledger/txmgmt/statedb/statecouchdb/query_wrapper_test.go @@ -336,3 +336,33 @@ func TestQueryNoSelector(t *testing.T) { testutil.AssertEquals(t, strings.Count(wrappedQuery, "\"selector\":{\"chaincodeid\":\"ns1\"}"), 1) } + +//TestQueryWithUseDesignDoc tests query with index design doc specified +func TestQueryWithUseDesignDoc(t *testing.T) { + + rawQuery := []byte(`{"selector":{"owner":{"$eq":"jerry"}},"use_index":"_design/testDoc","limit": 10,"skip": 0}`) + + wrappedQuery, err := ApplyQueryWrapper("ns1", string(rawQuery)) + + //Make sure the query did not throw an exception + testutil.AssertNoError(t, err, "Unexpected error thrown when for query JSON") + + //check to make sure the default selector is added + testutil.AssertEquals(t, strings.Count(wrappedQuery, "\"use_index\":\"_design/testDoc\""), 1) + +} + +//TestQueryWithUseDesignDocAndIndexName tests query with index design doc and index name specified +func TestQueryWithUseDesignDocAndIndexName(t *testing.T) { + + rawQuery := []byte(`{"selector":{"owner":{"$eq":"jerry"}},"use_index":["_design/testDoc","testIndexName"],"limit": 10,"skip": 0}`) + + wrappedQuery, err := ApplyQueryWrapper("ns1", string(rawQuery)) + + //Make sure the query did not throw an exception + testutil.AssertNoError(t, err, "Unexpected error thrown when for query JSON") + + //check to make sure the default selector is added + testutil.AssertEquals(t, strings.Count(wrappedQuery, "\"use_index\":[\"_design/testDoc\",\"testIndexName\"]"), 1) + +} diff --git a/devenv/Vagrantfile b/devenv/Vagrantfile index b8ce30d4888..fafa8b35d5a 100644 --- a/devenv/Vagrantfile +++ b/devenv/Vagrantfile @@ -20,6 +20,8 @@ LOCALDEV = "/local-dev" $script = <