mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-09-13 06:09:21 -04:00
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.
108 lines
3.9 KiB
Go
108 lines
3.9 KiB
Go
package query_test
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/opencloud-eu/opencloud/pkg/ast"
|
|
"github.com/opencloud-eu/opencloud/services/search/pkg/query"
|
|
)
|
|
|
|
// This is the single place the shared KQL lowering pass is tested (field
|
|
// resolution, media-type expansion, group-key defaulting, pointer conversion).
|
|
// The backend query compilers consume its canonical output and must not re-test
|
|
// it.
|
|
|
|
func norm(nodes ...ast.Node) []ast.Node {
|
|
return query.Normalize(&ast.Ast{Nodes: nodes}, query.ResolveField).Nodes
|
|
}
|
|
|
|
func TestResolveField(t *testing.T) {
|
|
require.Equal(t, "Name", query.ResolveField("")) // empty -> free-text default
|
|
require.Equal(t, "Name", query.ResolveField("NAME")) // canonical, case-insensitive key match
|
|
require.Equal(t, "Tags", query.ResolveField("tag")) // singular alias
|
|
require.Equal(t, "MimeType", query.ResolveField("mimetype")) // real field
|
|
require.Equal(t, "photo.cameraMake", query.ResolveField("photo.CAMERAMAKE")) // facet, case-insensitive key match
|
|
require.Equal(t, "unknown.field", query.ResolveField("unknown.field")) // unknown key: unchanged, becomes a dead query
|
|
}
|
|
|
|
func TestFieldIsCaseInsensitive(t *testing.T) {
|
|
// The four CaseInsensitive override fields (resolved canonical names).
|
|
for _, f := range []string{"Name", "Path", "Tags", "Favorites"} {
|
|
require.True(t, query.FieldIsCaseInsensitive(f), f)
|
|
}
|
|
// Case-preserved / non-keyword fields are not.
|
|
for _, f := range []string{"MimeType", "ID", "Content", "unknown"} {
|
|
require.False(t, query.FieldIsCaseInsensitive(f), f)
|
|
}
|
|
}
|
|
|
|
func TestNormalize_ResolvesFieldsAndExpandsMediatype(t *testing.T) {
|
|
got := norm(
|
|
&ast.StringNode{Key: "", Value: "free"},
|
|
&ast.OperatorNode{Value: "AND"},
|
|
&ast.StringNode{Key: "TAG", Value: "x"},
|
|
&ast.OperatorNode{Value: "AND"},
|
|
&ast.StringNode{Key: "photo.cameramake", Value: "canon"},
|
|
&ast.OperatorNode{Value: "AND"},
|
|
&ast.StringNode{Key: "mediatype", Value: "file"},
|
|
&ast.OperatorNode{Value: "AND"},
|
|
ast.NumberNode{Key: "size", Value: 100},
|
|
)
|
|
require.Equal(t, []ast.Node{
|
|
&ast.StringNode{Key: "Name", Value: "free", CaseInsensitive: true},
|
|
&ast.OperatorNode{Value: "AND"},
|
|
&ast.StringNode{Key: "Tags", Value: "x", CaseInsensitive: true},
|
|
&ast.OperatorNode{Value: "AND"},
|
|
&ast.StringNode{Key: "photo.cameraMake", Value: "canon"},
|
|
&ast.OperatorNode{Value: "AND"},
|
|
&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)
|
|
}
|
|
|
|
// A bare restriction inside a named group inherits the group key; a keyed child
|
|
// keeps its own key; a bare restriction in an unnamed group falls back to Name.
|
|
func TestNormalize_GroupKeyDefaulting(t *testing.T) {
|
|
got := norm(
|
|
&ast.GroupNode{Key: "author", Nodes: []ast.Node{
|
|
&ast.StringNode{Value: "b"},
|
|
&ast.OperatorNode{Value: "OR"},
|
|
&ast.StringNode{Key: "name", Value: "d"},
|
|
}},
|
|
&ast.OperatorNode{Value: "AND"},
|
|
&ast.GroupNode{Nodes: []ast.Node{
|
|
&ast.StringNode{Value: "e"},
|
|
}},
|
|
)
|
|
require.Equal(t, []ast.Node{
|
|
&ast.GroupNode{Key: "author", Nodes: []ast.Node{
|
|
&ast.StringNode{Key: "author", Value: "b"},
|
|
&ast.OperatorNode{Value: "OR"},
|
|
&ast.StringNode{Key: "Name", Value: "d", CaseInsensitive: true},
|
|
}},
|
|
&ast.OperatorNode{Value: "AND"},
|
|
&ast.GroupNode{Nodes: []ast.Node{
|
|
&ast.StringNode{Key: "Name", Value: "e", CaseInsensitive: true},
|
|
}},
|
|
}, got)
|
|
}
|
|
|
|
func TestNormalize_ConvertsValueNodesToPointers(t *testing.T) {
|
|
got := norm(
|
|
ast.StringNode{Key: "name", Value: "x"},
|
|
ast.OperatorNode{Value: "AND"},
|
|
ast.DateTimeNode{Key: "mtime"},
|
|
)
|
|
require.Equal(t, []ast.Node{
|
|
&ast.StringNode{Key: "Name", Value: "x", CaseInsensitive: true},
|
|
&ast.OperatorNode{Value: "AND"},
|
|
&ast.DateTimeNode{Key: "Mtime"},
|
|
}, got)
|
|
}
|