mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-09-12 21:58:58 -04:00
fix(search): keep mediatype:file atomic so it composes with other terms
mediatype:file expands to a NOT restriction. Spliced inline as `NOT MimeType:httpd/unix-directory`, the bleve compiler's NOT branch left a stale operand, so `mediatype:file AND name:x` dropped `name:x` and matched nothing (the web Files filter). It is now wrapped in a group so the negation stays atomic; verified fixing both bleve and OpenSearch.
This commit is contained in:
1 parent
69c517a8b2
commit
c94059fb85
6 files changed
+33
-67
No files matched your search
@@ -190,8 +190,8 @@ var _ = Describe("Bleve", func() {
|
||||
parentResource.Document.Tags = []string{"Work", "Urgent"}
|
||||
Expect(eng.Upsert(parentResource.ID, parentResource)).To(Succeed())
|
||||
|
||||
assertDocCount(rootResource.ID, "tag:work", 1) // stored "Work", queried lower
|
||||
assertDocCount(rootResource.ID, "tag:WORK", 1) // queried upper
|
||||
assertDocCount(rootResource.ID, "tag:work", 1) // stored "Work", queried lower
|
||||
assertDocCount(rootResource.ID, "tag:WORK", 1) // queried upper
|
||||
assertDocCount(rootResource.ID, "Tags:Urgent", 1)
|
||||
assertDocCount(rootResource.ID, "tag:missing", 0)
|
||||
})
|
||||
@@ -397,6 +397,19 @@ var _ = Describe("Bleve", func() {
|
||||
assertDocCount(rootResource.ID, "MimeType:image/svg+xml", 1)
|
||||
assertDocCount(rootResource.ID, "MimeType:image/png", 1)
|
||||
})
|
||||
|
||||
It("combines mediatype:file with another term", func() {
|
||||
// regression: mediatype:file (a NOT) next to an operator dropped the
|
||||
// other operand, so mediatype:file AND name:x matched nothing.
|
||||
parentResource.Document.MimeType = "httpd/unix-directory" // a folder
|
||||
childResource.Document.MimeType = "image/png" // a file
|
||||
for _, r := range []search.Resource{parentResource, childResource} {
|
||||
Expect(eng.Upsert(r.ID, r)).To(Succeed())
|
||||
}
|
||||
assertDocCount(rootResource.ID, "mediatype:file", 1) // only the file
|
||||
assertDocCount(rootResource.ID, "mediatype:file AND name:child.pdf", 1) // file AND its name
|
||||
assertDocCount(rootResource.ID, "mediatype:file AND name:nope", 0)
|
||||
})
|
||||
})
|
||||
|
||||
Context("Highlights", func() {
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
package convert_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/opencloud-eu/opencloud/pkg/ast"
|
||||
opensearchtest "github.com/opencloud-eu/opencloud/services/search/internal/opensearchtest"
|
||||
"github.com/opencloud-eu/opencloud/services/search/pkg/opensearch/internal/convert"
|
||||
)
|
||||
|
||||
// LowerValues runs after the shared query.Normalize pass, so it operates on
|
||||
// already-resolved pointer nodes. Field resolution, media-type expansion and
|
||||
// group-key defaulting are tested once at the query.Normalize level (see
|
||||
// pkg/query normalize_test), not here. Only lowercase-analyzed fields get their
|
||||
// value folded; case-preserved fields keep their casing.
|
||||
func TestLowerValues(t *testing.T) {
|
||||
tests := []opensearchtest.TableTest[[]ast.Node, []ast.Node]{
|
||||
{
|
||||
Name: "lowercase-analyzed field: value is folded, recursing into groups",
|
||||
Got: []ast.Node{
|
||||
&ast.StringNode{Key: "Name", Value: "StringNode"},
|
||||
&ast.GroupNode{Key: "GroupNode", Nodes: []ast.Node{
|
||||
&ast.StringNode{Key: "Name", Value: "StringNode"},
|
||||
}},
|
||||
},
|
||||
Want: []ast.Node{
|
||||
&ast.StringNode{Key: "Name", Value: "stringnode"},
|
||||
&ast.GroupNode{Key: "GroupNode", Nodes: []ast.Node{
|
||||
&ast.StringNode{Key: "Name", Value: "stringnode"},
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "case-preserved field: value keeps its casing",
|
||||
Got: []ast.Node{
|
||||
&ast.StringNode{Key: "aBc", Value: "StringNode"},
|
||||
&ast.GroupNode{Key: "GroupNode", Nodes: []ast.Node{
|
||||
&ast.StringNode{Key: "aBc", Value: "StringNode"},
|
||||
}},
|
||||
},
|
||||
Want: []ast.Node{
|
||||
&ast.StringNode{Key: "aBc", Value: "StringNode"},
|
||||
&ast.GroupNode{Key: "GroupNode", Nodes: []ast.Node{
|
||||
&ast.StringNode{Key: "aBc", Value: "StringNode"},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
require.Equal(t, test.Want, convert.LowerValues(test.Got))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -321,8 +321,8 @@ func Test_compile(t *testing.T) {
|
||||
},
|
||||
want: query.NewConjunctionQuery([]query.Query{
|
||||
query.NewQueryStringQuery(`Name_lowercase:john\ smith`),
|
||||
query.NewQueryStringQuery(`Hidden:T`),
|
||||
query.NewQueryStringQuery(`Hidden:T`),
|
||||
query.NewQueryStringQuery(`Hidden:t`),
|
||||
query.NewQueryStringQuery(`Hidden:t`),
|
||||
}),
|
||||
wantErr: false,
|
||||
},
|
||||
|
||||
@@ -23,9 +23,14 @@ func Expand(key, value string) []ast.Node {
|
||||
value = strings.ToLower(value)
|
||||
switch value {
|
||||
case "file":
|
||||
// Group the negation so it stays atomic next to an operator: a bare
|
||||
// `NOT <term>` sequence spliced inline miscompiles as the left of an AND
|
||||
// (mediatype:file AND name:x would drop name:x).
|
||||
return []ast.Node{
|
||||
&ast.OperatorNode{Value: kql.BoolNOT},
|
||||
&ast.StringNode{Key: field, Value: "httpd/unix-directory"},
|
||||
&ast.GroupNode{Nodes: []ast.Node{
|
||||
&ast.OperatorNode{Value: kql.BoolNOT},
|
||||
&ast.StringNode{Key: field, Value: "httpd/unix-directory"},
|
||||
}},
|
||||
}
|
||||
case "folder":
|
||||
return term("httpd/unix-directory")
|
||||
|
||||
@@ -45,9 +45,12 @@ func TestExpand_literalValuePassesThroughToMimeType(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExpand_fileIsNotAFolder(t *testing.T) {
|
||||
// grouped so the negation stays atomic next to an operator.
|
||||
require.Equal(t, []ast.Node{
|
||||
&ast.OperatorNode{Value: "NOT"},
|
||||
&ast.StringNode{Key: "MimeType", Value: "httpd/unix-directory"},
|
||||
&ast.GroupNode{Nodes: []ast.Node{
|
||||
&ast.OperatorNode{Value: "NOT"},
|
||||
&ast.StringNode{Key: "MimeType", Value: "httpd/unix-directory"},
|
||||
}},
|
||||
}, mimetype.Expand("mediatype", "file"))
|
||||
}
|
||||
|
||||
|
||||
@@ -57,8 +57,10 @@ func TestNormalize_ResolvesFieldsAndExpandsMediatype(t *testing.T) {
|
||||
&ast.OperatorNode{Value: "AND"},
|
||||
&ast.StringNode{Key: "photo.cameraMake", Value: "canon"},
|
||||
&ast.OperatorNode{Value: "AND"},
|
||||
&ast.OperatorNode{Value: "NOT"},
|
||||
&ast.StringNode{Key: "MimeType", Value: "httpd/unix-directory"},
|
||||
&ast.GroupNode{Nodes: []ast.Node{
|
||||
&ast.OperatorNode{Value: "NOT"},
|
||||
&ast.StringNode{Key: "MimeType", Value: "httpd/unix-directory"},
|
||||
}},
|
||||
&ast.OperatorNode{Value: "AND"},
|
||||
&ast.NumberNode{Key: "Size", Value: 100},
|
||||
}, got)
|
||||
|
||||
Reference in new issue
Block a user