fix: use substring match for contains/not_contains on relationship fields
Plain text values now use substring matching on wikilink stems so "Monday" matches [[Monday Ideas]], [[Monday Recap]], etc. Wikilink syntax values ([[...]]) still use exact stem matching. any_of/none_of remain exact match only. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -136,4 +136,58 @@ describe('evaluateView', () => {
|
||||
const result = evaluateView(view, entries)
|
||||
expect(result.map((e) => e.title)).toEqual(['A', 'B'])
|
||||
})
|
||||
|
||||
it('contains on relationship uses substring match for plain text', () => {
|
||||
const view: ViewDefinition = {
|
||||
name: 'Monday', icon: null, color: null, sort: null,
|
||||
filters: { all: [{ field: 'belongs to', op: 'contains', value: 'Monday' }] },
|
||||
}
|
||||
const entries = [
|
||||
makeEntry({ title: 'A', relationships: { 'belongs to': ['[[Monday Ideas]]'] } }),
|
||||
makeEntry({ title: 'B', relationships: { 'belongs to': ['[[Monday Recap]]'] } }),
|
||||
makeEntry({ title: 'C', relationships: { 'belongs to': ['[[Tuesday Ideas]]'] } }),
|
||||
]
|
||||
const result = evaluateView(view, entries)
|
||||
expect(result.map((e) => e.title)).toEqual(['A', 'B'])
|
||||
})
|
||||
|
||||
it('not_contains on relationship uses substring match for plain text', () => {
|
||||
const view: ViewDefinition = {
|
||||
name: 'Not Monday', icon: null, color: null, sort: null,
|
||||
filters: { all: [{ field: 'belongs to', op: 'not_contains', value: 'Monday' }] },
|
||||
}
|
||||
const entries = [
|
||||
makeEntry({ title: 'A', relationships: { 'belongs to': ['[[Monday Ideas]]'] } }),
|
||||
makeEntry({ title: 'B', relationships: { 'belongs to': ['[[Tuesday Ideas]]'] } }),
|
||||
makeEntry({ title: 'C', relationships: { 'belongs to': [] } }),
|
||||
]
|
||||
const result = evaluateView(view, entries)
|
||||
expect(result.map((e) => e.title)).toEqual(['B', 'C'])
|
||||
})
|
||||
|
||||
it('contains on relationship uses exact match for wikilink syntax', () => {
|
||||
const view: ViewDefinition = {
|
||||
name: 'Exact', icon: null, color: null, sort: null,
|
||||
filters: { all: [{ field: 'belongs to', op: 'contains', value: '[[Monday Ideas]]' }] },
|
||||
}
|
||||
const entries = [
|
||||
makeEntry({ title: 'A', relationships: { 'belongs to': ['[[Monday Ideas]]'] } }),
|
||||
makeEntry({ title: 'B', relationships: { 'belongs to': ['[[Monday Recap]]'] } }),
|
||||
]
|
||||
const result = evaluateView(view, entries)
|
||||
expect(result.map((e) => e.title)).toEqual(['A'])
|
||||
})
|
||||
|
||||
it('any_of / none_of on relationship always use exact stem match', () => {
|
||||
const view: ViewDefinition = {
|
||||
name: 'Exact list', icon: null, color: null, sort: null,
|
||||
filters: { all: [{ field: 'belongs to', op: 'any_of', value: ['[[Monday]]'] }] },
|
||||
}
|
||||
const entries = [
|
||||
makeEntry({ title: 'Exact', relationships: { 'belongs to': ['[[Monday]]'] } }),
|
||||
makeEntry({ title: 'Partial', relationships: { 'belongs to': ['[[Monday Ideas]]'] } }),
|
||||
]
|
||||
const result = evaluateView(view, entries)
|
||||
expect(result.map((e) => e.title)).toEqual(['Exact'])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -75,7 +75,10 @@ function evaluateCondition(cond: FilterCondition, entry: VaultEntry): boolean {
|
||||
|
||||
if (resolved.array) {
|
||||
const stem = wikilinkStem(condVal)
|
||||
const arrayMatch = (arr: string[]) => arr.some((item) => wikilinkStem(item) === stem)
|
||||
const isWikilink = condVal.trim().startsWith('[[')
|
||||
const arrayMatch = (arr: string[]) => arr.some((item) =>
|
||||
isWikilink ? wikilinkStem(item) === stem : wikilinkStem(item).includes(stem)
|
||||
)
|
||||
if (op === 'contains') return arrayMatch(resolved.array)
|
||||
if (op === 'not_contains') return !arrayMatch(resolved.array)
|
||||
if (op === 'any_of' && Array.isArray(value)) {
|
||||
|
||||
Reference in New Issue
Block a user