fix(176): split fulltext matches/notMatches from text contains

This commit is contained in:
2026-07-18 02:41:50 +02:00
parent 7f7062729d
commit bc524d0b53
3 changed files with 12 additions and 6 deletions
+5
View File
@@ -18,6 +18,11 @@ describe('compile', () => {
expect(compile({ match: 'all', children: [{ field: 'title', operator: 'startsWith', value: 'The' }] })).toBe('title:The*');
});
it('quotes fulltext matches / notMatches (distinct from text contains)', () => {
expect(compile({ match: 'all', children: [{ field: 'plot', operator: 'matches', value: 'car chase' }] })).toBe('plot:"car chase"');
expect(compile({ match: 'all', children: [{ field: 'plot', operator: 'notMatches', value: 'car' }] })).toBe('NOT plot:"car"');
});
it('emits numeric ranges', () => {
expect(compile({ match: 'all', children: [{ field: 'minutes', operator: 'gt', value: '30' }] })).toBe('minutes:{30 TO *}');
expect(compile({ match: 'all', children: [{ field: 'minutes', operator: 'lt', value: '90' }] })).toBe('minutes:{* TO 90}');
+2 -2
View File
@@ -15,15 +15,15 @@ function compileRule(rule: Rule): string {
const v = rule.value;
switch (rule.operator) {
case 'is':
case 'matches': // fulltext: same quoted form as text `is`, disambiguated by field type on parse
return `${f}:${quote(v)}`;
case 'isNot':
case 'notMatches':
return `NOT ${f}:${quote(v)}`;
case 'contains':
return `${f}:*${escapeWild(v)}*`;
case 'startsWith':
return `${f}:${escapeWild(v)}*`;
case 'notContains':
return `NOT ${f}:${quote(v)}`;
case 'eq':
return `${f}:${v}`;
case 'gt':
+5 -4
View File
@@ -2,9 +2,10 @@ export type FieldType = 'text' | 'fulltext' | 'number' | 'date' | 'enum';
export type Match = 'all' | 'any';
export type Operator =
| 'is' | 'isNot' | 'contains' | 'startsWith' | 'notContains'
| 'eq' | 'gt' | 'lt' | 'between'
| 'before' | 'after';
| 'is' | 'isNot' | 'contains' | 'startsWith' // text
| 'matches' | 'notMatches' // fulltext
| 'eq' | 'gt' | 'lt' | 'between' // number
| 'before' | 'after'; // date
export interface Rule {
field: string;
@@ -25,7 +26,7 @@ export function isGroup(node: Rule | Group): node is Group {
// Which fields each catalog type exposes as operators (used by the UI and tests).
export const OPERATORS_BY_TYPE: Record<FieldType, Operator[]> = {
text: ['is', 'isNot', 'contains', 'startsWith'],
fulltext: ['contains', 'notContains'],
fulltext: ['matches', 'notMatches'],
enum: ['is', 'isNot'],
number: ['eq', 'gt', 'lt', 'between'],
date: ['before', 'after', 'between']