diff --git a/extensions/search/pkg/search/index/index.go b/extensions/search/pkg/search/index/index.go index bc06ba466..07797f7d4 100644 --- a/extensions/search/pkg/search/index/index.go +++ b/extensions/search/pkg/search/index/index.go @@ -165,15 +165,16 @@ func (i *Index) Purge(id *sprovider.ResourceId) error { } // Move update the path of an entry and all its children -func (i *Index) Move(ri *sprovider.ResourceInfo, fullPath string) error { - doc, err := i.getEntity(idToBleveId(ri.Id)) +func (i *Index) Move(id *sprovider.ResourceId, fullPath string) error { + bleveId := idToBleveId(id) + doc, err := i.getEntity(bleveId) if err != nil { return err } oldName := doc.Path newName := utils.MakeRelativePath(fullPath) - doc, err = i.updateEntity(idToBleveId(ri.Id), func(doc *indexDocument) { + doc, err = i.updateEntity(bleveId, func(doc *indexDocument) { doc.Path = newName doc.Name = path.Base(newName) }) @@ -211,7 +212,7 @@ func (i *Index) Search(ctx context.Context, req *searchsvc.SearchIndexRequest) ( deletedQuery := bleve.NewBoolFieldQuery(false) deletedQuery.SetField("Deleted") query := bleve.NewConjunctionQuery( - bleve.NewQueryStringQuery("Name:"+strings.ToLower(req.Query)), + bleve.NewQueryStringQuery(req.Query), deletedQuery, // Skip documents that have been marked as deleted bleve.NewQueryStringQuery("RootID:"+req.Ref.ResourceId.StorageId+"!"+req.Ref.ResourceId.OpaqueId), // Limit search to the space bleve.NewQueryStringQuery("Path:"+utils.MakeRelativePath(path.Join(req.Ref.Path, "/"))+"*"), // Limit search to this directory in the space diff --git a/extensions/search/pkg/search/index/index_test.go b/extensions/search/pkg/search/index/index_test.go index 7c46c64b2..582df6f4f 100644 --- a/extensions/search/pkg/search/index/index_test.go +++ b/extensions/search/pkg/search/index/index_test.go @@ -24,25 +24,9 @@ var _ = Describe("Index", func() { StorageId: "storageid", OpaqueId: "rootopaqueid", } - ref = &sprovider.Reference{ - ResourceId: rootId, - Path: "./foo.pdf", - } - ri = &sprovider.ResourceInfo{ - Id: &sprovider.ResourceId{ - StorageId: "storageid", - OpaqueId: "opaqueid", - }, - ParentId: &sprovider.ResourceId{ - StorageId: "storageid", - OpaqueId: "someopaqueid", - }, - Path: "Foo.pdf", - Size: 12345, - Type: sprovider.ResourceType_RESOURCE_TYPE_FILE, - MimeType: "application/pdf", - Mtime: &typesv1beta1.Timestamp{Seconds: 4000}, - } + filename string + ref *sprovider.Reference + ri *sprovider.ResourceInfo parentRef = &sprovider.Reference{ ResourceId: rootId, Path: "./my/sudbir", @@ -76,7 +60,7 @@ var _ = Describe("Index", func() { Mtime: &typesv1beta1.Timestamp{Seconds: 4000}, } - assertDocCount = func(rootId *sprovider.ResourceId, query string, expectedCount int) { + assertDocCount = func(rootId *sprovider.ResourceId, query string, expectedCount int) []*searchmsg.Match { res, err := i.Search(ctx, &searchsvc.SearchIndexRequest{ Query: query, Ref: &searchmsg.Reference{ @@ -87,11 +71,14 @@ var _ = Describe("Index", func() { }, }) ExpectWithOffset(1, err).ToNot(HaveOccurred()) - ExpectWithOffset(1, len(res.Matches)).To(Equal(expectedCount)) + ExpectWithOffset(1, len(res.Matches)).To(Equal(expectedCount), "query returned unexpected number of results: "+query) + return res.Matches } ) BeforeEach(func() { + filename = "Foo.pdf" + mapping, err := index.BuildMapping() Expect(err).ToNot(HaveOccurred()) @@ -102,6 +89,28 @@ var _ = Describe("Index", func() { Expect(err).ToNot(HaveOccurred()) }) + JustBeforeEach(func() { + ref = &sprovider.Reference{ + ResourceId: rootId, + Path: "./" + filename, + } + ri = &sprovider.ResourceInfo{ + Id: &sprovider.ResourceId{ + StorageId: "storageid", + OpaqueId: "opaqueid", + }, + ParentId: &sprovider.ResourceId{ + StorageId: "storageid", + OpaqueId: "someopaqueid", + }, + Path: filename, + Size: 12345, + Type: sprovider.ResourceType_RESOURCE_TYPE_FILE, + MimeType: "application/pdf", + Mtime: &typesv1beta1.Timestamp{Seconds: 4000}, + } + }) + Describe("New", func() { It("returns a new index instance", func() { i, err := index.New(bleveIndex) @@ -119,163 +128,139 @@ var _ = Describe("Index", func() { }) Describe("Search", func() { - Context("with a file in the root of the space", func() { - BeforeEach(func() { + Context("by other fields than filename", func() { + JustBeforeEach(func() { err := i.Add(ref, ri) Expect(err).ToNot(HaveOccurred()) }) - It("scopes the search to the specified space", func() { - res, err := i.Search(ctx, &searchsvc.SearchIndexRequest{ - Ref: &searchmsg.Reference{ - ResourceId: &searchmsg.ResourceID{ - StorageId: "differentstorageid", - OpaqueId: "differentopaqueid", - }, - }, - Query: "foo.pdf", - }) + It("finds files by size", func() { + assertDocCount(ref.ResourceId, `Size:12345`, 1) + assertDocCount(ref.ResourceId, `Size:>1000`, 1) + assertDocCount(ref.ResourceId, `Size:<100000`, 1) + + assertDocCount(ref.ResourceId, `Size:12344`, 0) + assertDocCount(ref.ResourceId, `Size:<1000`, 0) + assertDocCount(ref.ResourceId, `Size:>100000`, 0) + }) + }) + + Context("by filename", func() { + It("finds files with spaces in the filename", func() { + ri.Path = "Foo oo.pdf" + ref.Path = "./" + ri.Path + err := i.Add(ref, ri) Expect(err).ToNot(HaveOccurred()) - Expect(res).ToNot(BeNil()) - Expect(len(res.Matches)).To(Equal(0)) + + assertDocCount(ref.ResourceId, `Name:foo\ o*`, 1) }) - It("limits the search to the relevant fields", func() { - res, err := i.Search(ctx, &searchsvc.SearchIndexRequest{ - Ref: &searchmsg.Reference{ - ResourceId: &searchmsg.ResourceID{ - StorageId: ref.ResourceId.StorageId, - OpaqueId: ref.ResourceId.OpaqueId, - }, - }, - Query: "*" + ref.ResourceId.OpaqueId + "*", - }) + It("finds files by digits in the filename", func() { + ri.Path = "12345.pdf" + ref.Path = "./" + ri.Path + err := i.Add(ref, ri) Expect(err).ToNot(HaveOccurred()) - Expect(res).ToNot(BeNil()) - Expect(len(res.Matches)).To(Equal(0)) + + assertDocCount(ref.ResourceId, `Name:1234*`, 1) }) - It("returns all desired fields", func() { - res, err := i.Search(ctx, &searchsvc.SearchIndexRequest{ - Ref: &searchmsg.Reference{ - ResourceId: &searchmsg.ResourceID{ - StorageId: ref.ResourceId.StorageId, - OpaqueId: ref.ResourceId.OpaqueId, - }, - }, - Query: "foo.pdf", - }) - Expect(err).ToNot(HaveOccurred()) - Expect(res).ToNot(BeNil()) - Expect(len(res.Matches)).To(Equal(1)) - match := res.Matches[0] - Expect(match.Entity.Ref.ResourceId.OpaqueId).To(Equal(ref.ResourceId.OpaqueId)) - Expect(match.Entity.Ref.Path).To(Equal(ref.Path)) - Expect(match.Entity.Id.OpaqueId).To(Equal(ri.Id.OpaqueId)) - Expect(match.Entity.Name).To(Equal(ri.Path)) - Expect(match.Entity.Size).To(Equal(ri.Size)) - Expect(match.Entity.Type).To(Equal(uint64(ri.Type))) - Expect(match.Entity.MimeType).To(Equal(ri.MimeType)) - Expect(match.Entity.Deleted).To(BeFalse()) - Expect(uint64(match.Entity.LastModifiedTime.AsTime().Unix())).To(Equal(ri.Mtime.Seconds)) - }) - - It("finds files by name, prefix or substring match", func() { - queries := []string{"foo.pdf", "foo*", "*oo.p*"} - for _, query := range queries { - res, err := i.Search(ctx, &searchsvc.SearchIndexRequest{ - Ref: &searchmsg.Reference{ - ResourceId: &searchmsg.ResourceID{ - StorageId: ref.ResourceId.StorageId, - OpaqueId: ref.ResourceId.OpaqueId, - }, - }, - Query: query, - }) - Expect(err).ToNot(HaveOccurred()) - Expect(res).ToNot(BeNil()) - Expect(len(res.Matches)).To(Equal(1), "query returned no result: "+query) - Expect(res.Matches[0].Entity.Ref.ResourceId.OpaqueId).To(Equal(ref.ResourceId.OpaqueId)) - Expect(res.Matches[0].Entity.Ref.Path).To(Equal(ref.Path)) - Expect(res.Matches[0].Entity.Id.OpaqueId).To(Equal(ri.Id.OpaqueId)) - Expect(res.Matches[0].Entity.Name).To(Equal(ri.Path)) - Expect(res.Matches[0].Entity.Size).To(Equal(ri.Size)) - } - }) - - It("is case-insensitive", func() { - res, err := i.Search(ctx, &searchsvc.SearchIndexRequest{ - Ref: &searchmsg.Reference{ - ResourceId: &searchmsg.ResourceID{ - StorageId: ref.ResourceId.StorageId, - OpaqueId: ref.ResourceId.OpaqueId, - }, - }, - Query: "Foo*", - }) - Expect(err).ToNot(HaveOccurred()) - Expect(res).ToNot(BeNil()) - Expect(len(res.Matches)).To(Equal(1)) - }) - - Context("and an additional file in a subdirectory", func() { - var ( - nestedRef *sprovider.Reference - nestedRI *sprovider.ResourceInfo - ) - - BeforeEach(func() { - nestedRef = &sprovider.Reference{ - ResourceId: &sprovider.ResourceId{ - StorageId: "storageid", - OpaqueId: "rootopaqueid", - }, - Path: "./nested/nestedpdf.pdf", - } - nestedRI = &sprovider.ResourceInfo{ - Id: &sprovider.ResourceId{ - StorageId: "storageid", - OpaqueId: "nestedopaqueid", - }, - Path: "nestedpdf.pdf", - Size: 12345, - } - err := i.Add(nestedRef, nestedRI) + Context("with a file in the root of the space", func() { + JustBeforeEach(func() { + err := i.Add(ref, ri) Expect(err).ToNot(HaveOccurred()) }) - It("finds files living deeper in the tree by filename, prefix or substring match", func() { - queries := []string{"nestedpdf.pdf", "nested*", "*tedpdf.*"} + It("scopes the search to the specified space", func() { + resourceId := &sprovider.ResourceId{ + StorageId: "differentstorageid", + OpaqueId: "differentopaqueid", + } + assertDocCount(resourceId, `Name:foo.pdf`, 0) + }) + + It("limits the search to the specified fields", func() { + assertDocCount(ref.ResourceId, "Name:*"+ref.ResourceId.OpaqueId+"*", 0) + }) + + It("returns all desired fields", func() { + matches := assertDocCount(ref.ResourceId, "Name:foo.pdf", 1) + match := matches[0] + Expect(match.Entity.Ref.ResourceId.OpaqueId).To(Equal(ref.ResourceId.OpaqueId)) + Expect(match.Entity.Ref.Path).To(Equal(ref.Path)) + Expect(match.Entity.Id.OpaqueId).To(Equal(ri.Id.OpaqueId)) + Expect(match.Entity.Name).To(Equal(ri.Path)) + Expect(match.Entity.Size).To(Equal(ri.Size)) + Expect(match.Entity.Type).To(Equal(uint64(ri.Type))) + Expect(match.Entity.MimeType).To(Equal(ri.MimeType)) + Expect(match.Entity.Deleted).To(BeFalse()) + Expect(uint64(match.Entity.LastModifiedTime.AsTime().Unix())).To(Equal(ri.Mtime.Seconds)) + }) + + It("finds files by name, prefix or substring match", func() { + queries := []string{"foo.pdf", "foo*", "*oo.p*"} for _, query := range queries { + matches := assertDocCount(ref.ResourceId, query, 1) + Expect(matches[0].Entity.Ref.ResourceId.OpaqueId).To(Equal(ref.ResourceId.OpaqueId)) + Expect(matches[0].Entity.Ref.Path).To(Equal(ref.Path)) + Expect(matches[0].Entity.Id.OpaqueId).To(Equal(ri.Id.OpaqueId)) + Expect(matches[0].Entity.Name).To(Equal(ri.Path)) + Expect(matches[0].Entity.Size).To(Equal(ri.Size)) + } + }) + + It("uses a lower-case index", func() { + assertDocCount(ref.ResourceId, "Name:foo*", 1) + assertDocCount(ref.ResourceId, "Name:Foo*", 0) + }) + + Context("and an additional file in a subdirectory", func() { + var ( + nestedRef *sprovider.Reference + nestedRI *sprovider.ResourceInfo + ) + + BeforeEach(func() { + nestedRef = &sprovider.Reference{ + ResourceId: &sprovider.ResourceId{ + StorageId: "storageid", + OpaqueId: "rootopaqueid", + }, + Path: "./nested/nestedpdf.pdf", + } + nestedRI = &sprovider.ResourceInfo{ + Id: &sprovider.ResourceId{ + StorageId: "storageid", + OpaqueId: "nestedopaqueid", + }, + Path: "nestedpdf.pdf", + Size: 12345, + } + err := i.Add(nestedRef, nestedRI) + Expect(err).ToNot(HaveOccurred()) + }) + + It("finds files living deeper in the tree by filename, prefix or substring match", func() { + queries := []string{"nestedpdf.pdf", "nested*", "*tedpdf.*"} + for _, query := range queries { + assertDocCount(ref.ResourceId, query, 1) + } + }) + + It("does not find the higher levels when limiting the searched directory", func() { res, err := i.Search(ctx, &searchsvc.SearchIndexRequest{ Ref: &searchmsg.Reference{ ResourceId: &searchmsg.ResourceID{ StorageId: ref.ResourceId.StorageId, OpaqueId: ref.ResourceId.OpaqueId, }, + Path: "./nested/", }, - Query: query, + Query: "Name:foo.pdf", }) Expect(err).ToNot(HaveOccurred()) Expect(res).ToNot(BeNil()) - Expect(len(res.Matches)).To(Equal(1), "query returned no result: "+query) - } - }) - - It("does not find the higher levels when limiting the searched directory", func() { - res, err := i.Search(ctx, &searchsvc.SearchIndexRequest{ - Ref: &searchmsg.Reference{ - ResourceId: &searchmsg.ResourceID{ - StorageId: ref.ResourceId.StorageId, - OpaqueId: ref.ResourceId.OpaqueId, - }, - Path: "./nested/", - }, - Query: "foo.pdf", + Expect(len(res.Matches)).To(Equal(0)) }) - Expect(err).ToNot(HaveOccurred()) - Expect(res).ToNot(BeNil()) - Expect(len(res.Matches)).To(Equal(0)) }) }) }) @@ -366,23 +351,13 @@ var _ = Describe("Index", func() { Expect(err).ToNot(HaveOccurred()) parentRi.Path = "newname" - err = i.Move(parentRi, "./somewhere/else/newname") + err = i.Move(parentRi.Id, "./somewhere/else/newname") Expect(err).ToNot(HaveOccurred()) assertDocCount(rootId, "subdir", 0) - res, err := i.Search(ctx, &searchsvc.SearchIndexRequest{ - Query: "child.pdf", - Ref: &searchmsg.Reference{ - ResourceId: &searchmsg.ResourceID{ - StorageId: rootId.StorageId, - OpaqueId: rootId.OpaqueId, - }, - }, - }) - Expect(err).ToNot(HaveOccurred()) - Expect(len(res.Matches)).To(Equal(1)) - Expect(res.Matches[0].Entity.Ref.Path).To(Equal("./somewhere/else/newname/child.pdf")) + matches := assertDocCount(rootId, "Name:child.pdf", 1) + Expect(matches[0].Entity.Ref.Path).To(Equal("./somewhere/else/newname/child.pdf")) }) }) }) diff --git a/extensions/search/pkg/search/mocks/IndexClient.go b/extensions/search/pkg/search/mocks/IndexClient.go index f43f9f3a9..d2286517a 100644 --- a/extensions/search/pkg/search/mocks/IndexClient.go +++ b/extensions/search/pkg/search/mocks/IndexClient.go @@ -30,13 +30,13 @@ func (_m *IndexClient) Add(ref *providerv1beta1.Reference, ri *providerv1beta1.R return r0 } -// Delete provides a mock function with given fields: ri -func (_m *IndexClient) Delete(ri *providerv1beta1.ResourceId) error { - ret := _m.Called(ri) +// Delete provides a mock function with given fields: id +func (_m *IndexClient) Delete(id *providerv1beta1.ResourceId) error { + ret := _m.Called(id) var r0 error if rf, ok := ret.Get(0).(func(*providerv1beta1.ResourceId) error); ok { - r0 = rf(ri) + r0 = rf(id) } else { r0 = ret.Error(0) } @@ -65,13 +65,13 @@ func (_m *IndexClient) DocCount() (uint64, error) { return r0, r1 } -// Move provides a mock function with given fields: ri, path -func (_m *IndexClient) Move(ri *providerv1beta1.ResourceInfo, path string) error { - ret := _m.Called(ri, path) +// Move provides a mock function with given fields: id, fullPath +func (_m *IndexClient) Move(id *providerv1beta1.ResourceId, fullPath string) error { + ret := _m.Called(id, fullPath) var r0 error - if rf, ok := ret.Get(0).(func(*providerv1beta1.ResourceInfo, string) error); ok { - r0 = rf(ri, path) + if rf, ok := ret.Get(0).(func(*providerv1beta1.ResourceId, string) error); ok { + r0 = rf(id, fullPath) } else { r0 = ret.Error(0) } @@ -79,13 +79,13 @@ func (_m *IndexClient) Move(ri *providerv1beta1.ResourceInfo, path string) error return r0 } -// Purge provides a mock function with given fields: ri -func (_m *IndexClient) Purge(ri *providerv1beta1.ResourceId) error { - ret := _m.Called(ri) +// Purge provides a mock function with given fields: id +func (_m *IndexClient) Purge(id *providerv1beta1.ResourceId) error { + ret := _m.Called(id) var r0 error if rf, ok := ret.Get(0).(func(*providerv1beta1.ResourceId) error); ok { - r0 = rf(ri) + r0 = rf(id) } else { r0 = ret.Error(0) } @@ -93,13 +93,13 @@ func (_m *IndexClient) Purge(ri *providerv1beta1.ResourceId) error { return r0 } -// Restore provides a mock function with given fields: ri -func (_m *IndexClient) Restore(ri *providerv1beta1.ResourceId) error { - ret := _m.Called(ri) +// Restore provides a mock function with given fields: id +func (_m *IndexClient) Restore(id *providerv1beta1.ResourceId) error { + ret := _m.Called(id) var r0 error if rf, ok := ret.Get(0).(func(*providerv1beta1.ResourceId) error); ok { - r0 = rf(ri) + r0 = rf(id) } else { r0 = ret.Error(0) } diff --git a/extensions/search/pkg/search/provider/events.go b/extensions/search/pkg/search/provider/events.go index d243c5237..641c3010a 100644 --- a/extensions/search/pkg/search/provider/events.go +++ b/extensions/search/pkg/search/provider/events.go @@ -58,11 +58,11 @@ func (p *Provider) handleEvent(ev interface{}) { statRes, err := p.statResource(ref, owner) if err != nil { - p.logger.Error().Err(err).Msg("failed to stat the changed resource") + p.logger.Error().Err(err).Msg("failed to stat the moved resource") return } if statRes.Status.Code != rpc.Code_CODE_OK { - p.logger.Error().Interface("statRes", statRes).Msg("failed to stat the changed resource") + p.logger.Error().Interface("statRes", statRes).Msg("failed to stat the moved resource") return } @@ -76,9 +76,9 @@ func (p *Provider) handleEvent(ev interface{}) { return } - err = p.indexClient.Move(statRes.Info, gpRes.Path) + err = p.indexClient.Move(statRes.Info.Id, gpRes.Path) if err != nil { - p.logger.Error().Err(err).Msg("failed to restore the changed resource in the index") + p.logger.Error().Err(err).Msg("failed to move the changed resource in the index") } return case events.ContainerCreated: @@ -126,7 +126,6 @@ func (p *Provider) statResource(ref *provider.Reference, owner *user.User) (*pro return nil, err } - // Stat changed resource resource return p.gwClient.Stat(ownerCtx, &provider.StatRequest{Ref: ref}) } @@ -136,7 +135,6 @@ func (p *Provider) getPath(id *provider.ResourceId, owner *user.User) (*provider return nil, err } - // Stat changed resource resource return p.gwClient.GetPath(ownerCtx, &provider.GetPathRequest{ResourceId: id}) } diff --git a/extensions/search/pkg/search/provider/events_test.go b/extensions/search/pkg/search/provider/events_test.go index 9de17c5df..a36b36aef 100644 --- a/extensions/search/pkg/search/provider/events_test.go +++ b/extensions/search/pkg/search/provider/events_test.go @@ -156,8 +156,8 @@ var _ = Describe("Searchprovider", func() { Status: status.NewOK(ctx), Path: "./new/path.pdf", }, nil) - indexClient.On("Move", mock.MatchedBy(func(riToIndex *sprovider.ResourceInfo) bool { - return riToIndex.Id.OpaqueId == ri.Id.OpaqueId + indexClient.On("Move", mock.MatchedBy(func(id *sprovider.ResourceId) bool { + return id.OpaqueId == ri.Id.OpaqueId }), "./new/path.pdf").Return(nil).Run(func(args mock.Arguments) { called = true }) diff --git a/extensions/search/pkg/search/provider/searchprovider.go b/extensions/search/pkg/search/provider/searchprovider.go index 940405635..067d9665e 100644 --- a/extensions/search/pkg/search/provider/searchprovider.go +++ b/extensions/search/pkg/search/provider/searchprovider.go @@ -125,7 +125,7 @@ func (p *Provider) Search(ctx context.Context, req *searchsvc.SearchRequest) (*s _, rootStorageID := storagespace.SplitStorageID(space.Root.StorageId) res, err := p.indexClient.Search(ctx, &searchsvc.SearchIndexRequest{ - Query: req.Query, + Query: formatQuery(req.Query), Ref: &searchmsg.Reference{ ResourceId: &searchmsg.ResourceID{ StorageId: space.Root.StorageId, @@ -217,3 +217,18 @@ func (p *Provider) logDocCount() { } p.logger.Debug().Interface("count", c).Msg("new document count") } + +func formatQuery(q string) string { + query := q + fields := []string{"RootID", "Path", "ID", "Name", "Size", "Mtime", "MimeType", "Type"} + for _, field := range fields { + query = strings.ReplaceAll(query, strings.ToLower(field)+":", field+":") + } + + if strings.Contains(query, ":") { + return query // Sophisticated field based search + } + + // this is a basic filename search + return "Name:*" + strings.ReplaceAll(strings.ToLower(query), " ", `\ `) + "*" +} diff --git a/extensions/search/pkg/search/provider/searchprovider_test.go b/extensions/search/pkg/search/provider/searchprovider_test.go index 37f59ff1c..5f7278273 100644 --- a/extensions/search/pkg/search/provider/searchprovider_test.go +++ b/extensions/search/pkg/search/provider/searchprovider_test.go @@ -146,6 +146,50 @@ var _ = Describe("Searchprovider", func() { }, nil) }) + It("lowercases the filename", func() { + p.Search(ctx, &searchsvc.SearchRequest{ + Query: "Foo.pdf", + }) + indexClient.AssertCalled(GinkgoT(), "Search", mock.Anything, mock.MatchedBy(func(req *searchsvc.SearchIndexRequest) bool { + return req.Query == "Name:*foo.pdf*" + })) + }) + + It("does not mess with field-based searches", func() { + p.Search(ctx, &searchsvc.SearchRequest{ + Query: "Size:<10", + }) + indexClient.AssertCalled(GinkgoT(), "Search", mock.Anything, mock.MatchedBy(func(req *searchsvc.SearchIndexRequest) bool { + return req.Query == "Size:<10" + })) + }) + + It("uppercases field names", func() { + tests := []struct { + Original string + Expected string + }{ + {Original: "size:<100", Expected: "Size:<100"}, + } + for _, test := range tests { + p.Search(ctx, &searchsvc.SearchRequest{ + Query: test.Original, + }) + indexClient.AssertCalled(GinkgoT(), "Search", mock.Anything, mock.MatchedBy(func(req *searchsvc.SearchIndexRequest) bool { + return req.Query == test.Expected + })) + } + }) + + It("escapes special characters", func() { + p.Search(ctx, &searchsvc.SearchRequest{ + Query: "Foo oo.pdf", + }) + indexClient.AssertCalled(GinkgoT(), "Search", mock.Anything, mock.MatchedBy(func(req *searchsvc.SearchIndexRequest) bool { + return req.Query == `Name:*foo\ oo.pdf*` + })) + }) + It("searches the personal user space", func() { res, err := p.Search(ctx, &searchsvc.SearchRequest{ Query: "foo", @@ -160,7 +204,7 @@ var _ = Describe("Searchprovider", func() { Expect(match.Entity.Ref.Path).To(Equal("./path/to/Foo.pdf")) indexClient.AssertCalled(GinkgoT(), "Search", mock.Anything, mock.MatchedBy(func(req *searchsvc.SearchIndexRequest) bool { - return req.Query == "foo" && req.Ref.ResourceId.OpaqueId == personalSpace.Root.OpaqueId && req.Ref.Path == "" + return req.Query == "Name:*foo*" && req.Ref.ResourceId.OpaqueId == personalSpace.Root.OpaqueId && req.Ref.Path == "" })) }) }) @@ -225,7 +269,7 @@ var _ = Describe("Searchprovider", func() { }, nil) res, err := p.Search(ctx, &searchsvc.SearchRequest{ - Query: "foo", + Query: "Foo", }) Expect(err).ToNot(HaveOccurred()) Expect(res).ToNot(BeNil()) @@ -237,7 +281,7 @@ var _ = Describe("Searchprovider", func() { Expect(match.Entity.Ref.Path).To(Equal("./to/Shared.pdf")) indexClient.AssertCalled(GinkgoT(), "Search", mock.Anything, mock.MatchedBy(func(req *searchsvc.SearchIndexRequest) bool { - return req.Query == "foo" && req.Ref.ResourceId.StorageId == grantSpace.Root.StorageId && req.Ref.Path == "./grant/path" + return req.Query == "Name:*foo*" && req.Ref.ResourceId.StorageId == grantSpace.Root.StorageId && req.Ref.Path == "./grant/path" })) }) diff --git a/extensions/search/pkg/search/search.go b/extensions/search/pkg/search/search.go index 12df20342..0a8bedd26 100644 --- a/extensions/search/pkg/search/search.go +++ b/extensions/search/pkg/search/search.go @@ -38,9 +38,9 @@ type ProviderClient interface { type IndexClient interface { Search(ctx context.Context, req *searchsvc.SearchIndexRequest) (*searchsvc.SearchIndexResponse, error) Add(ref *providerv1beta1.Reference, ri *providerv1beta1.ResourceInfo) error - Move(ri *providerv1beta1.ResourceInfo, path string) error - Delete(ri *providerv1beta1.ResourceId) error - Restore(ri *providerv1beta1.ResourceId) error - Purge(ri *providerv1beta1.ResourceId) error + Move(id *providerv1beta1.ResourceId, fullPath string) error + Delete(id *providerv1beta1.ResourceId) error + Restore(id *providerv1beta1.ResourceId) error + Purge(id *providerv1beta1.ResourceId) error DocCount() (uint64, error) } diff --git a/extensions/webdav/pkg/service/v0/search.go b/extensions/webdav/pkg/service/v0/search.go index e90382984..817f401a1 100644 --- a/extensions/webdav/pkg/service/v0/search.go +++ b/extensions/webdav/pkg/service/v0/search.go @@ -9,6 +9,7 @@ import ( "strconv" "time" + provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" revactx "github.com/cs3org/reva/v2/pkg/ctx" "github.com/owncloud/ocis/v2/extensions/webdav/pkg/net" "github.com/owncloud/ocis/v2/extensions/webdav/pkg/prop" @@ -43,7 +44,7 @@ func (g Webdav) Search(w http.ResponseWriter, r *http.Request) { ctx := revactx.ContextSetToken(r.Context(), t) ctx = metadata.Set(ctx, revactx.TokenHeader, t) rsp, err := g.searchClient.Search(ctx, &searchsvc.SearchRequest{ - Query: "*" + rep.SearchFiles.Search.Pattern + "*", + Query: rep.SearchFiles.Search.Pattern, }) if err != nil { e := merrors.Parse(err.Error()) @@ -128,9 +129,15 @@ func matchToPropResponse(ctx context.Context, match *searchmsg.Match) (*propfind propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:getcontenttype", match.Entity.MimeType)) size := strconv.FormatUint(match.Entity.Size, 10) - propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:size", size)) - propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:getcontentlength", size)) - + if match.Entity.Type == uint64(provider.ResourceType_RESOURCE_TYPE_CONTAINER) { + propstatOK.Prop = append(propstatOK.Prop, prop.Raw("d:resourcetype", "")) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:size", size)) + } else { + propstatOK.Prop = append(propstatOK.Prop, + prop.Escaped("d:resourcetype", ""), + prop.Escaped("d:getcontentlength", size), + ) + } // TODO find name for score property score := strconv.FormatFloat(float64(match.Score), 'f', -1, 64) propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:score", score))