feat(search): driveId as a query field

scope: takes an opaque resource id, which is hostile to hand-written
queries. Accept driveId:"<storage$space>" as a regular KQL field
instead: it resolves to the indexed RootID, and a bare drive id is
completed to the root resource id (a space root's opaque id is its
space id). Full root ids pass through untouched.

Combined with path: this gives a readable location scope without any
token stripping: both are plain fields, so they compose with groups,
OR and NOT like everything else.
This commit is contained in:
Dominik Schmidt committed 2026-08-31 15:23:25 +02:00
1 parent d38fbc8e52
commit 094baab7be
3 files changed
+35

No files matched your search

+17
View File
@@ -2,6 +2,7 @@ package query
import (
"strconv"
"strings"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
"reflect"
@@ -41,6 +42,9 @@ func normalizeNodes(nodes []ast.Node, resolve func(string) string, defaultKey st
if node.Key == "Type" {
node.Value = resourceType(node.Value)
}
if node.Key == "RootID" {
node.Value = completeRootID(node.Value)
}
if exp := mimetype.Expand(node.Key, node.Value); exp != nil {
out = append(out, normalizeNodes(exp, resolve, defaultKey)...)
continue
@@ -71,6 +75,19 @@ func normalizeNodes(nodes []ast.Node, resolve func(string) string, defaultKey st
return out
}
// completeRootID turns a driveId ("storage$space") into the full root
// resource id ("storage$space!space") stored in the index: a space root's
// opaque id is its space id. Full ids pass through untouched.
func completeRootID(v string) string {
if strings.Contains(v, "!") {
return v
}
if i := strings.LastIndex(v, "$"); i >= 0 && i+1 < len(v) {
return v + "!" + v[i+1:]
}
return v
}
// toPointer returns n as a pointer; the parser emits some nodes by value and the
// in-place key rewrites would be lost on those.
func toPointer(n ast.Node) ast.Node {
@@ -82,6 +82,23 @@ var _ = Describe("Normalize", func() {
}))
})
It("resolves driveId to RootID and completes the root id", func() {
got := norm(
&ast.StringNode{Key: "driveId", Value: "1$2"},
&ast.OperatorNode{Value: "AND"},
&ast.StringNode{Key: "driveid", Value: "1$2!4"},
&ast.OperatorNode{Value: "AND"},
&ast.StringNode{Key: "RootID", Value: "1$2"},
)
Expect(got).To(Equal([]ast.Node{
&ast.StringNode{Key: "RootID", Value: "1$2!2"},
&ast.OperatorNode{Value: "AND"},
&ast.StringNode{Key: "RootID", Value: "1$2!4"},
&ast.OperatorNode{Value: "AND"},
&ast.StringNode{Key: "RootID", Value: "1$2!2"},
}))
})
// 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.
+1
View File
@@ -13,6 +13,7 @@ import (
var aliases = map[string]string{
"tag": "Tags",
"favorite": "Favorites",
"driveid": "RootID",
}
// fieldIndex maps a lowercased KQL key to its canonical field name ("" is the