diff --git a/services/search/pkg/bleve/backend.go b/services/search/pkg/bleve/backend.go index d1ae7f094d..2adcac2543 100644 --- a/services/search/pkg/bleve/backend.go +++ b/services/search/pkg/bleve/backend.go @@ -74,11 +74,8 @@ func (b *Backend) Search(_ context.Context, sir *searchService.SearchIndexReques ), }, ) - // Scope below the space root: restrict at query level so totals and - // paging respect the path too. Path is case-preserving (paths act as - // references, /Foo and /foo are distinct) and analyzed into its - // ancestor prefixes, so the folder term matches all of, and only, - // the scope. + // scope at query level so totals and paging respect it; the folder + // term matches the folder and its descendants (see PathAnalyzer) if requestedPath := utils.MakeRelativePath(sir.Ref.Path); requestedPath != "." { q.Conjuncts = append(q.Conjuncts, &query.TermQuery{FieldVal: "Path", Term: requestedPath}) } diff --git a/services/search/pkg/bleve/batch.go b/services/search/pkg/bleve/batch.go index 5c76c08b22..90943aa089 100644 --- a/services/search/pkg/bleve/batch.go +++ b/services/search/pkg/bleve/batch.go @@ -102,10 +102,7 @@ func (b *Batch) Purge(id string, onlyDeleted bool) error { }) } -// forSelfAndDescendants applies fn to the resource with the given id and, for -// a container, to every descendant, pushing the batch whenever it fills up. -// fn sees the root first; a root that fn mutates is not re-read, its original -// path drives the descendant lookup. +// fn sees the root first; the root's original path drives the descendant lookup func (b *Batch) forSelfAndDescendants(id string, fn func(*search.Resource) error) error { root, err := searchResourceByID(id, b.index) if err != nil { diff --git a/services/search/pkg/bleve/descendants_bench_test.go b/services/search/pkg/bleve/descendants_bench_test.go index c5fe6024bb..709d672a63 100644 --- a/services/search/pkg/bleve/descendants_bench_test.go +++ b/services/search/pkg/bleve/descendants_bench_test.go @@ -11,8 +11,7 @@ import ( "github.com/opencloud-eu/opencloud/services/search/pkg/search" ) -// peak live heap is what OOMs servers on folder delete/move, so it is reported -// as peak-MB next to the usual allocs +// reports peak live heap as peak-MB, the number that OOMs servers func BenchmarkSearchResourcesByPath(b *testing.B) { for _, n := range []int{20_000, 100_000} { b.Run(fmt.Sprintf("docs=%d", n), func(b *testing.B) { diff --git a/services/search/pkg/bleve/hierarchy/hierarchy.go b/services/search/pkg/bleve/hierarchy/hierarchy.go index dca60a387c..725aaa5e21 100644 --- a/services/search/pkg/bleve/hierarchy/hierarchy.go +++ b/services/search/pkg/bleve/hierarchy/hierarchy.go @@ -8,11 +8,8 @@ import ( "github.com/blevesearch/bleve/v2/registry" ) -// Name is the tokenizer type. It emits every prefix of the input up to each -// level boundary, so a term query on a level matches everything at or below it: -// "./a/b" with delimiter "/" becomes ".", "./a", "./a/b"; without a delimiter -// every byte is a level. tag_depth prefixes each token with "/" so a -// facet can be restricted to one level via TermPrefix. +// emits every prefix up to a level: "./a/b" -> ".", "./a", "./a/b" with +// delimiter "/", one level per byte without. tag_depth prepends "/". const Name = "hierarchy" type Tokenizer struct { diff --git a/services/search/pkg/bleve/index.go b/services/search/pkg/bleve/index.go index a6be1fac6b..9121809f1e 100644 --- a/services/search/pkg/bleve/index.go +++ b/services/search/pkg/bleve/index.go @@ -239,12 +239,8 @@ func searchResourceByID(id string, index bleve.Index) (*search.Resource, error) return matchToResource(res.Hits[0]), nil } -// forEachResourceByPath streams the descendants of the folder at lookupPath in -// the space rootID, excluding the folder itself. Path is analyzed into its -// ancestor prefixes, so the folder path is a single term shared by every -// descendant: one posting list, not one searcher per descendant. Hits are -// paged by id so live memory is bounded by the page, not the folder, and the -// caller may write to the index between pages. +// streams the descendants of lookupPath, excluding the folder itself; paged by +// id so memory is bounded by the page and fn may write to the index func forEachResourceByPath(rootID string, lookupPath string, index bleve.Index, fn func(*search.Resource) error) error { rootQuery := bleve.NewTermQuery(rootID) rootQuery.SetField("RootID") diff --git a/services/search/pkg/mapping/bleve.go b/services/search/pkg/mapping/bleve.go index d345d0b1a2..2921831582 100644 --- a/services/search/pkg/mapping/bleve.go +++ b/services/search/pkg/mapping/bleve.go @@ -83,8 +83,8 @@ func buildBleveDocMapping(t reflect.Type, overrides map[string]FieldOpts, prefix return doc, err } -// bleveKeywordMapping is a case-preserving keyword field; path fields are -// analyzed into their ancestor prefixes and stay out of _all by default. +// bleveKeywordMapping is a case-preserving keyword field; path fields stay out +// of _all by default. func bleveKeywordMapping(fieldType string, opts FieldOpts) *bleveMapping.FieldMapping { fm := bleve.NewKeywordFieldMapping() switch { diff --git a/services/search/pkg/mapping/opts.go b/services/search/pkg/mapping/opts.go index def03373e1..12d4e459df 100644 --- a/services/search/pkg/mapping/opts.go +++ b/services/search/pkg/mapping/opts.go @@ -27,8 +27,7 @@ const WordsSuffix = "_words" const WordsAnalyzer = "words" // PathAnalyzer names the bleve analyzer for TypePath fields: every ancestor -// prefix is a term, so a term query on a folder matches it and its descendants -// (OpenSearch uses its built-in path_hierarchy for the same field). +// prefix is a term, like path_hierarchy in OpenSearch. const PathAnalyzer = "path" // FieldOpts overrides the default type inference for a struct field. Keys in diff --git a/services/search/pkg/query/bleve/compiler.go b/services/search/pkg/query/bleve/compiler.go index 36f3563e80..b9ce4e6f9c 100644 --- a/services/search/pkg/query/bleve/compiler.go +++ b/services/search/pkg/query/bleve/compiler.go @@ -124,9 +124,8 @@ func walk(offset int, nodes []ast.Node) (bleveQuery.Query, int, error) { var q bleveQuery.Query = bleveQuery.NewQueryStringQuery(k + ":" + v) switch { case searchQuery.FieldIsPath(n.Key) && !isWildcard: - // a path value is one term in the hierarchy token stream and - // matches the folder and its descendants; a query string would - // analyze it into its prefixes and match everything under root + // a query string would analyze the path into its prefixes and + // match everything under root tq := bleveQuery.NewTermQuery(val) tq.SetField(k) q = tq diff --git a/services/search/pkg/query/bleve/compiler_test.go b/services/search/pkg/query/bleve/compiler_test.go index 51271aa40a..632a29b387 100644 --- a/services/search/pkg/query/bleve/compiler_test.go +++ b/services/search/pkg/query/bleve/compiler_test.go @@ -51,8 +51,7 @@ func Test_compile(t *testing.T) { wantErr: false, }, { - // a path is one term of the hierarchy analyzer and matches the - // folder itself and its descendants + // one term matches the folder itself and its descendants name: `path:/Foo`, args: &ast.Ast{ Nodes: []ast.Node{