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

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


The following commit(s) were added to refs/heads/master by this push:
     new f1ff3dd  #87  view create and get content (#88)
f1ff3dd is described below

commit f1ff3ddaa784dbff19879a97410fd3c2beddb8b6
Author: Shawn <[email protected]>
AuthorDate: Fri Feb 7 10:49:37 2020 +0800

    #87  view create and get content (#88)
    
    * #87  view create and get content
    
    * #87  ut
    
    * #87  ut
    
    * #87  ut
    
    * #87  ut
    
    * #87  ut
    
    * #87 lint
    
    * #87 lint
    
    * #87 lint
---
 .travis.yml                                    |   4 +-
 client/client.go                               |   8 +-
 client/client_test.go                          |  25 ++--
 deployments/db.js                              |  23 +++-
 examples/dev/docker-compose.yaml               |   2 +-
 go.mod                                         |   4 +-
 go.sum                                         |  63 +++++++++
 pkg/model/db_schema.go                         |   9 ++
 pkg/model/kv.go                                |   6 +
 server/resource/v1/common.go                   |   1 +
 server/resource/v1/kv_resource.go              |   1 +
 server/resource/v1/label_resouce.go            |   5 +-
 server/service/mongo/kv/kv_test.go             |   4 +-
 server/service/mongo/kv/tool.go                |   6 +-
 server/service/mongo/label/label_dao.go        |   2 +-
 server/service/mongo/label/label_service.go    |   2 +
 server/service/mongo/session/session.go        |  28 +++-
 server/service/mongo/view/view_dao.go          |  58 ++++++++
 server/service/mongo/view/view_service.go      | 177 +++++++++++++++++++++++++
 server/service/mongo/view/view_service_test.go | 113 ++++++++++++++++
 server/service/service.go                      |  16 ++-
 21 files changed, 519 insertions(+), 38 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index f9147ae..a2f7b8a 100755
--- a/.travis.yml
+++ b/.travis.yml
@@ -43,7 +43,7 @@ jobs:
         - bash scripts/travis/goConstChecker.sh
     - stage: GoLint Checker
       script:
-        - go get -u golang.org/x/lint
+        - go get -u golang.org/x/lint/golint
         - bash scripts/travis/goLintChecker.sh
     - stage: GoCyclo Checker
       script:
@@ -55,8 +55,8 @@ jobs:
         - GO111MODULE=on go mod download
         - GO111MODULE=on go mod vendor
         - bash scripts/travis/start_deps.sh
-        - sleep 15
         - cd $HOME/gopath/src/github.com/apache/servicecomb-kie
         - go get github.com/mattn/goveralls
         - go get golang.org/x/tools/cmd/cover
+        - sleep 30
         - bash scripts/travis/unit_test.sh && $HOME/gopath/bin/goveralls 
-coverprofile=coverage.txt -service=travis-ci
diff --git a/client/client.go b/client/client.go
index 01b8e94..2bc1d43 100644
--- a/client/client.go
+++ b/client/client.go
@@ -53,7 +53,7 @@ type Client struct {
        opts            Config
        cipher          security.Cipher
        c               *httpclient.Requests
-       currentRevision string
+       CurrentRevision string
 }
 
 //Config is the config of client
@@ -133,9 +133,9 @@ func (c *Client) Get(ctx context.Context, opts 
...GetOption) (*model.KVResponse,
 
        var url string
        if options.Key != "" {
-               url = fmt.Sprintf("%s/%s/%s/%s/%s?revision=%s", 
c.opts.Endpoint, version, options.Project, APIPathKV, options.Key, 
c.currentRevision)
+               url = fmt.Sprintf("%s/%s/%s/%s/%s?revision=%s", 
c.opts.Endpoint, version, options.Project, APIPathKV, options.Key, 
c.CurrentRevision)
        } else {
-               url = fmt.Sprintf("%s/%s/%s/%s?revision=%s", c.opts.Endpoint, 
version, options.Project, APIPathKV, c.currentRevision)
+               url = fmt.Sprintf("%s/%s/%s/%s?revision=%s", c.opts.Endpoint, 
version, options.Project, APIPathKV, c.CurrentRevision)
        }
        if options.Wait != "" {
                url = url + "&wait=" + options.Wait
@@ -176,7 +176,7 @@ func (c *Client) Get(ctx context.Context, opts 
...GetOption) (*model.KVResponse,
                openlogging.Error("unmarshal kv failed:" + err.Error())
                return nil, err
        }
-       c.currentRevision = resp.Header.Get(common.HeaderRevision)
+       c.CurrentRevision = resp.Header.Get(common.HeaderRevision)
        return kvs, nil
 }
 
diff --git a/client/client_test.go b/client/client_test.go
index 53cc84b..b66a598 100644
--- a/client/client_test.go
+++ b/client/client_test.go
@@ -37,17 +37,17 @@ func TestClient_Put(t *testing.T) {
                Labels: map[string]string{"service": "client"},
                Value:  "timeout: 1s",
        }
-       _, err := c.Put(context.TODO(), kv, WithProject("test"))
+       _, err := c.Put(context.TODO(), kv, WithProject("client_test"))
        assert.NoError(t, err)
 
        kvs, _ := c.Get(context.TODO(),
                WithKey("app.properties"),
-               WithGetProject("test"),
+               WithGetProject("client_test"),
                WithLabels(map[string]string{"service": "client"}))
        assert.GreaterOrEqual(t, len(kvs.Data), 1)
 
        _, err = c.Get(context.TODO(),
-               WithGetProject("test"),
+               WithGetProject("client_test"),
                WithLabels(map[string]string{"service": "client"}))
        assert.Equal(t, ErrNoChanges, err)
 
@@ -59,7 +59,7 @@ func TestClient_Put(t *testing.T) {
                go func() {
                        kvs, err = c.Get(context.TODO(),
                                WithLabels(map[string]string{"service": 
"client"}),
-                               WithGetProject("test"),
+                               WithGetProject("client_test"),
                                WithWait("10s"))
                        assert.NoError(t, err)
                        assert.Equal(t, "timeout: 2s", kvs.Data[0].Value)
@@ -69,7 +69,7 @@ func TestClient_Put(t *testing.T) {
                        Labels: map[string]string{"service": "client"},
                        Value:  "timeout: 2s",
                }
-               _, err := c.Put(context.TODO(), kv, WithProject("test"))
+               _, err := c.Put(context.TODO(), kv, WithProject("client_test"))
                assert.NoError(t, err)
        })
        t.Run("exact match", func(t *testing.T) {
@@ -78,10 +78,11 @@ func TestClient_Put(t *testing.T) {
                        Labels: map[string]string{"service": "client", 
"version": "1.0"},
                        Value:  "timeout: 2s",
                }
-               _, err := c.Put(context.TODO(), kv, WithProject("test"))
+               _, err := c.Put(context.TODO(), kv, WithProject("client_test"))
                assert.NoError(t, err)
+               t.Log(c.CurrentRevision)
                kvs, err = c.Get(context.TODO(),
-                       WithGetProject("test"),
+                       WithGetProject("client_test"),
                        WithLabels(map[string]string{"service": "client"}),
                        WithExact())
                assert.NoError(t, err)
@@ -99,15 +100,15 @@ func TestClient_Delete(t *testing.T) {
        kvBody.Value = "100s"
        kvBody.ValueType = "text"
        kvBody.Labels = make(map[string]string)
-       kvBody.Labels["env"] = "test"
-       kv, err := c.Put(context.TODO(), kvBody, WithProject("test"))
+       kvBody.Labels["env"] = "client_test"
+       kv, err := c.Put(context.TODO(), kvBody, WithProject("client_test"))
        assert.NoError(t, err)
        kvs, err := c.Get(context.TODO(),
                WithKey("time"),
-               WithGetProject("test"),
-               WithLabels(map[string]string{"env": "test"}))
+               WithGetProject("client_test"),
+               WithLabels(map[string]string{"env": "client_test"}))
        assert.NoError(t, err)
        assert.NotNil(t, kvs)
-       err = c.Delete(context.TODO(), kv.ID, "", WithProject("test"))
+       err = c.Delete(context.TODO(), kv.ID, "", WithProject("client_test"))
        assert.NoError(t, err)
 }
diff --git a/deployments/db.js b/deployments/db.js
index 288b2d9..ef6e407 100644
--- a/deployments/db.js
+++ b/deployments/db.js
@@ -67,11 +67,32 @@ db.createCollection( "label", {
             }
         } }
 } );
-
+db.createCollection( "view", {
+    validator: { $jsonSchema: {
+            bsonType: "object",
+            required: [ "id","domain","project","display","criteria" ],
+            properties: {
+                id: {
+                    bsonType: "string",
+                },
+                domain: {
+                    bsonType: "string"
+                },
+                project: {
+                    bsonType: "string"
+                },
+                criteria: {
+                    bsonType: "string"
+                }
+            }
+        } }
+} );
 //index
 db.kv.createIndex({"id": 1}, { unique: true } );
 db.kv.createIndex({key: 1, label_id: 1,domain:1,project:1},{ unique: true });
 db.label.createIndex({"id": 1}, { unique: true } );
 db.label.createIndex({format: 1,domain:1,project:1},{ unique: true });
+db.view.createIndex({"id": 1}, { unique: true } );
+db.view.createIndex({display:1,domain:1,project:1},{ unique: true });
 //db config
 db.setProfilingLevel(1, {slowms: 80, sampleRate: 1} );
\ No newline at end of file
diff --git a/examples/dev/docker-compose.yaml b/examples/dev/docker-compose.yaml
index 8ca81b4..04e207d 100644
--- a/examples/dev/docker-compose.yaml
+++ b/examples/dev/docker-compose.yaml
@@ -17,7 +17,7 @@
 version: '3.1'
 services:
   mongo:
-    image: mongo
+    image: mongo:4.0
     restart: always
     ports:
       - 27017:27017
diff --git a/go.mod b/go.mod
index 70eda61..4c0ea9e 100644
--- a/go.mod
+++ b/go.mod
@@ -8,15 +8,13 @@ require (
        github.com/go-chassis/go-chassis v1.8.2-0.20191227102336-e3ac2ea137b1
        github.com/go-chassis/paas-lager v1.1.1
        github.com/go-mesh/openlogging v1.0.1
-       github.com/golang/snappy v0.0.1 // indirect
        github.com/hashicorp/mdns v1.0.1 // indirect
        github.com/hashicorp/serf v0.8.5
        github.com/satori/go.uuid v1.2.0
        github.com/stretchr/testify v1.4.0
        github.com/urfave/cli v1.20.0
-       github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c // indirect
        github.com/xdg/stringprep v1.0.0 // indirect
-       go.mongodb.org/mongo-driver v1.2.1
+       go.mongodb.org/mongo-driver v1.3.0
        gopkg.in/yaml.v2 v2.2.4
 )
 
diff --git a/go.sum b/go.sum
index a13f0fb..255818c 100644
--- a/go.sum
+++ b/go.sum
@@ -73,6 +73,30 @@ github.com/go-openapi/spec 
v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nA
 github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod 
h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
 github.com/go-stack/stack v1.8.0 
h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
 github.com/go-stack/stack v1.8.0/go.mod 
h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod 
h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
+github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod 
h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
+github.com/gobuffalo/depgen v0.1.0/go.mod 
h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
+github.com/gobuffalo/envy v1.6.15/go.mod 
h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/envy v1.7.0/go.mod 
h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/flect v0.1.0/go.mod 
h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs=
+github.com/gobuffalo/flect v0.1.1/go.mod 
h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
+github.com/gobuffalo/flect v0.1.3/go.mod 
h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
+github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod 
h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk=
+github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod 
h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28=
+github.com/gobuffalo/genny v0.1.0/go.mod 
h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo=
+github.com/gobuffalo/genny v0.1.1/go.mod 
h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk=
+github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod 
h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw=
+github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod 
h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360=
+github.com/gobuffalo/gogen v0.1.0/go.mod 
h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg=
+github.com/gobuffalo/gogen v0.1.1/go.mod 
h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE=
+github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod 
h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8=
+github.com/gobuffalo/mapi v1.0.1/go.mod 
h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
+github.com/gobuffalo/mapi v1.0.2/go.mod 
h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
+github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod 
h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
+github.com/gobuffalo/packd v0.1.0/go.mod 
h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
+github.com/gobuffalo/packr/v2 v2.0.9/go.mod 
h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
+github.com/gobuffalo/packr/v2 v2.2.0/go.mod 
h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
+github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod 
h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
 github.com/gogo/protobuf v1.1.1/go.mod 
h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod 
h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod 
h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@@ -136,6 +160,8 @@ github.com/hashicorp/serf v0.8.5/go.mod 
h1:UpNcs7fFbpKIyZaUuSW6EPiH+eZC7OuyFD+wc
 github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 github.com/hpcloud/tail v1.0.0/go.mod 
h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 github.com/imdario/mergo v0.3.5/go.mod 
h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod 
h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/joho/godotenv v1.3.0/go.mod 
h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
 github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod 
h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/json-iterator/go v1.1.5/go.mod 
h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/json-iterator/go v1.1.8 
h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok=
@@ -143,14 +169,21 @@ github.com/json-iterator/go v1.1.8/go.mod 
h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
 github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod 
h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
 github.com/jtolds/gls v4.20.0+incompatible/go.mod 
h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 github.com/julienschmidt/httprouter v1.2.0/go.mod 
h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/karrick/godirwalk v1.8.0/go.mod 
h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
+github.com/karrick/godirwalk v1.10.3/go.mod 
h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
 github.com/kisielk/errcheck v1.2.0/go.mod 
h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
 github.com/kisielk/gotool v1.0.0/go.mod 
h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.9.5 
h1:U+CaK85mrNNb4k8BNOfgJtJ/gr6kswUCFj6miSzVC6M=
+github.com/klauspost/compress v1.9.5/go.mod 
h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod 
h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod 
h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod 
h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 github.com/kr/pretty v0.1.0/go.mod 
h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod 
h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod 
h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod 
h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
+github.com/markbates/safe v1.0.1/go.mod 
h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
 github.com/mattn/go-colorable v0.0.9 
h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
 github.com/mattn/go-colorable v0.0.9/go.mod 
h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
 github.com/mattn/go-isatty v0.0.3 
h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
@@ -170,6 +203,7 @@ github.com/modern-go/reflect2 
v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lN
 github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod 
h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/modern-go/reflect2 v1.0.1 
h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
 github.com/modern-go/reflect2 v1.0.1/go.mod 
h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod 
h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
 github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod 
h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod 
h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod 
h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
@@ -185,9 +219,12 @@ github.com/opentracing/opentracing-go v1.0.2/go.mod 
h1:UkNAQd3GIcIGf0SeVgPpRdFSt
 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod 
h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
 github.com/patrickmn/go-cache v2.1.0+incompatible 
h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
 github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod 
h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
+github.com/pelletier/go-toml v1.4.0/go.mod 
h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
 github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod 
h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
 github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
 github.com/pkg/errors v0.8.0/go.mod 
h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
+github.com/pkg/errors v0.8.1/go.mod 
h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod 
h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pmezard/go-difflib v1.0.0 
h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod 
h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -203,19 +240,27 @@ github.com/prometheus/common v0.2.0/go.mod 
h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod 
h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1 
h1:/K3IL0Z1quvmJ7X0A1AwNEK7CRkVK3YwfOU/QAL4WGg=
 github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod 
h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/rogpeppe/go-internal v1.1.0/go.mod 
h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.2.2/go.mod 
h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.3.0/go.mod 
h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod 
h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
 github.com/satori/go.uuid v1.2.0 
h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
 github.com/satori/go.uuid v1.2.0/go.mod 
h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 
h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod 
h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
 github.com/sirupsen/logrus v1.2.0/go.mod 
h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.0/go.mod 
h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.1/go.mod 
h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
+github.com/sirupsen/logrus v1.4.2/go.mod 
h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod 
h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod 
h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod 
h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 github.com/spf13/afero v1.2.2/go.mod 
h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
 github.com/spf13/cast v1.2.0 h1:HHl1DSRbEQN2i8tJmtS6ViPyHx35+p51amrdsiTCrkg=
 github.com/spf13/cast v1.2.0/go.mod 
h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
+github.com/spf13/cobra v0.0.3/go.mod 
h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
 github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod 
h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.3/go.mod 
h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.5/go.mod 
h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/stretchr/objx v0.1.0/go.mod 
h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod 
h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -224,21 +269,27 @@ github.com/stretchr/testify v1.2.2/go.mod 
h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
 github.com/stretchr/testify v1.3.0/go.mod 
h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0 
h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
 github.com/stretchr/testify v1.4.0/go.mod 
h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/tidwall/pretty v1.0.0/go.mod 
h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
 github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
 github.com/urfave/cli v1.20.0/go.mod 
h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
 github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c 
h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk=
 github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod 
h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
+github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod 
h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
 github.com/xdg/stringprep v1.0.0 
h1:d9X0esnoa3dFsV0FG35rAT0RIhYFlPq7MiP+DW89La0=
 github.com/xdg/stringprep v1.0.0/go.mod 
h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
 go.mongodb.org/mongo-driver v1.0.3 
h1:GKoji1ld3tw2aC+GX1wbr/J2fX13yNacEYoJ8Nhr0yU=
 go.mongodb.org/mongo-driver v1.0.3/go.mod 
h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
 go.mongodb.org/mongo-driver v1.2.1 
h1:ANAlYXXM5XmOdW/Nc38jOr+wS5nlk7YihT24U1imiWM=
 go.mongodb.org/mongo-driver v1.2.1/go.mod 
h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
+go.mongodb.org/mongo-driver v1.3.0 
h1:ew6uUIeJOo+qdUUv7LxFCUhtWmVv7ZV/Xuy4FAUsw2E=
+go.mongodb.org/mongo-driver v1.3.0/go.mod 
h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod 
h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod 
h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod 
h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod 
h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod 
h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
+golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod 
h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586 
h1:7KByu05hhLed2MO29w7p1XfZvZ13m8mub3shuVftRs0=
 golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod 
h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod 
h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -266,6 +317,9 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod 
h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 
h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI=
 golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58 
h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod 
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod 
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod 
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -275,7 +329,11 @@ golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod 
h1:STP8DvDyc/dI5b8T5h
 golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod 
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod 
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod 
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191018095205-727590c5006e 
h1:ZtoklVMHQy6BFRHkbG6JzK+S6rX82//Yeok1vMlizfQ=
 golang.org/x/sys v0.0.0-20191018095205-727590c5006e/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -294,6 +352,10 @@ golang.org/x/tools 
v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
 golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod 
h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod 
h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod 
h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod 
h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod 
h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod 
h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod 
h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 google.golang.org/api v0.4.0/go.mod 
h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
 google.golang.org/appengine v1.1.0/go.mod 
h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod 
h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -305,6 +367,7 @@ google.golang.org/grpc v1.19.0/go.mod 
h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod 
h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod 
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod 
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod 
h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
diff --git a/pkg/model/db_schema.go b/pkg/model/db_schema.go
index 535e495..19922f4 100644
--- a/pkg/model/db_schema.go
+++ b/pkg/model/db_schema.go
@@ -43,3 +43,12 @@ type KVDoc struct {
        Domain string            `json:"domain,omitempty" 
yaml:"domain,omitempty"` //redundant
 
 }
+
+//ViewDoc is db struct, it saves user's custom view name and criteria
+type ViewDoc struct {
+       ID       string `json:"id,omitempty" bson:"id,omitempty" 
yaml:"id,omitempty" swag:"string"`
+       Display  string `json:"display,omitempty" yaml:"display,omitempty"`
+       Project  string `json:"project,omitempty" yaml:"project,omitempty"`
+       Domain   string `json:"domain,omitempty" yaml:"domain,omitempty"`
+       Criteria string `json:"criteria,omitempty" yaml:"criteria,omitempty"`
+}
diff --git a/pkg/model/kv.go b/pkg/model/kv.go
index a9a8952..0db6875 100644
--- a/pkg/model/kv.go
+++ b/pkg/model/kv.go
@@ -48,3 +48,9 @@ type LabelHistoryResponse struct {
        KVs      []*KVDoc          `json:"data,omitempty"`
        Revision int               `json:"revision"`
 }
+
+//ViewResponse represents the view list
+type ViewResponse struct {
+       Total int        `json:"total,omitempty"`
+       Data  []*ViewDoc `json:"data,omitempty"`
+}
diff --git a/server/resource/v1/common.go b/server/resource/v1/common.go
index 310b4e3..3414554 100644
--- a/server/resource/v1/common.go
+++ b/server/resource/v1/common.go
@@ -50,6 +50,7 @@ const (
        MaxWait = 5 * time.Minute
 )
 
+//err
 var (
        ErrInvalidRev = errors.New(MsgInvalidRev)
 )
diff --git a/server/resource/v1/kv_resource.go 
b/server/resource/v1/kv_resource.go
index 8851f5e..87ba64b 100644
--- a/server/resource/v1/kv_resource.go
+++ b/server/resource/v1/kv_resource.go
@@ -106,6 +106,7 @@ func (r *KVResource) GetByKey(rctx *restful.Context) {
        returnData(rctx, domain, project, labels, limit, offset)
 }
 
+//List response kv list
 func (r *KVResource) List(rctx *restful.Context) {
        var err error
        project := rctx.ReadPathParameter("project")
diff --git a/server/resource/v1/label_resouce.go 
b/server/resource/v1/label_resouce.go
index a29a865..c573189 100644
--- a/server/resource/v1/label_resouce.go
+++ b/server/resource/v1/label_resouce.go
@@ -10,12 +10,11 @@ import (
        "net/http"
 )
 
-//LabelResource
+//LabelResource is label API
 type LabelResource struct {
 }
 
-//PutLabel
-// update by label_id , only can modify alias
+//PutLabel update by label_id , only can modify alias
 // create return 201 / update return 200
 func (r *LabelResource) PutLabel(context *restful.Context) {
        var err error
diff --git a/server/service/mongo/kv/kv_test.go 
b/server/service/mongo/kv/kv_test.go
index 0416053..69afb3a 100644
--- a/server/service/mongo/kv/kv_test.go
+++ b/server/service/mongo/kv/kv_test.go
@@ -97,12 +97,12 @@ func TestService_CreateOrUpdate(t *testing.T) {
                        Domain:  "default",
                        Project: "test",
                })
-               assert.Equal(t, beforeKV.ID, afterKV.ID)
+               assert.Equal(t, "3s", afterKV.Value)
                savedKV, err := kvsvc.Exist(context.Background(), "default", 
"timeout", "test", service.WithLabels(map[string]string{
                        "app": "mall",
                }))
                assert.NoError(t, err)
-               assert.Equal(t, beforeKV.ID, savedKV.ID)
+               assert.Equal(t, afterKV.Value, savedKV.Value)
                kvs, err := kvsvc.FindKV(context.Background(), "default", 
"test",
                        service.WithKey("timeout"),
                        service.WithLabels(map[string]string{
diff --git a/server/service/mongo/kv/tool.go b/server/service/mongo/kv/tool.go
index c1a3f47..6852cfe 100644
--- a/server/service/mongo/kv/tool.go
+++ b/server/service/mongo/kv/tool.go
@@ -20,6 +20,7 @@ package kv
 import (
        "context"
        "github.com/apache/servicecomb-kie/server/service"
+       "reflect"
 
        "github.com/apache/servicecomb-kie/pkg/model"
        "github.com/go-mesh/openlogging"
@@ -38,19 +39,18 @@ func clearPart(kv *model.KVDoc) {
 }
 func cursorToOneKV(ctx context.Context, cur *mongo.Cursor, labels 
map[string]string) ([]*model.KVResponse, error) {
        result := make([]*model.KVResponse, 0)
-       curKV := &model.KVDoc{} //reuse this pointer to reduce GC, only clear 
label
        //check label length to get the exact match
        for cur.Next(ctx) { //although complexity is O(n), but there won't be 
so much labels for one key
                if cur.Err() != nil {
                        return nil, cur.Err()
                }
-               curKV.Labels = nil
+               curKV := &model.KVDoc{}
                err := cur.Decode(curKV)
                if err != nil {
                        openlogging.Error("decode error: " + err.Error())
                        return nil, err
                }
-               if len(curKV.Labels) == len(labels) {
+               if reflect.DeepEqual(curKV.Labels, labels) {
                        openlogging.Debug("hit exact labels")
                        labelGroup := &model.KVResponse{
                                LabelDoc: &model.LabelDocResponse{
diff --git a/server/service/mongo/label/label_dao.go 
b/server/service/mongo/label/label_dao.go
index 5a75283..5fb14aa 100644
--- a/server/service/mongo/label/label_dao.go
+++ b/server/service/mongo/label/label_dao.go
@@ -97,7 +97,7 @@ func CreateLabel(ctx context.Context, label *model.LabelDoc) 
(*model.LabelDoc, e
        return label, nil
 }
 
-//UpdateLabel
+//UpdateLabel update alias
 func UpdateLabel(ctx context.Context, label *model.LabelDoc) (*model.LabelDoc, 
error) {
        collection := session.GetDB().Collection(session.CollectionLabel)
        queryFilter := bson.M{"id": label.ID}
diff --git a/server/service/mongo/label/label_service.go 
b/server/service/mongo/label/label_service.go
index 1ae2904..5b0d934 100644
--- a/server/service/mongo/label/label_service.go
+++ b/server/service/mongo/label/label_service.go
@@ -5,9 +5,11 @@ import (
        "github.com/apache/servicecomb-kie/pkg/model"
 )
 
+//Service is db service
 type Service struct {
 }
 
+//CreateOrUpdate create or update labels
 func (s *Service) CreateOrUpdate(ctx context.Context, label *model.LabelDoc) 
(*model.LabelDoc, error) {
        if label.ID != "" {
                return UpdateLabel(ctx, label)
diff --git a/server/service/mongo/session/session.go 
b/server/service/mongo/session/session.go
index 58f9289..199a8bf 100644
--- a/server/service/mongo/session/session.go
+++ b/server/service/mongo/session/session.go
@@ -46,9 +46,9 @@ const (
        CollectionKV         = "kv"
        CollectionKVRevision = "kv_revision"
        CollectionCounter    = "counter"
-
-       DefaultTimeout   = 5 * time.Second
-       DefaultValueType = "text"
+       CollectionView       = "view"
+       DefaultTimeout       = 5 * time.Second
+       DefaultValueType     = "text"
 )
 
 //db errors
@@ -59,9 +59,14 @@ var (
        ErrTooMany         = errors.New("key with labels should be only one")
        ErrKeyMustNotEmpty = errors.New("must supply key if you want to get 
exact one result")
 
-       ErrKVIDIsNil              = errors.New("kvID id is nil")
+       ErrIDIsNil                = errors.New("id is empty")
        ErrKvIDAndLabelIDNotMatch = errors.New("kvID and labelID do not match")
        ErrRootCAMissing          = errors.New("rootCAFile is empty in config 
file")
+
+       ErrViewCreation = errors.New("can not create view")
+       ErrViewUpdate   = errors.New("can not update view")
+       ErrViewNotExist = errors.New("view not exists")
+       ErrViewFinding  = errors.New("view search error")
 )
 
 var client *mongo.Client
@@ -132,3 +137,18 @@ func Init() error {
 func GetDB() *mongo.Database {
        return db
 }
+
+//CreateView run mongo db command to create view
+func CreateView(ctx context.Context, view, source string, pipeline 
mongo.Pipeline) error {
+       sr := GetDB().RunCommand(ctx,
+               bson.D{
+                       {"create", view},
+                       {"viewOn", source},
+                       {"pipeline", pipeline},
+               })
+       if sr.Err() != nil {
+               openlogging.Error("can not create view: " + sr.Err().Error())
+               return ErrViewCreation
+       }
+       return nil
+}
diff --git a/server/service/mongo/view/view_dao.go 
b/server/service/mongo/view/view_dao.go
new file mode 100644
index 0000000..90d4314
--- /dev/null
+++ b/server/service/mongo/view/view_dao.go
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package view
+
+import (
+       "context"
+       "github.com/apache/servicecomb-kie/pkg/model"
+       "github.com/apache/servicecomb-kie/server/service/mongo/session"
+       "github.com/go-mesh/openlogging"
+       uuid "github.com/satori/go.uuid"
+       "go.mongodb.org/mongo-driver/bson"
+)
+
+func create(ctx context.Context, viewDoc *model.ViewDoc) error {
+       viewDoc.ID = uuid.NewV4().String()
+       viewDoc.Criteria = "" //TODO parse pipe line to sql-like lang
+       _, err := 
session.GetDB().Collection(session.CollectionView).InsertOne(ctx, viewDoc)
+       if err != nil {
+               openlogging.Error("can not insert view collection: " + 
err.Error())
+               return session.ErrViewCreation
+       }
+       return nil
+}
+func findOne(ctx context.Context, viewID, domain, project string) 
(*model.ViewDoc, error) {
+       filter := bson.M{"domain": domain,
+               "project": project,
+               "id":      viewID}
+       sr := session.GetDB().Collection(session.CollectionView).FindOne(ctx, 
filter)
+       if sr.Err() != nil {
+               openlogging.Error("can not insert view collection: " + 
sr.Err().Error())
+               return nil, sr.Err()
+       }
+       result := &model.ViewDoc{}
+       err := sr.Decode(result)
+       if err != nil {
+               openlogging.Error("decode error: " + err.Error())
+               return nil, err
+       }
+       if result.ID == viewID {
+               return result, nil
+       }
+       return nil, session.ErrViewNotExist
+}
diff --git a/server/service/mongo/view/view_service.go 
b/server/service/mongo/view/view_service.go
new file mode 100644
index 0000000..98c9f9b
--- /dev/null
+++ b/server/service/mongo/view/view_service.go
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package view
+
+import (
+       "context"
+       "github.com/apache/servicecomb-kie/pkg/model"
+       "github.com/apache/servicecomb-kie/server/service"
+       "github.com/apache/servicecomb-kie/server/service/mongo/session"
+       "github.com/go-mesh/openlogging"
+       uuid "github.com/satori/go.uuid"
+       "go.mongodb.org/mongo-driver/bson"
+       "go.mongodb.org/mongo-driver/mongo"
+       "go.mongodb.org/mongo-driver/mongo/options"
+       "time"
+)
+
+//Service operate data in mongodb
+type Service struct {
+       timeout time.Duration
+}
+
+//Create insert a view data and create a mongo db view
+func (s *Service) Create(ctx context.Context, viewDoc *model.ViewDoc, options 
...service.FindOption) (*model.ViewDoc, error) {
+       if viewDoc.Domain == "" {
+               return nil, session.ErrMissingDomain
+       }
+       var pipeline mongo.Pipeline = []bson.D{
+               {{"$match", bson.D{{"domain", viewDoc.Domain}}}},
+               {{"$match", bson.D{{"project", viewDoc.Project}}}},
+       }
+       opts := service.FindOptions{}
+       for _, o := range options {
+               o(&opts)
+       }
+       if opts.Key != "" {
+               pipeline = append(pipeline, bson.D{{"$match", bson.D{{"key", 
opts.Key}}}})
+       }
+       if len(opts.Labels) != 0 {
+               for k, v := range opts.Labels {
+                       pipeline = append(pipeline, bson.D{{"$match", 
bson.D{{"labels." + k, v}}}})
+               }
+       }
+       viewDoc.ID = uuid.NewV4().String()
+       viewDoc.Criteria = "" //TODO parse pipe line to sql-like lang
+       err := create(ctx, viewDoc)
+       if err != nil {
+               openlogging.Error("can not insert view collection: " + 
err.Error())
+               return nil, session.ErrViewCreation
+       }
+       err = session.CreateView(ctx, generateViewName(viewDoc.ID, 
viewDoc.Domain, viewDoc.Project), session.CollectionKV, pipeline)
+       if err != nil {
+               openlogging.Error("can not create view: " + err.Error())
+               return nil, session.ErrViewCreation
+       }
+       return viewDoc, nil
+}
+
+//Update is only able to update name and criteria
+func (s *Service) Update(ctx context.Context, viewDoc *model.ViewDoc) error {
+       if viewDoc.Domain == "" {
+               return session.ErrMissingDomain
+       }
+       if viewDoc.Project == "" {
+               return session.ErrMissingProject
+       }
+       if viewDoc.ID == "" {
+               return session.ErrIDIsNil
+       }
+       oldView, err := findOne(ctx, viewDoc.ID, viewDoc.Domain, 
viewDoc.Project)
+       if err != nil {
+               return err
+       }
+       if viewDoc.Display != "" {
+               oldView.Display = viewDoc.Display
+       }
+       if viewDoc.Criteria != "" {
+               oldView.Criteria = viewDoc.Criteria
+       }
+       _, err = 
session.GetDB().Collection(session.CollectionView).UpdateOne(ctx, 
bson.M{"domain": oldView.Domain,
+               "project": oldView.Project,
+               "id":      oldView.ID},
+               bson.D{
+                       {"$set", bson.D{
+                               {"name", oldView.Display},
+                               {"criteria", oldView.Criteria},
+                       }},
+               })
+       if err != nil {
+               openlogging.Error("can not update view: " + err.Error())
+               return session.ErrViewUpdate
+       }
+       //TODO delete and create a new view
+       return nil
+}
+
+//List return all view user created
+func (s *Service) List(ctx context.Context, domain, project string, opts 
...service.FindOption) (*model.ViewResponse, error) {
+       option := service.FindOptions{}
+       for _, o := range opts {
+               o(&option)
+       }
+       collection := session.GetDB().Collection(session.CollectionView)
+       filter := bson.M{"domain": domain, "project": project}
+       mOpt := options.Find()
+       if option.Limit != 0 {
+               mOpt = mOpt.SetLimit(option.Limit)
+       }
+       if option.Offset != 0 {
+               mOpt = mOpt.SetSkip(option.Offset)
+       }
+       cur, err := collection.Find(ctx, filter, mOpt)
+       if err != nil {
+               openlogging.Error("can not find view: " + err.Error())
+               return nil, session.ErrViewFinding
+       }
+       result := &model.ViewResponse{}
+       for cur.Next(ctx) {
+               v := &model.ViewDoc{}
+               if err := cur.Decode(v); err != nil {
+                       openlogging.Error("decode error: " + err.Error())
+                       return nil, err
+               }
+               result.Data = append(result.Data, v)
+       }
+       return result, nil
+}
+
+//GetContent query view's kv data
+func (s *Service) GetContent(ctx context.Context, id, domain, project string, 
opts ...service.FindOption) (*model.KVResponse, error) {
+       option := service.FindOptions{}
+       for _, o := range opts {
+               o(&option)
+       }
+       mOpt := options.Find()
+       if option.Limit != 0 {
+               mOpt = mOpt.SetLimit(option.Limit)
+       }
+       if option.Offset != 0 {
+               mOpt = mOpt.SetSkip(option.Offset)
+       }
+       collection := session.GetDB().Collection(generateViewName(id, domain, 
project))
+       cur, err := collection.Find(ctx, bson.D{}, mOpt)
+       if err != nil {
+               openlogging.Error("can not find view content: " + err.Error())
+               return nil, session.ErrViewFinding
+       }
+       result := &model.KVResponse{}
+       for cur.Next(ctx) {
+               v := &model.KVDoc{}
+               if err := cur.Decode(v); err != nil {
+                       openlogging.Error("decode error: " + err.Error())
+                       return nil, err
+               }
+               result.Data = append(result.Data, v)
+       }
+       return result, nil
+}
+
+func generateViewName(id, domain, project string) string {
+       return domain + "::" + project + "::" + id
+}
diff --git a/server/service/mongo/view/view_service_test.go 
b/server/service/mongo/view/view_service_test.go
new file mode 100644
index 0000000..58d5937
--- /dev/null
+++ b/server/service/mongo/view/view_service_test.go
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package view_test
+
+import (
+       "context"
+       "github.com/apache/servicecomb-kie/pkg/model"
+       "github.com/apache/servicecomb-kie/server/config"
+       "github.com/apache/servicecomb-kie/server/service"
+       "github.com/apache/servicecomb-kie/server/service/mongo/kv"
+       "github.com/apache/servicecomb-kie/server/service/mongo/session"
+       "github.com/apache/servicecomb-kie/server/service/mongo/view"
+       "github.com/stretchr/testify/assert"
+       "testing"
+)
+
+func TestGet(t *testing.T) {
+       var err error
+       config.Configurations = &config.Config{DB: config.DB{URI: 
"mongodb://kie:[email protected]:27017/kie"}}
+       err = session.Init()
+       assert.NoError(t, err)
+       kvsvc := &kv.Service{}
+       t.Run("put view data", func(t *testing.T) {
+               kv, err := kvsvc.CreateOrUpdate(context.TODO(), &model.KVDoc{
+                       Key:   "timeout",
+                       Value: "2s",
+                       Labels: map[string]string{
+                               "app":     "mall",
+                               "service": "cart",
+                               "view":    "view_test",
+                       },
+                       Domain:  "default",
+                       Project: "view_test",
+               })
+               assert.NoError(t, err)
+               assert.NotEmpty(t, kv.ID)
+
+               kv, err = kvsvc.CreateOrUpdate(context.TODO(), &model.KVDoc{
+                       Key:   "timeout",
+                       Value: "2s",
+                       Labels: map[string]string{
+                               "app": "mall",
+                       },
+                       Domain:  "default",
+                       Project: "view_test",
+               })
+               assert.NoError(t, err)
+               assert.NotEmpty(t, kv.ID)
+
+               kv, err = kvsvc.CreateOrUpdate(context.TODO(), &model.KVDoc{
+                       Key:   "retry",
+                       Value: "2",
+                       Labels: map[string]string{
+                               "app": "mall",
+                       },
+                       Domain:  "default",
+                       Project: "view_test",
+               })
+               assert.NoError(t, err)
+               assert.NotEmpty(t, kv.ID)
+       })
+
+       svc := &view.Service{}
+       t.Run("create and get view content", func(t *testing.T) {
+               view1, err := svc.Create(context.TODO(), &model.ViewDoc{
+                       Display: "timeout_config",
+                       Project: "view_test",
+                       Domain:  "default",
+               }, service.WithKey("timeout"))
+               assert.NoError(t, err)
+               assert.NotEmpty(t, view1.ID)
+               view2, err := svc.Create(context.TODO(), &model.ViewDoc{
+                       Display: "mall_config",
+                       Project: "view_test",
+                       Domain:  "default",
+               }, service.WithLabels(map[string]string{
+                       "app": "mall",
+               }))
+               assert.NoError(t, err)
+               assert.NotEmpty(t, view2.ID)
+
+               resp1, err := svc.GetContent(context.TODO(), view1.ID, 
"default", "view_test")
+               assert.NoError(t, err)
+               assert.Equal(t, 2, len(resp1.Data))
+               assert.Equal(t, "timeout", resp1.Data[0].Key)
+
+               resp2, err := svc.GetContent(context.TODO(), view2.ID, 
"default", "view_test")
+               assert.NoError(t, err)
+               assert.Equal(t, "mall", resp1.Data[0].Labels["app"])
+               t.Log(resp2.Data)
+       })
+       t.Run(" list view", func(t *testing.T) {
+               r, err := svc.List(context.TODO(), "default", "view_test")
+               assert.NoError(t, err)
+               assert.Equal(t, 2, len(r.Data))
+       })
+
+}
diff --git a/server/service/service.go b/server/service/service.go
index 60945f3..a26e697 100644
--- a/server/service/service.go
+++ b/server/service/service.go
@@ -28,13 +28,13 @@ var (
        KVService       KV
        HistoryService  History
        RevisionService Revision
-       DBInit          Init
        LabelService    Label
+       DBInit          Init
 )
 
 //db errors
 var (
-       ErrKeyNotExists     = errors.New("key with labels does not exits")
+       ErrKeyNotExists     = errors.New("can not find any key value")
        ErrRevisionNotExist = errors.New("revision does not exist")
        ErrAliasNotGiven    = errors.New("label alias not given")
 )
@@ -53,13 +53,25 @@ type KV interface {
 type History interface {
        GetHistory(ctx context.Context, keyID string, options ...FindOption) 
([]*model.KVDoc, error)
 }
+
+//Revision is global revision number management
 type Revision interface {
        GetRevision(ctx context.Context, domain string) (int64, error)
 }
 
+//Label manages labels data
 type Label interface {
        CreateOrUpdate(ctx context.Context, label *model.LabelDoc) 
(*model.LabelDoc, error)
 }
 
+//View create update and get view data
+type View interface {
+       Create(ctx context.Context, viewDoc *model.ViewDoc, options 
...FindOption) error
+       Update(ctx context.Context, viewDoc *model.ViewDoc) error
+       //TODO
+       //List(ctx context.Context, domain, project string, options 
...FindOption) ([]*model.ViewDoc, error)
+       GetContent(ctx context.Context, id, domain, project string, options 
...FindOption) ([]*model.KVResponse, error)
+}
+
 //Init init db session
 type Init func() error

Reply via email to