You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by as...@apache.org on 2019/06/27 11:53:37 UTC

[servicecomb-kie] 26/29: support combination query

This is an automated email from the ASF dual-hosted git repository.

asifdxtreme pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/servicecomb-kie.git

commit 999a22414e4ff0340d883e28e149d83fcab60648
Author: tian <xi...@gmail.com>
AuthorDate: Wed Jun 19 18:35:34 2019 +0800

    support combination query
---
 go.mod                              |  3 +-
 pkg/common/common.go                |  3 +-
 server/dao/kie_api.go               |  7 +---
 server/dao/kv_test.go               |  8 ++--
 server/resource/v1/common.go        | 31 +++++++++++----
 server/resource/v1/common_test.go   | 31 +++++++++++++++
 server/resource/v1/doc_struct.go    | 14 ++++---
 server/resource/v1/kv_resource.go   | 77 ++++++++++++++-----------------------
 server/resource/v1/v1_suite_test.go | 13 +++++++
 9 files changed, 113 insertions(+), 74 deletions(-)

diff --git a/go.mod b/go.mod
index d5efedc..6646204 100644
--- a/go.mod
+++ b/go.mod
@@ -4,11 +4,12 @@ require (
 	github.com/emicklei/go-restful v2.8.0+incompatible
 	github.com/go-chassis/foundation v0.0.0-20190516083152-b8b2476b6db7
 	github.com/go-chassis/go-archaius v0.16.0
-	github.com/go-chassis/go-chassis v1.4.1
+	github.com/go-chassis/go-chassis v1.4.3
 	github.com/go-chassis/paas-lager v1.0.2-0.20190328010332-cf506050ddb2
 	github.com/go-mesh/openlogging v1.0.1-0.20181205082104-3d418c478b2d
 	github.com/onsi/ginkgo v1.8.0
 	github.com/onsi/gomega v1.5.0
+	github.com/pkg/errors v0.8.0
 	github.com/stretchr/testify v1.2.2
 	github.com/urfave/cli v1.20.0
 	github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c // indirect
diff --git a/pkg/common/common.go b/pkg/common/common.go
index e39dca6..f111bce 100644
--- a/pkg/common/common.go
+++ b/pkg/common/common.go
@@ -19,8 +19,7 @@ package common
 
 //match mode
 const (
-	MatchGreedy = "greedy"
-	MatchExact  = "exact"
+	QueryParamQ = "q"
 )
 
 //http headers
diff --git a/server/dao/kie_api.go b/server/dao/kie_api.go
index a5cca3d..0e0020d 100644
--- a/server/dao/kie_api.go
+++ b/server/dao/kie_api.go
@@ -191,7 +191,7 @@ func (s *MongodbService) FindKVByLabelID(ctx context.Context, domain, labelID, k
 
 //FindKV get kvs by key, labels
 //because labels has a a lot of combination,
-//you can use WithExactLabels to return only one kv which's labels exactly match the criteria
+//you can use WithDepth(0) to return only one kv which's labels exactly match the criteria
 func (s *MongodbService) FindKV(ctx context.Context, domain string, options ...FindOption) ([]*model.KVResponse, error) {
 	opts := FindOptions{}
 	for _, o := range options {
@@ -208,7 +208,7 @@ func (s *MongodbService) FindKV(ctx context.Context, domain string, options ...F
 	defer cur.Close(ctx)
 
 	kvResp := make([]*model.KVResponse, 0)
-	if opts.ExactLabels {
+	if opts.Depth == 0 {
 		openlogging.Debug("find one key", openlogging.WithTags(
 			map[string]interface{}{
 				"key":    opts.Key,
@@ -218,9 +218,6 @@ func (s *MongodbService) FindKV(ctx context.Context, domain string, options ...F
 		))
 		return cursorToOneKV(ctx, cur, opts.Labels)
 	}
-	if opts.Depth == 0 {
-		opts.Depth = 1
-	}
 	for cur.Next(ctx) {
 		curKV := &model.KVDoc{}
 
diff --git a/server/dao/kv_test.go b/server/dao/kv_test.go
index efbe6d4..db92857 100644
--- a/server/dao/kv_test.go
+++ b/server/dao/kv_test.go
@@ -132,8 +132,8 @@ var _ = Describe("Kv mongodb service", func() {
 			It("should not return err", func() {
 				Expect(err).Should(BeNil())
 			})
-			It("should has 2 records", func() {
-				Expect(len(kvs)).Should(Equal(2))
+			It("should has 1 records", func() {
+				Expect(len(kvs)).Should(Equal(1))
 			})
 
 		})
@@ -189,8 +189,8 @@ var _ = Describe("Kv mongodb service", func() {
 			It("should not return err", func() {
 				Expect(err).Should(BeNil())
 			})
-			It("should has 2 records", func() {
-				Expect(len(kvs)).Should(Equal(2))
+			It("should has 1 records", func() {
+				Expect(len(kvs)).Should(Equal(1))
 			})
 
 		})
diff --git a/server/resource/v1/common.go b/server/resource/v1/common.go
index 48baa22..dcf9901 100644
--- a/server/resource/v1/common.go
+++ b/server/resource/v1/common.go
@@ -22,15 +22,17 @@ import (
 	"fmt"
 	"github.com/apache/servicecomb-kie/pkg/common"
 	"github.com/apache/servicecomb-kie/pkg/model"
+	goRestful "github.com/emicklei/go-restful"
 	"github.com/go-chassis/go-chassis/server/restful"
 	"github.com/go-mesh/openlogging"
+	"github.com/pkg/errors"
 	"strconv"
+	"strings"
 )
 
 //const of server
 const (
 	MsgDomainMustNotBeEmpty = "domain must not be empty"
-	MsgIllegalFindPolicy    = "value of header " + common.HeaderMatch + " can be greedy or exact"
 	MsgIllegalLabels        = "label's value can not be empty, " +
 		"label can not be duplicated, please check your query parameters"
 	MsgIllegalDepth   = "X-Depth must be number"
@@ -55,14 +57,27 @@ func ReadFindDepth(context *restful.Context) (int, error) {
 	return depth, nil
 }
 
-//ReadMatchPolicy get match policy
-func ReadMatchPolicy(context *restful.Context) string {
-	policy := context.ReadRestfulRequest().HeaderParameter(common.HeaderMatch)
-	if policy == "" {
-		//default is exact to reduce network traffic
-		return common.MatchExact
+//ReadLabelCombinations get query combination from url
+//q=app:default+service:payment&q=app:default
+func ReadLabelCombinations(req *goRestful.Request) ([]map[string]string, error) {
+	queryCombinations := req.QueryParameters(common.QueryParamQ)
+	labelCombinations := make([]map[string]string, 0)
+	for _, queryStr := range queryCombinations {
+		labelStr := strings.Split(queryStr, " ")
+		labels := make(map[string]string, len(labelStr))
+		for _, label := range labelStr {
+			l := strings.Split(label, ":")
+			if len(l) != 2 {
+				return nil, errors.New("wrong query syntax:" + label)
+			}
+			labels[l[0]] = l[1]
+		}
+		if len(labels) == 0 {
+			continue
+		}
+		labelCombinations = append(labelCombinations, labels)
 	}
-	return policy
+	return labelCombinations, nil
 }
 
 //WriteErrResponse write error message to client
diff --git a/server/resource/v1/common_test.go b/server/resource/v1/common_test.go
new file mode 100644
index 0000000..6f3b139
--- /dev/null
+++ b/server/resource/v1/common_test.go
@@ -0,0 +1,31 @@
+package v1_test
+
+import (
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+
+	. "github.com/apache/servicecomb-kie/server/resource/v1"
+	"github.com/emicklei/go-restful"
+	"net/http"
+)
+
+var _ = Describe("Common", func() {
+	Describe("set query combination", func() {
+		Context("valid param", func() {
+			r, err := http.NewRequest("GET",
+				"/kv?q=app:mall+service:payment&q=app:mall+service:payment+version:1.0.0",
+				nil)
+			It("should not return err ", func() {
+				Expect(err).Should(BeNil())
+			})
+			c, err := ReadLabelCombinations(restful.NewRequest(r))
+			It("should not return err ", func() {
+				Expect(err).Should(BeNil())
+			})
+			It("should has 2 combinations", func() {
+				Expect(len(c)).Should(Equal(2))
+			})
+
+		})
+	})
+})
diff --git a/server/resource/v1/doc_struct.go b/server/resource/v1/doc_struct.go
index 37f7432..33f0eb7 100644
--- a/server/resource/v1/doc_struct.go
+++ b/server/resource/v1/doc_struct.go
@@ -31,17 +31,19 @@ var (
 		ParamType: goRestful.HeaderParameterKind,
 		Desc:      "integer, default is 1, if you set match policy, you can set,depth to decide label number",
 	}
+	DocQueryCombination = &restful.Parameters{
+		DataType:  "string",
+		Name:      common.QueryParamQ,
+		ParamType: goRestful.QueryParameterKind,
+		Desc: "the combination format is {label_key}:{label_value}+{label_key}:{label_value} " +
+			"for example: /v1/kv?q=app:mall&q=app:mall+service:cart " +
+			"that will query key values from 2 kinds of labels",
+	}
 	DocPathKey = &restful.Parameters{
 		DataType:  "string",
 		Name:      "key",
 		ParamType: goRestful.PathParameterKind,
 	}
-	DocHeaderMath = &restful.Parameters{
-		DataType:  "string",
-		Name:      common.HeaderMatch,
-		ParamType: goRestful.HeaderParameterKind,
-		Desc:      "greedy or exact",
-	}
 )
 
 //KVBody is open api doc
diff --git a/server/resource/v1/kv_resource.go b/server/resource/v1/kv_resource.go
index a1f94b6..c072dc6 100644
--- a/server/resource/v1/kv_resource.go
+++ b/server/resource/v1/kv_resource.go
@@ -21,7 +21,6 @@ package v1
 import (
 	"encoding/json"
 	"fmt"
-	"github.com/apache/servicecomb-kie/pkg/common"
 	"github.com/apache/servicecomb-kie/pkg/model"
 	"github.com/apache/servicecomb-kie/server/dao"
 	goRestful "github.com/emicklei/go-restful"
@@ -67,8 +66,8 @@ func (r *KVResource) Put(context *restful.Context) {
 
 }
 
-//FindWithKey search key by label and key
-func (r *KVResource) FindWithKey(context *restful.Context) {
+//GetByKey search key by label and key
+func (r *KVResource) GetByKey(context *restful.Context) {
 	var err error
 	key := context.ReadPathParameter("key")
 	if key == "" {
@@ -94,23 +93,12 @@ func (r *KVResource) FindWithKey(context *restful.Context) {
 		WriteErrResponse(context, http.StatusInternalServerError, MsgDomainMustNotBeEmpty)
 		return
 	}
-	policy := ReadMatchPolicy(context)
 	d, err := ReadFindDepth(context)
 	if err != nil {
 		WriteErrResponse(context, http.StatusBadRequest, MsgIllegalDepth)
 		return
 	}
-	var kvs []*model.KVResponse
-	switch policy {
-	case common.MatchGreedy:
-		kvs, err = s.FindKV(context.Ctx, domain.(string), dao.WithKey(key), dao.WithLabels(labels), dao.WithDepth(d))
-	case common.MatchExact:
-		kvs, err = s.FindKV(context.Ctx, domain.(string), dao.WithKey(key), dao.WithLabels(labels),
-			dao.WithExactLabels())
-	default:
-		WriteErrResponse(context, http.StatusBadRequest, MsgIllegalFindPolicy)
-		return
-	}
+	kvs, err := s.FindKV(context.Ctx, domain.(string), dao.WithKey(key), dao.WithLabels(labels), dao.WithDepth(d))
 	if err == dao.ErrKeyNotExists {
 		WriteErrResponse(context, http.StatusNotFound, err.Error())
 		return
@@ -126,17 +114,13 @@ func (r *KVResource) FindWithKey(context *restful.Context) {
 
 }
 
-//FindByLabels search key only by label
-func (r *KVResource) FindByLabels(context *restful.Context) {
+//SearchByLabels search key only by label
+func (r *KVResource) SearchByLabels(context *restful.Context) {
 	var err error
-	values := context.ReadRequest().URL.Query()
-	labels := make(map[string]string, len(values))
-	for k, v := range values {
-		if len(v) != 1 {
-			WriteErrResponse(context, http.StatusBadRequest, MsgIllegalLabels)
-			return
-		}
-		labels[k] = v[0]
+	labelCombinations, err := ReadLabelCombinations(context.ReadRestfulRequest())
+	if err != nil {
+		WriteErrResponse(context, http.StatusBadRequest, err.Error())
+		return
 	}
 	s, err := dao.NewKVService()
 	if err != nil {
@@ -148,27 +132,24 @@ func (r *KVResource) FindByLabels(context *restful.Context) {
 		WriteErrResponse(context, http.StatusInternalServerError, MsgDomainMustNotBeEmpty)
 		return
 	}
-	policy := ReadMatchPolicy(context)
-	d, err := ReadFindDepth(context)
-	if err != nil {
-		WriteErrResponse(context, http.StatusBadRequest, MsgIllegalDepth)
-		return
-	}
 	var kvs []*model.KVResponse
-	switch policy {
-	case common.MatchGreedy:
-		kvs, err = s.FindKV(context.Ctx, domain.(string), dao.WithLabels(labels), dao.WithDepth(d))
-	case common.MatchExact:
-		kvs, err = s.FindKV(context.Ctx, domain.(string), dao.WithLabels(labels),
-			dao.WithExactLabels())
-	default:
-		WriteErrResponse(context, http.StatusBadRequest, MsgIllegalFindPolicy)
-		return
+	for _, labels := range labelCombinations {
+		result, err := s.FindKV(context.Ctx, domain.(string), dao.WithLabels(labels))
+		if err != nil {
+			if err == dao.ErrKeyNotExists {
+				continue
+			}
+			WriteErrResponse(context, http.StatusInternalServerError, err.Error())
+			return
+		}
+		kvs = append(kvs, result...)
+
 	}
-	if err == dao.ErrKeyNotExists {
+	if len(kvs) == 0 {
 		WriteErrResponse(context, http.StatusNotFound, err.Error())
 		return
 	}
+
 	err = context.WriteHeaderAndJSON(http.StatusOK, kvs, goRestful.MIME_JSON)
 	if err != nil {
 		openlogging.Error(err.Error())
@@ -215,7 +196,7 @@ func (r *KVResource) URLPatterns() []restful.Route {
 					DataType:  "string",
 					Name:      "X-Realm",
 					ParamType: goRestful.HeaderParameterKind,
-					Desc:      "set kv to heterogeneous config server",
+					Desc:      "set kv to heterogeneous config server, not implement yet",
 				},
 			},
 			Returns: []*restful.Returns{
@@ -230,7 +211,7 @@ func (r *KVResource) URLPatterns() []restful.Route {
 		}, {
 			Method:           http.MethodGet,
 			Path:             "/v1/kv/{key}",
-			ResourceFuncName: "FindWithKey",
+			ResourceFuncName: "GetByKey",
 			FuncDesc:         "get key values by key and labels",
 			Parameters: []*restful.Parameters{
 				DocPathKey, DocHeaderMath, DocHeaderDepth,
@@ -239,7 +220,7 @@ func (r *KVResource) URLPatterns() []restful.Route {
 				{
 					Code:    http.StatusOK,
 					Message: "get key value success",
-					Model:   []*KVBody{},
+					Model:   []*model.KVResponse{},
 				},
 			},
 			Consumes: []string{goRestful.MIME_JSON},
@@ -248,16 +229,16 @@ func (r *KVResource) URLPatterns() []restful.Route {
 		}, {
 			Method:           http.MethodGet,
 			Path:             "/v1/kv",
-			ResourceFuncName: "FindByLabels",
-			FuncDesc:         "find key values only by labels",
+			ResourceFuncName: "SearchByLabels",
+			FuncDesc:         "search key values by labels combination",
 			Parameters: []*restful.Parameters{
-				DocHeaderMath, DocHeaderDepth,
+				DocHeaderMath, DocQueryCombination,
 			},
 			Returns: []*restful.Returns{
 				{
 					Code:    http.StatusOK,
 					Message: "get key value success",
-					Model:   []*KVBody{},
+					Model:   []*model.KVResponse{},
 				},
 			},
 			Consumes: []string{goRestful.MIME_JSON},
diff --git a/server/resource/v1/v1_suite_test.go b/server/resource/v1/v1_suite_test.go
new file mode 100644
index 0000000..23b7482
--- /dev/null
+++ b/server/resource/v1/v1_suite_test.go
@@ -0,0 +1,13 @@
+package v1_test
+
+import (
+	"testing"
+
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+)
+
+func TestV1(t *testing.T) {
+	RegisterFailHandler(Fail)
+	RunSpecs(t, "V1 Suite")
+}