diff --git a/api/doc.go b/api/doc.go index 1a2bda9..fac42ae 100644 --- a/api/doc.go +++ b/api/doc.go @@ -35,6 +35,10 @@ func (s *HttpServer) store() gin.HandlerFunc { c.JSON(400, gin.H{"error": err.Error()}) return } + if err := body.Valid(); err != nil { + c.JSON(400, gin.H{"error": err.Error()}) + return + } body.Namespace = namespace body.EntryId = entryId // store the document @@ -79,6 +83,10 @@ func (s *HttpServer) get() gin.HandlerFunc { c.String(500, fmt.Sprintf("get document error: %s", err)) return } + if document == nil { + c.String(404, fmt.Sprintf("document not found: %s", entryId)) + return + } docWithAttr := &DocumentWithAttr{ Document: document, } @@ -102,7 +110,7 @@ func (s *HttpServer) get() gin.HandlerFunc { } } - c.JSON(200, attrs) + c.JSON(200, docWithAttr) } } @@ -170,8 +178,8 @@ func (s *HttpServer) filter() gin.HandlerFunc { if fuzzyName != "" { docQuery.FuzzyName = &fuzzyName } - if c.Query("unread") != "" { - docQuery.UnRead = utils.ToPtr(c.Query("unread") == "true") + if c.Query("unRead") != "" { + docQuery.UnRead = utils.ToPtr(c.Query("unRead") == "true") } if c.Query("mark") != "" { docQuery.Mark = utils.ToPtr(c.Query("mark") == "true") @@ -191,10 +199,10 @@ func (s *HttpServer) filter() gin.HandlerFunc { c.String(500, fmt.Sprintf("list document attrs error: %s", err)) return } - attrsMap := map[string][]doc.DocumentAttr{} + attrsMap := map[string][]*doc.DocumentAttr{} for _, attr := range allAttrs { if attrsMap[attr.EntryId] == nil { - attrsMap[attr.EntryId] = []doc.DocumentAttr{} + attrsMap[attr.EntryId] = []*doc.DocumentAttr{} } attrsMap[attr.EntryId] = append(attrsMap[attr.EntryId], attr) } @@ -226,21 +234,21 @@ func (s *HttpServer) filter() gin.HandlerFunc { func (s *HttpServer) delete() gin.HandlerFunc { return func(c *gin.Context) { namespace := c.Param("namespace") - queries := []doc.AttrQuery{} + queries := []*doc.AttrQuery{} entryId := c.Param("entryId") queries = append(queries, - doc.AttrQuery{ + &doc.AttrQuery{ Attr: "entryId", Option: "=", Value: entryId, }, - doc.AttrQuery{ + &doc.AttrQuery{ Attr: "namespace", Option: "=", Value: namespace, }, ) - if err := s.chain.DeleteByFilter(c, queries); err != nil { + if err := s.chain.DeleteByFilter(c, doc.DocumentAttrQuery{AttrQueries: queries}); err != nil { c.String(500, fmt.Sprintf("delete document error: %s", err)) return } diff --git a/api/request.go b/api/request.go index 53bb035..63c7bc7 100644 --- a/api/request.go +++ b/api/request.go @@ -17,6 +17,8 @@ package api import ( + "fmt" + "github.com/google/uuid" "github.com/basenana/friday/pkg/models/doc" @@ -38,6 +40,7 @@ func (r *DocRequest) ToDocument() *doc.Document { Id: uuid.New().String(), EntryId: r.EntryId, Name: r.Name, + Kind: "document", Namespace: r.Namespace, Source: r.Source, WebUrl: r.WebUrl, @@ -47,6 +50,16 @@ func (r *DocRequest) ToDocument() *doc.Document { } } +func (r *DocRequest) Valid() error { + if r.EntryId == "" || r.EntryId == "0" { + return fmt.Errorf("entryId is required") + } + if r.Namespace == "" { + return fmt.Errorf("namespace is required") + } + return nil +} + type DocAttrRequest struct { Namespace string `json:"namespace"` EntryId string `json:"entryId,omitempty"` @@ -121,55 +134,55 @@ func (q *DocQuery) ToQuery() *doc.DocumentQuery { Asc: !q.Desc, }}, } - attrQueries := []doc.AttrQuery{{ + attrQueries := []*doc.AttrQuery{{ Attr: "namespace", Option: "=", Value: q.Namespace, }} if q.Source != "" { - attrQueries = append(attrQueries, doc.AttrQuery{ + attrQueries = append(attrQueries, &doc.AttrQuery{ Attr: "source", Option: "=", Value: q.Source, }) } if q.WebUrl != "" { - attrQueries = append(attrQueries, doc.AttrQuery{ + attrQueries = append(attrQueries, &doc.AttrQuery{ Attr: "webUrl", Option: "=", Value: q.WebUrl, }) } if q.CreatedAtStart != nil { - attrQueries = append(attrQueries, doc.AttrQuery{ + attrQueries = append(attrQueries, &doc.AttrQuery{ Attr: "createdAt", Option: ">=", Value: *q.CreatedAtStart, }) } if q.ChangedAtStart != nil { - attrQueries = append(attrQueries, doc.AttrQuery{ + attrQueries = append(attrQueries, &doc.AttrQuery{ Attr: "updatedAt", Option: ">=", Value: *q.ChangedAtStart, }) } if q.CreatedAtEnd != nil { - attrQueries = append(attrQueries, doc.AttrQuery{ + attrQueries = append(attrQueries, &doc.AttrQuery{ Attr: "createdAt", Option: "<=", Value: *q.CreatedAtEnd, }) } if q.ChangedAtEnd != nil { - attrQueries = append(attrQueries, doc.AttrQuery{ + attrQueries = append(attrQueries, &doc.AttrQuery{ Attr: "updatedAt", Option: "<=", Value: *q.ChangedAtEnd, }) } if q.FuzzyName != nil { - attrQueries = append(attrQueries, doc.AttrQuery{ + attrQueries = append(attrQueries, &doc.AttrQuery{ Attr: "name", Option: "CONTAINS", Value: *q.FuzzyName, @@ -184,7 +197,7 @@ func (q *DocQuery) GetAttrQueries() []*doc.DocumentAttrQuery { attrQueries := []*doc.DocumentAttrQuery{} if q.UnRead != nil { attrQueries = append(attrQueries, &doc.DocumentAttrQuery{ - AttrQueries: []doc.AttrQuery{ + AttrQueries: []*doc.AttrQuery{ { Attr: "namespace", Option: "=", @@ -205,7 +218,7 @@ func (q *DocQuery) GetAttrQueries() []*doc.DocumentAttrQuery { } if q.Mark != nil { attrQueries = append(attrQueries, &doc.DocumentAttrQuery{ - AttrQueries: []doc.AttrQuery{ + AttrQueries: []*doc.AttrQuery{ { Attr: "namespace", Option: "=", @@ -226,7 +239,7 @@ func (q *DocQuery) GetAttrQueries() []*doc.DocumentAttrQuery { } if q.ParentID != "" { attrQueries = append(attrQueries, &doc.DocumentAttrQuery{ - AttrQueries: []doc.AttrQuery{ + AttrQueries: []*doc.AttrQuery{ { Attr: "namespace", Option: "=", @@ -249,7 +262,7 @@ func (q *DocQuery) GetAttrQueries() []*doc.DocumentAttrQuery { } type DocumentWithAttr struct { - doc.Document + *doc.Document ParentID string `json:"parentId,omitempty"` UnRead *bool `json:"unRead,omitempty"` diff --git a/pkg/models/doc/attr.go b/pkg/models/doc/attr.go new file mode 100644 index 0000000..ee36584 --- /dev/null +++ b/pkg/models/doc/attr.go @@ -0,0 +1,56 @@ +/* + Copyright 2024 Friday Author. + + Licensed 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 doc + +import "fmt" + +type DocumentAttr struct { + Id string `json:"id"` + Kind string `json:"kind"` + Namespace string `json:"namespace"` + EntryId string `json:"entryId"` + Key string `json:"key"` + Value interface{} `json:"value"` +} + +var _ DocPtrInterface = &DocumentAttr{} + +func (d *DocumentAttr) ID() string { + return d.Id +} + +func (d *DocumentAttr) EntryID() string { + return d.EntryId +} + +func (d *DocumentAttr) Type() string { + return "attr" +} + +func (d *DocumentAttr) String() string { + return fmt.Sprintf("EntryId(%s) %s: %v", d.EntryId, d.Key, d.Value) +} + +type DocumentAttrList []*DocumentAttr + +func (d DocumentAttrList) String() string { + result := "" + for _, attr := range d { + result += fmt.Sprintf("EntryId(%s) %s: %v\n", attr.EntryId, attr.Key, attr.Value) + } + return result +} diff --git a/pkg/models/doc/document.go b/pkg/models/doc/document.go index 5432778..3104762 100644 --- a/pkg/models/doc/document.go +++ b/pkg/models/doc/document.go @@ -17,10 +17,7 @@ package doc import ( - "encoding/json" "fmt" - - "github.com/meilisearch/meilisearch-go" ) var ( @@ -65,99 +62,18 @@ func (d *Document) Type() string { return "document" } -type DocumentAttr struct { - Id string `json:"id"` - Kind string `json:"kind"` - Namespace string `json:"namespace"` - EntryId string `json:"entryId"` - Key string `json:"key"` - Value interface{} `json:"value"` -} - -func (d *DocumentAttr) ID() string { - return d.Id -} - -func (d *DocumentAttr) EntryID() string { - return d.EntryId -} - -func (d *DocumentAttr) Type() string { - return "attr" -} - -var _ DocPtrInterface = &Document{} -var _ DocPtrInterface = &DocumentAttr{} - -type DocumentQuery struct { - AttrQueries []AttrQuery - - Search string - HitsPerPage int64 - Page int64 - Offset int64 - Limit int64 - Sort []Sort +func (d *Document) String() string { + return fmt.Sprintf("EntryId(%s) %s", d.EntryId, d.Name) } -type Sort struct { - Attr string - Asc bool -} +type DocumentList []*Document -func (s *Sort) String() string { - if s.Asc { - return fmt.Sprintf("%s:asc", s.Attr) +func (d DocumentList) String() string { + result := "" + for _, doc := range d { + result += fmt.Sprintf("EntryId(%s) %s\n", doc.EntryId, doc.Name) } - return fmt.Sprintf("%s:desc", s.Attr) + return result } -type DocumentAttrQuery struct { - AttrQueries []AttrQuery -} - -type AttrQuery struct { - Attr string - Option string - Value interface{} -} - -func (aq *AttrQuery) ToFilter() interface{} { - vs, _ := json.Marshal(aq.Value) - return fmt.Sprintf("%s %s %s", aq.Attr, aq.Option, vs) -} - -func (q *DocumentQuery) ToRequest() *meilisearch.SearchRequest { - // build filter - filter := []interface{}{} - for _, aq := range q.AttrQueries { - filter = append(filter, aq.ToFilter()) - } - sorts := []string{} - for _, s := range q.Sort { - sorts = append(sorts, s.String()) - } - - return &meilisearch.SearchRequest{ - Offset: q.Offset, - Limit: q.Limit, - Sort: sorts, - HitsPerPage: q.HitsPerPage, - Page: q.Page, - Query: q.Search, - Filter: filter, - } -} - -func (q *DocumentAttrQuery) ToRequest() *meilisearch.SearchRequest { - filter := []interface{}{} - for _, aq := range q.AttrQueries { - filter = append(filter, aq.ToFilter()) - } - return &meilisearch.SearchRequest{ - Filter: filter, - Limit: 10000, - HitsPerPage: 10000, - Query: "", - } -} +var _ DocPtrInterface = &Document{} diff --git a/pkg/models/doc/query.go b/pkg/models/doc/query.go new file mode 100644 index 0000000..7c64404 --- /dev/null +++ b/pkg/models/doc/query.go @@ -0,0 +1,117 @@ +/* + Copyright 2024 Friday Author. + + Licensed 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 doc + +import ( + "encoding/json" + "fmt" + + "github.com/meilisearch/meilisearch-go" +) + +type DocumentQuery struct { + AttrQueries []*AttrQuery + + Search string + HitsPerPage int64 + Page int64 + Offset int64 + Limit int64 + Sort []Sort +} + +type Sort struct { + Attr string + Asc bool +} + +func (s *Sort) String() string { + if s.Asc { + return fmt.Sprintf("%s:asc", s.Attr) + } + return fmt.Sprintf("%s:desc", s.Attr) +} + +type DocumentAttrQuery struct { + AttrQueries []*AttrQuery +} + +func (q *DocumentAttrQuery) String() string { + result := "" + for _, aq := range q.AttrQueries { + result += aq.String() + " " + } + return result +} + +type AttrQuery struct { + Attr string + Option string + Value interface{} +} + +func (aq *AttrQuery) ToFilter() interface{} { + vs, _ := json.Marshal(aq.Value) + return fmt.Sprintf("%s %s %s", aq.Attr, aq.Option, vs) +} + +func (aq *AttrQuery) String() string { + return aq.ToFilter().(string) +} + +func (q *DocumentQuery) String() string { + filters := "" + for _, aq := range q.AttrQueries { + filters += aq.String() + " " + } + return fmt.Sprintf("search: [%s], attr query: [%s]", q.Search, filters) +} + +func (q *DocumentQuery) ToRequest() *meilisearch.SearchRequest { + // build filter + filter := []interface{}{} + for _, aq := range q.AttrQueries { + filter = append(filter, aq.ToFilter()) + } + sorts := []string{} + for _, s := range q.Sort { + sorts = append(sorts, s.String()) + } + + return &meilisearch.SearchRequest{ + Offset: q.Offset, + Limit: q.Limit, + Sort: sorts, + HitsPerPage: q.HitsPerPage, + Page: q.Page, + Query: q.Search, + Filter: filter, + } +} + +func (q *DocumentAttrQuery) ToRequest() *meilisearch.SearchRequest { + filter := []interface{}{} + for _, aq := range q.AttrQueries { + filter = append(filter, aq.ToFilter()) + } + return &meilisearch.SearchRequest{ + Filter: filter, + Limit: 10000, + HitsPerPage: 10000, + Query: "", + } +} diff --git a/pkg/service/chain.go b/pkg/service/chain.go index a12f385..71bad69 100644 --- a/pkg/service/chain.go +++ b/pkg/service/chain.go @@ -19,16 +19,20 @@ package service import ( "context" + "go.uber.org/zap" + "github.com/basenana/friday/config" "github.com/basenana/friday/pkg/dispatch" "github.com/basenana/friday/pkg/dispatch/plugin" "github.com/basenana/friday/pkg/models/doc" "github.com/basenana/friday/pkg/store/docstore" + "github.com/basenana/friday/pkg/utils/logger" ) type Chain struct { MeiliClient docstore.DocStoreInterface Plugins []plugin.ChainPlugin + Log *zap.SugaredLogger } var ChainPool *dispatch.Pool @@ -38,56 +42,80 @@ func NewChain(conf config.Config) (*Chain, error) { for _, p := range conf.Plugins { plugins = append(plugins, plugin.DefaultRegisterer.Get(p)) } + log := logger.NewLog("chain") client, err := docstore.NewMeiliClient(conf) if err != nil { + log.Errorf("new meili client error: %s", err) return nil, err } return &Chain{ MeiliClient: client, Plugins: plugins, + Log: log, }, nil } func (c *Chain) Store(ctx context.Context, document *doc.Document) error { + document.Kind = "document" return ChainPool.Run(ctx, func(ctx context.Context) error { + c.Log.Debugf("store document: %+v", document.String()) + if d, err := c.GetDocument(ctx, document.Namespace, document.EntryId); err != nil { + c.Log.Errorf("get document error: %s", err) + return err + } else if d != nil { + c.Log.Debugf("document already exists: %+v", d.String()) + return nil + } for _, plugin := range c.Plugins { err := plugin.Run(ctx, document) if err != nil { + c.Log.Errorf("plugin error: %s", err) return err } } + c.Log.Debugf("store document: %+v", document.String()) return c.MeiliClient.Store(ctx, document) }) } func (c *Chain) StoreAttr(ctx context.Context, docAttr *doc.DocumentAttr) error { return ChainPool.Run(ctx, func(ctx context.Context) error { - if err := c.MeiliClient.DeleteByFilter(ctx, []doc.AttrQuery{ - { - Attr: "namespace", - Option: "=", - Value: docAttr.Namespace, - }, - { - Attr: "key", - Option: "=", - Value: docAttr.Key, - }, - { - Attr: "entryId", - Option: "=", - Value: docAttr.EntryId, - }, + if err := c.MeiliClient.DeleteByFilter(ctx, doc.DocumentAttrQuery{ + AttrQueries: []*doc.AttrQuery{ + { + Attr: "namespace", + Option: "=", + Value: docAttr.Namespace, + }, + { + Attr: "key", + Option: "=", + Value: docAttr.Key, + }, + { + Attr: "entryId", + Option: "=", + Value: docAttr.EntryId, + }, + { + Attr: "kind", + Option: "=", + Value: "attr", + }}, }); err != nil { + c.Log.Errorf("delete document attr error: %s", err) return err } + docAttr.Kind = "attr" + c.Log.Debugf("store attr: %+v", docAttr.String()) return c.MeiliClient.Store(ctx, docAttr) }) } -func (c *Chain) GetDocument(ctx context.Context, namespace, entryId string) (doc.Document, error) { +func (c *Chain) GetDocument(ctx context.Context, namespace, entryId string) (*doc.Document, error) { + c.Log.Debugf("get document: namespace=%s, entryId=%s", namespace, entryId) docs, err := c.MeiliClient.Search(ctx, &doc.DocumentQuery{ - AttrQueries: []doc.AttrQuery{ + AttrQueries: []*doc.AttrQuery{ { Attr: "namespace", Option: "=", @@ -104,19 +132,25 @@ func (c *Chain) GetDocument(ctx context.Context, namespace, entryId string) (doc Value: "document", }, }, + Search: "", + HitsPerPage: 1, + Page: 1, }) if err != nil { - return doc.Document{}, err + c.Log.Errorf("get document error: %s", err) + return nil, err } if len(docs) == 0 { - return doc.Document{}, nil + c.Log.Debugf("document not found: namespace=%s, entryId=%s", namespace, entryId) + return nil, nil } + c.Log.Debugf("get document: %+v", docs[0].String()) return docs[0], nil } -func (c *Chain) ListDocumentAttrs(ctx context.Context, namespace string, entryIds []string) ([]doc.DocumentAttr, error) { - attrs, err := c.MeiliClient.FilterAttr(ctx, &doc.DocumentAttrQuery{ - AttrQueries: []doc.AttrQuery{ +func (c *Chain) ListDocumentAttrs(ctx context.Context, namespace string, entryIds []string) (doc.DocumentAttrList, error) { + docAttrQuery := &doc.DocumentAttrQuery{ + AttrQueries: []*doc.AttrQuery{ { Attr: "namespace", Option: "=", @@ -133,16 +167,20 @@ func (c *Chain) ListDocumentAttrs(ctx context.Context, namespace string, entryId Value: "attr", }, }, - }) + } + c.Log.Debugf("list document attrs: %+v", docAttrQuery.String()) + attrs, err := c.MeiliClient.FilterAttr(ctx, docAttrQuery) if err != nil { + c.Log.Errorf("list document attrs error: %s", err) return nil, err } + c.Log.Debugf("list %d document attrs: %s", len(attrs), attrs.String()) return attrs, nil } -func (c *Chain) GetDocumentAttrs(ctx context.Context, namespace, entryId string) ([]doc.DocumentAttr, error) { - attrs, err := c.MeiliClient.FilterAttr(ctx, &doc.DocumentAttrQuery{ - AttrQueries: []doc.AttrQuery{ +func (c *Chain) GetDocumentAttrs(ctx context.Context, namespace, entryId string) ([]*doc.DocumentAttr, error) { + docAttrQuery := &doc.DocumentAttrQuery{ + AttrQueries: []*doc.AttrQuery{ { Attr: "namespace", Option: "=", @@ -159,52 +197,71 @@ func (c *Chain) GetDocumentAttrs(ctx context.Context, namespace, entryId string) Value: "attr", }, }, - }) + } + c.Log.Debugf("get document attrs: %+v", docAttrQuery.String()) + attrs, err := c.MeiliClient.FilterAttr(ctx, docAttrQuery) if err != nil { + c.Log.Errorf("get document attrs error: %s", err) return nil, err } + c.Log.Debugf("get %d document attrs: %s", len(attrs), attrs.String()) return attrs, nil } -func (c *Chain) Search(ctx context.Context, query *doc.DocumentQuery, attrQueries []*doc.DocumentAttrQuery) ([]doc.Document, error) { - attrs := []doc.DocumentAttr{} +func (c *Chain) Search(ctx context.Context, query *doc.DocumentQuery, attrQueries []*doc.DocumentAttrQuery) ([]*doc.Document, error) { + attrs := doc.DocumentAttrList{} for _, attrQuery := range attrQueries { - attrQuery.AttrQueries = append(attrQuery.AttrQueries, doc.AttrQuery{ + attrQuery.AttrQueries = append(attrQuery.AttrQueries, &doc.AttrQuery{ Attr: "kind", Option: "=", Value: "attr", }) + c.Log.Debugf("filter attr query: %+v", attrQuery.String()) attr, err := c.MeiliClient.FilterAttr(ctx, attrQuery) if err != nil { return nil, err } attrs = append(attrs, attr...) } + c.Log.Debugf("filter %d attrs: %s", len(attrs), attrs.String()) ids := []string{} for _, attr := range attrs { ids = append(ids, attr.EntryId) } if len(ids) == 0 && len(attrQueries) != 0 { - return []doc.Document{}, nil + return nil, nil } - query.AttrQueries = append(query.AttrQueries, doc.AttrQuery{ + query.AttrQueries = append(query.AttrQueries, &doc.AttrQuery{ Attr: "kind", Option: "=", Value: "document", }) if len(ids) != 0 { - query.AttrQueries = append(query.AttrQueries, doc.AttrQuery{ + query.AttrQueries = append(query.AttrQueries, &doc.AttrQuery{ Attr: "entryId", Option: "IN", Value: ids, }) } - return c.MeiliClient.Search(ctx, query) + c.Log.Debugf("search document query: %+v", query.String()) + docs, err := c.MeiliClient.Search(ctx, query) + if err != nil { + c.Log.Errorf("search document error: %s", err) + return nil, err + } + c.Log.Debugf("search %d documents: %s", len(docs), docs.String()) + return docs, nil } -func (c *Chain) DeleteByFilter(ctx context.Context, queries []doc.AttrQuery) error { +func (c *Chain) DeleteByFilter(ctx context.Context, queries doc.DocumentAttrQuery) error { return ChainPool.Run(ctx, func(ctx context.Context) error { - return c.MeiliClient.DeleteByFilter(ctx, queries) + c.Log.Debugf("delete by filter: %+v", queries.String()) + err := c.MeiliClient.DeleteByFilter(ctx, queries) + if err != nil { + c.Log.Errorf("delete by filter error: %s", err) + return err + } + return nil }) } diff --git a/pkg/service/chain_test.go b/pkg/service/chain_test.go index c9726c9..7c9c0c0 100644 --- a/pkg/service/chain_test.go +++ b/pkg/service/chain_test.go @@ -28,6 +28,7 @@ import ( "github.com/basenana/friday/pkg/models/doc" "github.com/basenana/friday/pkg/service" "github.com/basenana/friday/pkg/store/docstore" + "github.com/basenana/friday/pkg/utils/logger" ) var _ = Describe("Chain", func() { @@ -48,9 +49,11 @@ var _ = Describe("Chain", func() { BeforeEach(func() { service.ChainPool = dispatch.NewPool(10) + logger.InitLog() Chain = &service.Chain{ MeiliClient: &docstore.MockClient{}, Plugins: []plugin.ChainPlugin{}, + Log: logger.NewLog("test"), } for _, p := range plugin.DefaultRegisterer.Chains { Chain.Plugins = append(Chain.Plugins, p) @@ -137,7 +140,7 @@ var _ = Describe("Chain", func() { Context("search document", func() { It("search document should be successful", func() { docs, err := Chain.Search(context.TODO(), &doc.DocumentQuery{ - AttrQueries: []doc.AttrQuery{{ + AttrQueries: []*doc.AttrQuery{{ Attr: "namespace", Option: "=", Value: "test-ns", @@ -149,7 +152,7 @@ var _ = Describe("Chain", func() { }) It("search document with attr should be successful", func() { docs, err := Chain.Search(context.TODO(), &doc.DocumentQuery{ - AttrQueries: []doc.AttrQuery{{ + AttrQueries: []*doc.AttrQuery{{ Attr: "namespace", Option: "=", Value: "test-ns", @@ -157,7 +160,7 @@ var _ = Describe("Chain", func() { Search: "test", }, []*doc.DocumentAttrQuery{ { - AttrQueries: []doc.AttrQuery{ + AttrQueries: []*doc.AttrQuery{ { Attr: "parentId", Option: "=", @@ -206,21 +209,23 @@ var _ = Describe("Chain", func() { EntryId: "10", }) Expect(err).Should(BeNil()) - err = Chain.DeleteByFilter(context.TODO(), []doc.AttrQuery{ - { - Attr: "namespace", - Option: "=", - Value: "test-ns", - }, - { - Attr: "entryId", - Option: "=", - Value: "10", - }, - }) + err = Chain.DeleteByFilter(context.TODO(), doc.DocumentAttrQuery{ + AttrQueries: []*doc.AttrQuery{ + { + Attr: "namespace", + Option: "=", + Value: "test-ns", + }, + { + Attr: "entryId", + Option: "=", + Value: "10", + }, + }}, + ) Expect(err).Should(BeNil()) docs, err := Chain.Search(context.TODO(), &doc.DocumentQuery{ - AttrQueries: []doc.AttrQuery{{ + AttrQueries: []*doc.AttrQuery{{ Attr: "entryId", Option: "=", Value: "10", diff --git a/pkg/store/docstore/interface.go b/pkg/store/docstore/interface.go index b6703b3..282cc07 100644 --- a/pkg/store/docstore/interface.go +++ b/pkg/store/docstore/interface.go @@ -24,7 +24,7 @@ import ( type DocStoreInterface interface { Store(ctx context.Context, docPtr doc.DocPtrInterface) error - FilterAttr(ctx context.Context, query *doc.DocumentAttrQuery) ([]doc.DocumentAttr, error) - Search(ctx context.Context, query *doc.DocumentQuery) ([]doc.Document, error) - DeleteByFilter(ctx context.Context, aqs []doc.AttrQuery) error + FilterAttr(ctx context.Context, query *doc.DocumentAttrQuery) (doc.DocumentAttrList, error) + Search(ctx context.Context, query *doc.DocumentQuery) (doc.DocumentList, error) + DeleteByFilter(ctx context.Context, aqs doc.DocumentAttrQuery) error } diff --git a/pkg/store/docstore/meili.go b/pkg/store/docstore/meili.go index 719c7e7..f438d7d 100644 --- a/pkg/store/docstore/meili.go +++ b/pkg/store/docstore/meili.go @@ -85,15 +85,15 @@ func (c *MeiliClient) Store(ctx context.Context, docPtr doc.DocPtrInterface) err return nil } -func (c *MeiliClient) FilterAttr(ctx context.Context, query *doc.DocumentAttrQuery) ([]doc.DocumentAttr, error) { +func (c *MeiliClient) FilterAttr(ctx context.Context, query *doc.DocumentAttrQuery) (doc.DocumentAttrList, error) { rep, err := c.index.Search("", query.ToRequest()) if err != nil { return nil, err } - var attrs []doc.DocumentAttr + attrs := doc.DocumentAttrList{} for _, hit := range rep.Hits { b, _ := json.Marshal(hit) - var attr doc.DocumentAttr + attr := &doc.DocumentAttr{} err = json.Unmarshal(b, &attr) if err != nil { c.log.Errorf("unmarshal document attr error: %s", err) @@ -104,15 +104,15 @@ func (c *MeiliClient) FilterAttr(ctx context.Context, query *doc.DocumentAttrQue return attrs, nil } -func (c *MeiliClient) Search(ctx context.Context, query *doc.DocumentQuery) ([]doc.Document, error) { +func (c *MeiliClient) Search(ctx context.Context, query *doc.DocumentQuery) (doc.DocumentList, error) { rep, err := c.index.Search(query.Search, query.ToRequest()) if err != nil { return nil, err } - var documents []doc.Document + documents := doc.DocumentList{} for _, hit := range rep.Hits { b, _ := json.Marshal(hit) - var document doc.Document + document := &doc.Document{} err = json.Unmarshal(b, &document) if err != nil { c.log.Errorf("unmarshal document error: %s", err) @@ -149,9 +149,9 @@ func (c *MeiliClient) Delete(ctx context.Context, docId string) error { return nil } -func (c *MeiliClient) DeleteByFilter(ctx context.Context, aqs []doc.AttrQuery) error { +func (c *MeiliClient) DeleteByFilter(ctx context.Context, aqs doc.DocumentAttrQuery) error { filter := []interface{}{} - for _, aq := range aqs { + for _, aq := range aqs.AttrQueries { filter = append(filter, aq.ToFilter()) } diff --git a/pkg/store/docstore/mock.go b/pkg/store/docstore/mock.go index 7cfb326..a588a0e 100644 --- a/pkg/store/docstore/mock.go +++ b/pkg/store/docstore/mock.go @@ -25,8 +25,8 @@ import ( ) type MockClient struct { - docs []doc.Document - attrs []doc.DocumentAttr + docs []*doc.Document + attrs []*doc.DocumentAttr } var _ DocStoreInterface = &MockClient{} @@ -35,23 +35,23 @@ func (m *MockClient) Store(ctx context.Context, docPtr doc.DocPtrInterface) erro if docPtr.Type() == "document" { d := docPtr.(*doc.Document) if m.docs == nil { - m.docs = []doc.Document{} + m.docs = []*doc.Document{} } - m.docs = append(m.docs, *d) + m.docs = append(m.docs, d) } if docPtr.Type() == "attr" { d := docPtr.(*doc.DocumentAttr) if m.attrs == nil { - m.attrs = []doc.DocumentAttr{} + m.attrs = []*doc.DocumentAttr{} } - m.attrs = append(m.attrs, *d) + m.attrs = append(m.attrs, d) } return nil } -func (m *MockClient) FilterAttr(ctx context.Context, query *doc.DocumentAttrQuery) ([]doc.DocumentAttr, error) { +func (m *MockClient) FilterAttr(ctx context.Context, query *doc.DocumentAttrQuery) (doc.DocumentAttrList, error) { aq := query.AttrQueries - result := []doc.DocumentAttr{} + result := []*doc.DocumentAttr{} for _, attr := range m.attrs { matched := true all := len(aq) @@ -91,9 +91,9 @@ func (m *MockClient) FilterAttr(ctx context.Context, query *doc.DocumentAttrQuer return result, nil } -func (m *MockClient) Search(ctx context.Context, query *doc.DocumentQuery) ([]doc.Document, error) { +func (m *MockClient) Search(ctx context.Context, query *doc.DocumentQuery) (doc.DocumentList, error) { aq := query.AttrQueries - result := []doc.Document{} + result := []*doc.Document{} for _, d := range m.docs { matched := true all := len(aq) @@ -134,19 +134,19 @@ func (m *MockClient) Search(ctx context.Context, query *doc.DocumentQuery) ([]do return result, nil } -func (m *MockClient) DeleteByFilter(ctx context.Context, aqs []doc.AttrQuery) error { - attrs := make(map[string]doc.DocumentAttr) +func (m *MockClient) DeleteByFilter(ctx context.Context, aqs doc.DocumentAttrQuery) error { + attrs := make(map[string]*doc.DocumentAttr) for _, attr := range m.attrs { attrs[attr.Id] = attr } - docs := make(map[string]doc.Document) + docs := make(map[string]*doc.Document) for _, d := range m.docs { docs[d.Id] = d } for _, d := range m.docs { matched := true - all := len(aqs) - for _, q := range aqs { + all := len(aqs.AttrQueries) + for _, q := range aqs.AttrQueries { if q.Attr == "entryId" { all -= 1 if !match(q, d.EntryId) { @@ -182,8 +182,8 @@ func (m *MockClient) DeleteByFilter(ctx context.Context, aqs []doc.AttrQuery) er } for _, attr := range m.attrs { matched := true - all := len(aqs) - for _, aq := range aqs { + all := len(aqs.AttrQueries) + for _, aq := range aqs.AttrQueries { if attr.Key == aq.Attr { all -= 1 if !match(aq, attr.Value) { @@ -203,12 +203,12 @@ func (m *MockClient) DeleteByFilter(ctx context.Context, aqs []doc.AttrQuery) er delete(attrs, attr.Id) } } - var attrsSlice []doc.DocumentAttr + var attrsSlice []*doc.DocumentAttr for _, v := range attrs { attrsSlice = append(attrsSlice, v) } m.attrs = attrsSlice - var docsSlice []doc.Document + var docsSlice []*doc.Document for _, v := range docs { docsSlice = append(docsSlice, v) } @@ -216,7 +216,7 @@ func (m *MockClient) DeleteByFilter(ctx context.Context, aqs []doc.AttrQuery) er return nil } -func match[T string | interface{}](aq doc.AttrQuery, t T) bool { +func match[T string | interface{}](aq *doc.AttrQuery, t T) bool { if aq.Option == "=" && reflect.DeepEqual(t, aq.Value.(T)) { return true }