diff --git a/services/search/pkg/opensearch/internal/convert/kql_transpile.go b/services/search/pkg/opensearch/internal/convert/kql_transpile.go index 9747c7e2b3..101677651e 100644 --- a/services/search/pkg/opensearch/internal/convert/kql_transpile.go +++ b/services/search/pkg/opensearch/internal/convert/kql_transpile.go @@ -119,9 +119,6 @@ func (t kqlOpensearchTranspiler) toBuilder(node ast.Node) (osu.Builder, error) { } field, value := node.Key, node.Value - if query.FieldIsPath(node.Key) { - value = strings.TrimSuffix(value, "/") - } if node.CaseInsensitive { field += mapping.LowercaseSuffix value = strings.ToLower(value) diff --git a/services/search/pkg/parity/README.md b/services/search/pkg/parity/README.md index 05b205dea6..6845457ede 100644 --- a/services/search/pkg/parity/README.md +++ b/services/search/pkg/parity/README.md @@ -250,6 +250,15 @@ Fixtures: | PATH-06 | `path:"./DOCUMENTS"` | docs-upper | docs-upper | docs-upper | ✅ | | PATH-07 | `path:"./Documents"` | docs-mixed | docs-mixed | docs-mixed | ✅ | | PATH-08 | `path:"./parent/"` | child.jpg, parent | child.jpg, parent | child.jpg, parent | ✅ | +| PATH-09 | `path:"/"` | child.jpg, docs-lower, docs-mixed, docs-upper, parent | child.jpg, docs-lower, docs-mixed, docs-upper, parent | child.jpg, docs-lower, docs-mixed, docs-upper, parent | ✅ | +| PATH-10 | `path:""` | child.jpg, docs-lower, docs-mixed, docs-upper, parent | child.jpg, docs-lower, docs-mixed, docs-upper, parent | child.jpg, docs-lower, docs-mixed, docs-upper, parent | ✅ | +| PATH-11 | `path:"/parent"` | child.jpg, parent | child.jpg, parent | child.jpg, parent | ✅ | +| PATH-12 | `path:"parent"` | child.jpg, parent | child.jpg, parent | child.jpg, parent | ✅ | +| PATH-13 | `path:"parent/*"` | child.jpg | child.jpg | child.jpg | ✅ | +| PATH-14 | `path:"*/parent"` | child.jpg, parent | child.jpg, parent | child.jpg, parent | ✅ | +| PATH-15 | `path:"?/parent"` | child.jpg, parent | child.jpg, parent | child.jpg, parent | ✅ | +| PATH-16 | `path:"*/Documents"` | docs-mixed | docs-mixed | docs-mixed | ✅ | +| PATH-17 | `path:"*/parent/"` | child.jpg, parent | child.jpg, parent | child.jpg, parent | ✅ | ### fields diff --git a/services/search/pkg/parity/query_path_test.go b/services/search/pkg/parity/query_path_test.go index e97e8e1596..0200a98f68 100644 --- a/services/search/pkg/parity/query_path_test.go +++ b/services/search/pkg/parity/query_path_test.go @@ -23,6 +23,15 @@ func pathGroup() queryGroup { {id: 6, query: `path:"./DOCUMENTS"`, want: []string{"docs-upper"}}, {id: 7, query: `path:"./Documents"`, want: []string{"docs-mixed"}}, {id: 8, query: `path:"./parent/"`, want: []string{"parent", "child.jpg"}}, + {id: 9, query: `path:"/"`, want: []string{"parent", "child.jpg", "docs-lower", "docs-upper", "docs-mixed"}}, + {id: 10, query: `path:""`, want: []string{"parent", "child.jpg", "docs-lower", "docs-upper", "docs-mixed"}}, + {id: 11, query: `path:"/parent"`, want: []string{"parent", "child.jpg"}}, + {id: 12, query: `path:"parent"`, want: []string{"parent", "child.jpg"}}, + {id: 13, query: `path:"parent/*"`, want: []string{"child.jpg"}}, + {id: 14, query: `path:"*/parent"`, want: []string{"parent", "child.jpg"}}, + {id: 15, query: `path:"?/parent"`, want: []string{"parent", "child.jpg"}}, + {id: 16, query: `path:"*/Documents"`, want: []string{"docs-mixed"}}, + {id: 17, query: `path:"*/parent/"`, want: []string{"parent", "child.jpg"}}, }, } } diff --git a/services/search/pkg/query/bleve/compiler.go b/services/search/pkg/query/bleve/compiler.go index a1dba9a65a..74f7d60dc6 100644 --- a/services/search/pkg/query/bleve/compiler.go +++ b/services/search/pkg/query/bleve/compiler.go @@ -95,9 +95,6 @@ func walk(offset int, nodes []ast.Node) (bleveQuery.Query, int, error) { // bleve treats `/` and `+` as literals mid-term, so a literal MIME like // image/svg+xml still matches exactly. val := n.Value - if searchQuery.FieldIsPath(n.Key) { - val = strings.TrimSuffix(val, "/") - } k := n.Key v := val if k != "ID" && k != "Size" && k != "MimeType" { diff --git a/services/search/pkg/query/bleve/compiler_test.go b/services/search/pkg/query/bleve/compiler_test.go index 632a29b387..606c953050 100644 --- a/services/search/pkg/query/bleve/compiler_test.go +++ b/services/search/pkg/query/bleve/compiler_test.go @@ -59,7 +59,7 @@ func Test_compile(t *testing.T) { }, }, want: func() query.Query { - tq := query.NewTermQuery("/Foo") + tq := query.NewTermQuery("./Foo") tq.SetField("Path") return query.NewConjunctionQuery([]query.Query{tq}) }(), diff --git a/services/search/pkg/query/normalize.go b/services/search/pkg/query/normalize.go index bce1fd1be6..364a23f683 100644 --- a/services/search/pkg/query/normalize.go +++ b/services/search/pkg/query/normalize.go @@ -5,6 +5,7 @@ import ( "strings" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + "github.com/opencloud-eu/reva/v2/pkg/utils" "reflect" "github.com/opencloud-eu/opencloud/pkg/ast" @@ -36,6 +37,14 @@ func normalizeNodes(nodes []ast.Node, resolve func(string) string, defaultKey st switch node := n.(type) { case *ast.StringNode: node.Key = resolveKey(node.Key) + if FieldIsPath(node.Key) { + if strings.IndexAny(node.Value, "*?") == 0 { + // a leading wildcard stands in for the "./" prefix, anchoring would break it + node.Value = strings.TrimSuffix(node.Value, "/") + } else { + node.Value = utils.MakeRelativePath(node.Value) + } + } if FieldValueIsNormalized(node.Key) { node.Value = strings.ToLower(node.Value) } diff --git a/services/search/pkg/query/normalize_test.go b/services/search/pkg/query/normalize_test.go index 94eecfb300..afc9182c3e 100644 --- a/services/search/pkg/query/normalize_test.go +++ b/services/search/pkg/query/normalize_test.go @@ -127,6 +127,27 @@ var _ = Describe("Normalize", func() { })) }) + DescribeTable("normalizes path values to the stored relative form", + func(in, want string) { + got := norm(&ast.StringNode{Key: "path", Value: in}) + Expect(got).To(Equal([]ast.Node{&ast.StringNode{Key: "Path", Value: want}})) + }, + Entry("relative", "./parent", "./parent"), + Entry("trailing slash", "./parent/", "./parent"), + Entry("absolute", "/parent", "./parent"), + Entry("bare", "parent", "./parent"), + Entry("nested bare", "parent/child.jpg", "./parent/child.jpg"), + Entry("root", "/", "."), + Entry("empty", "", "."), + Entry("dot", ".", "."), + Entry("wildcard", "./par*/", "./par*"), + Entry("bare wildcard", "parent/*", "./parent/*"), + Entry("leading wildcard", "*child*", "*child*"), + Entry("leading wildcard covers the prefix", "*/parent", "*/parent"), + Entry("leading single-char wildcard covers the prefix", "?/parent", "?/parent"), + Entry("leading wildcard trailing slash", "*/parent/", "*/parent"), + ) + It("converts value nodes to pointers", func() { got := norm( ast.StringNode{Key: "name", Value: "x"},