mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-07-08 21:15:09 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ab7a546366 |
@@ -61,12 +61,15 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
|
||||
writeValue(newValue: number[]): void {
|
||||
this.value = newValue
|
||||
}
|
||||
|
||||
registerOnChange(fn: any): void {
|
||||
this.onChange = fn
|
||||
}
|
||||
|
||||
registerOnTouched(fn: any): void {
|
||||
this.onTouched = fn
|
||||
}
|
||||
|
||||
setDisabledState?(isDisabled: boolean): void {
|
||||
this.disabled = isDisabled
|
||||
}
|
||||
@@ -74,6 +77,7 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
|
||||
ngOnInit(): void {
|
||||
this.tagService.listAll().subscribe((result) => {
|
||||
this.tags = result.results
|
||||
this.tagSearchTextCache.clear()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -113,15 +117,12 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
|
||||
|
||||
tags: Tag[] = []
|
||||
|
||||
private readonly tagSearchTextCache = new Map<number, string>()
|
||||
|
||||
public createTagRef: (name) => void
|
||||
|
||||
public searchFn = (term: string, tag: Tag): boolean =>
|
||||
matchesSearchText(
|
||||
[this.getParentChain(tag?.id).map((parent) => parent.name), tag?.name]
|
||||
.flat()
|
||||
.join(' '),
|
||||
term
|
||||
)
|
||||
matchesSearchText(this.getTagSearchText(tag), term)
|
||||
|
||||
getTag(id: number) {
|
||||
if (this.tags) {
|
||||
@@ -152,8 +153,8 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
|
||||
|
||||
private removeChildren(tagIDs: number[], tag: Tag) {
|
||||
if (tag.children?.length) {
|
||||
const childIDs = tag.children.map((child) => child.id)
|
||||
tagIDs = tagIDs.filter((id) => !childIDs.includes(id))
|
||||
const childIDs = new Set(tag.children.map((child) => child.id))
|
||||
tagIDs = tagIDs.filter((id) => !childIDs.has(id))
|
||||
for (const child of tag.children) {
|
||||
tagIDs = this.removeChildren(tagIDs, child)
|
||||
}
|
||||
@@ -188,6 +189,7 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
|
||||
tap((newTag) => {
|
||||
this.tagService.listAll().subscribe((tags) => {
|
||||
this.tags = tags.results
|
||||
this.tagSearchTextCache.clear()
|
||||
add && this.addTag(newTag.id)
|
||||
})
|
||||
})
|
||||
@@ -238,4 +240,22 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
|
||||
}
|
||||
return chain
|
||||
}
|
||||
|
||||
private getTagSearchText(tag: Tag): string {
|
||||
if (!tag) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const cachedSearchText = this.tagSearchTextCache.get(tag.id)
|
||||
if (cachedSearchText !== undefined) {
|
||||
return cachedSearchText
|
||||
}
|
||||
|
||||
const searchText = [
|
||||
...this.getParentChain(tag.id).map((parent) => parent.name),
|
||||
tag.name,
|
||||
].join(' ')
|
||||
this.tagSearchTextCache.set(tag.id, searchText)
|
||||
return searchText
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,4 +46,23 @@ describe('FilterPipe', () => {
|
||||
itemsReturned = pipe.transform(items, 'slug', 'slug')
|
||||
expect(itemsReturned).toEqual([items[0]])
|
||||
})
|
||||
|
||||
it('should filter matchingmodel items by normalized independent terms', () => {
|
||||
const pipe = new FilterPipe()
|
||||
const items: MatchingModel[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Financ\u00e9 Taxes 2026',
|
||||
slug: 'finance-taxes',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Receipt Archive 2026',
|
||||
slug: 'receipt-archive',
|
||||
},
|
||||
]
|
||||
|
||||
expect(pipe.transform(items, 'finance 26')).toEqual([items[0]])
|
||||
expect(pipe.transform(items, 'finance receipt')).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -8,8 +8,39 @@ export type SearchTextValue =
|
||||
| null
|
||||
| undefined
|
||||
|
||||
const normalizedStringCache = new Map<string, string>()
|
||||
const searchTermsCache = new Map<string, string[]>()
|
||||
|
||||
function normalizeString(value: string): string {
|
||||
const cachedValue = normalizedStringCache.get(value)
|
||||
if (cachedValue !== undefined) {
|
||||
return cachedValue
|
||||
}
|
||||
|
||||
const normalizedValue = normalizeSync(value).toLocaleLowerCase()
|
||||
normalizedStringCache.set(value, normalizedValue)
|
||||
return normalizedValue
|
||||
}
|
||||
|
||||
export function normalizeSearchText(value: SearchTextValue): string {
|
||||
return normalizeSync(String(value ?? '')).toLocaleLowerCase()
|
||||
return normalizeString(String(value ?? ''))
|
||||
}
|
||||
|
||||
function getSearchTerms(searchText: SearchTextValue): string[] {
|
||||
const normalizedSearchText = normalizeSearchText(searchText).trim()
|
||||
|
||||
if (!normalizedSearchText) {
|
||||
return []
|
||||
}
|
||||
|
||||
const cachedTerms = searchTermsCache.get(normalizedSearchText)
|
||||
if (cachedTerms !== undefined) {
|
||||
return cachedTerms
|
||||
}
|
||||
|
||||
const searchTerms = normalizedSearchText.split(/\s+/)
|
||||
searchTermsCache.set(normalizedSearchText, searchTerms)
|
||||
return searchTerms
|
||||
}
|
||||
|
||||
export function matchesSearchText(
|
||||
@@ -17,7 +48,7 @@ export function matchesSearchText(
|
||||
searchText: SearchTextValue
|
||||
): boolean {
|
||||
const normalizedValue = normalizeSearchText(value)
|
||||
const searchTerms = normalizeSearchText(searchText).trim().split(/\s+/)
|
||||
const searchTerms = getSearchTerms(searchText)
|
||||
|
||||
return searchTerms.every((term) => normalizedValue.includes(term))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user