Merge pull request #3516 from opencloud-eu/fix/search-kql-path-normalization

fix(search): normalize KQL path once
This commit is contained in:
Andre Duffeck authored and GitHub committed 2026-10-30 16:18:52 +01:00
commit 70fbc9fcb6
7 files changed
+49 -7

No files matched your search

@@ -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)
+9
View File
@@ -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
@@ -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"}},
},
}
}
@@ -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" {
@@ -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})
}(),
+9
View File
@@ -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)
}
@@ -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"},