mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-07-09 05:25:10 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ab7a546366 |
@@ -61,12 +61,15 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
|
|||||||
writeValue(newValue: number[]): void {
|
writeValue(newValue: number[]): void {
|
||||||
this.value = newValue
|
this.value = newValue
|
||||||
}
|
}
|
||||||
|
|
||||||
registerOnChange(fn: any): void {
|
registerOnChange(fn: any): void {
|
||||||
this.onChange = fn
|
this.onChange = fn
|
||||||
}
|
}
|
||||||
|
|
||||||
registerOnTouched(fn: any): void {
|
registerOnTouched(fn: any): void {
|
||||||
this.onTouched = fn
|
this.onTouched = fn
|
||||||
}
|
}
|
||||||
|
|
||||||
setDisabledState?(isDisabled: boolean): void {
|
setDisabledState?(isDisabled: boolean): void {
|
||||||
this.disabled = isDisabled
|
this.disabled = isDisabled
|
||||||
}
|
}
|
||||||
@@ -74,6 +77,7 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.tagService.listAll().subscribe((result) => {
|
this.tagService.listAll().subscribe((result) => {
|
||||||
this.tags = result.results
|
this.tags = result.results
|
||||||
|
this.tagSearchTextCache.clear()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,15 +117,12 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
|
|||||||
|
|
||||||
tags: Tag[] = []
|
tags: Tag[] = []
|
||||||
|
|
||||||
|
private readonly tagSearchTextCache = new Map<number, string>()
|
||||||
|
|
||||||
public createTagRef: (name) => void
|
public createTagRef: (name) => void
|
||||||
|
|
||||||
public searchFn = (term: string, tag: Tag): boolean =>
|
public searchFn = (term: string, tag: Tag): boolean =>
|
||||||
matchesSearchText(
|
matchesSearchText(this.getTagSearchText(tag), term)
|
||||||
[this.getParentChain(tag?.id).map((parent) => parent.name), tag?.name]
|
|
||||||
.flat()
|
|
||||||
.join(' '),
|
|
||||||
term
|
|
||||||
)
|
|
||||||
|
|
||||||
getTag(id: number) {
|
getTag(id: number) {
|
||||||
if (this.tags) {
|
if (this.tags) {
|
||||||
@@ -152,8 +153,8 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
|
|||||||
|
|
||||||
private removeChildren(tagIDs: number[], tag: Tag) {
|
private removeChildren(tagIDs: number[], tag: Tag) {
|
||||||
if (tag.children?.length) {
|
if (tag.children?.length) {
|
||||||
const childIDs = tag.children.map((child) => child.id)
|
const childIDs = new Set(tag.children.map((child) => child.id))
|
||||||
tagIDs = tagIDs.filter((id) => !childIDs.includes(id))
|
tagIDs = tagIDs.filter((id) => !childIDs.has(id))
|
||||||
for (const child of tag.children) {
|
for (const child of tag.children) {
|
||||||
tagIDs = this.removeChildren(tagIDs, child)
|
tagIDs = this.removeChildren(tagIDs, child)
|
||||||
}
|
}
|
||||||
@@ -188,6 +189,7 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
|
|||||||
tap((newTag) => {
|
tap((newTag) => {
|
||||||
this.tagService.listAll().subscribe((tags) => {
|
this.tagService.listAll().subscribe((tags) => {
|
||||||
this.tags = tags.results
|
this.tags = tags.results
|
||||||
|
this.tagSearchTextCache.clear()
|
||||||
add && this.addTag(newTag.id)
|
add && this.addTag(newTag.id)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -238,4 +240,22 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
|
|||||||
}
|
}
|
||||||
return chain
|
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')
|
itemsReturned = pipe.transform(items, 'slug', 'slug')
|
||||||
expect(itemsReturned).toEqual([items[0]])
|
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
|
| null
|
||||||
| undefined
|
| 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 {
|
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(
|
export function matchesSearchText(
|
||||||
@@ -17,7 +48,7 @@ export function matchesSearchText(
|
|||||||
searchText: SearchTextValue
|
searchText: SearchTextValue
|
||||||
): boolean {
|
): boolean {
|
||||||
const normalizedValue = normalizeSearchText(value)
|
const normalizedValue = normalizeSearchText(value)
|
||||||
const searchTerms = normalizeSearchText(searchText).trim().split(/\s+/)
|
const searchTerms = getSearchTerms(searchText)
|
||||||
|
|
||||||
return searchTerms.every((term) => normalizedValue.includes(term))
|
return searchTerms.every((term) => normalizedValue.includes(term))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user