diff --git a/packages/backend-core/src/db/lucene.ts b/packages/backend-core/src/db/lucene.ts index d9dddd0097..37768e934e 100644 --- a/packages/backend-core/src/db/lucene.ts +++ b/packages/backend-core/src/db/lucene.ts @@ -431,10 +431,28 @@ export class QueryBuilder { }) } if (this.#query.empty) { - build(this.#query.empty, (key: string) => `(*:* -${key}:["" TO *])`) + build(this.#query.empty, (key: string) => { + // Because the structure of an empty filter looks like this: + // { empty: { someKey: null } } + // + // The check inside of `build` does not set `allFiltersEmpty`, which results + // in weird behaviour when the empty filter is the only filter. We get around + // this by setting `allFiltersEmpty` to false here. + allFiltersEmpty = false + return `(*:* -${key}:["" TO *])` + }) } if (this.#query.notEmpty) { - build(this.#query.notEmpty, (key: string) => `${key}:["" TO *]`) + build(this.#query.notEmpty, (key: string) => { + // Because the structure of a notEmpty filter looks like this: + // { notEmpty: { someKey: null } } + // + // The check inside of `build` does not set `allFiltersEmpty`, which results + // in weird behaviour when the empty filter is the only filter. We get around + // this by setting `allFiltersEmpty` to false here. + allFiltersEmpty = false + return `${key}:["" TO *]` + }) } if (this.#query.oneOf) { build(this.#query.oneOf, oneOf) diff --git a/packages/server/src/api/routes/tests/search.spec.ts b/packages/server/src/api/routes/tests/search.spec.ts index 27c07d85be..1c098ebfa0 100644 --- a/packages/server/src/api/routes/tests/search.spec.ts +++ b/packages/server/src/api/routes/tests/search.spec.ts @@ -498,6 +498,31 @@ describe.each([ }).toFindNothing()) }) + describe("empty", () => { + it("finds no empty rows", () => + expectQuery({ empty: { name: null } }).toFindNothing()) + + it("should not be affected by when filter empty behaviour", () => + expectQuery({ + empty: { name: null }, + onEmptyFilter: EmptyFilterOption.RETURN_ALL, + }).toFindNothing()) + }) + + describe("notEmpty", () => { + it("finds all non-empty rows", () => + expectQuery({ notEmpty: { name: null } }).toContainExactly([ + { name: "foo" }, + { name: "bar" }, + ])) + + it("should not be affected by when filter empty behaviour", () => + expectQuery({ + notEmpty: { name: null }, + onEmptyFilter: EmptyFilterOption.RETURN_NONE, + }).toContainExactly([{ name: "foo" }, { name: "bar" }])) + }) + describe("sort", () => { it("sorts ascending", () => expectSearch({