mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-05-17 12:05:24 +00:00
Enhancement: unify text search to use tantivy (#12485)
This commit is contained in:
@@ -428,7 +428,7 @@ describe('BulkEditorComponent', () => {
|
||||
req.flush(true)
|
||||
expect(req.request.body).toEqual({
|
||||
all: true,
|
||||
filters: { title__icontains: 'apple' },
|
||||
filters: { title_search: 'apple' },
|
||||
method: 'modify_tags',
|
||||
parameters: { add_tags: [101], remove_tags: [] },
|
||||
})
|
||||
|
||||
+114
-26
@@ -67,6 +67,8 @@ import {
|
||||
FILTER_OWNER_DOES_NOT_INCLUDE,
|
||||
FILTER_OWNER_ISNULL,
|
||||
FILTER_SHARED_BY_USER,
|
||||
FILTER_SIMPLE_TEXT,
|
||||
FILTER_SIMPLE_TITLE,
|
||||
FILTER_STORAGE_PATH,
|
||||
FILTER_TITLE,
|
||||
FILTER_TITLE_CONTENT,
|
||||
@@ -312,7 +314,7 @@ describe('FilterEditorComponent', () => {
|
||||
expect(component.textFilter).toEqual(null)
|
||||
component.filterRules = [
|
||||
{
|
||||
rule_type: FILTER_TITLE_CONTENT,
|
||||
rule_type: FILTER_SIMPLE_TEXT,
|
||||
value: 'foo',
|
||||
},
|
||||
]
|
||||
@@ -320,6 +322,18 @@ describe('FilterEditorComponent', () => {
|
||||
expect(component.textFilterTarget).toEqual('title-content') // TEXT_FILTER_TARGET_TITLE_CONTENT
|
||||
}))
|
||||
|
||||
it('should ingest legacy text filter rules for doc title + content', fakeAsync(() => {
|
||||
expect(component.textFilter).toEqual(null)
|
||||
component.filterRules = [
|
||||
{
|
||||
rule_type: FILTER_TITLE_CONTENT,
|
||||
value: 'legacy foo',
|
||||
},
|
||||
]
|
||||
expect(component.textFilter).toEqual('legacy foo')
|
||||
expect(component.textFilterTarget).toEqual('title-content') // TEXT_FILTER_TARGET_TITLE_CONTENT
|
||||
}))
|
||||
|
||||
it('should ingest text filter rules for doc asn', fakeAsync(() => {
|
||||
expect(component.textFilter).toEqual(null)
|
||||
component.filterRules = [
|
||||
@@ -1117,7 +1131,7 @@ describe('FilterEditorComponent', () => {
|
||||
expect(component.textFilter).toEqual('foo')
|
||||
expect(component.filterRules).toEqual([
|
||||
{
|
||||
rule_type: FILTER_TITLE_CONTENT,
|
||||
rule_type: FILTER_SIMPLE_TEXT,
|
||||
value: 'foo',
|
||||
},
|
||||
])
|
||||
@@ -1136,7 +1150,7 @@ describe('FilterEditorComponent', () => {
|
||||
expect(component.textFilterTarget).toEqual('title')
|
||||
expect(component.filterRules).toEqual([
|
||||
{
|
||||
rule_type: FILTER_TITLE,
|
||||
rule_type: FILTER_SIMPLE_TITLE,
|
||||
value: 'foo',
|
||||
},
|
||||
])
|
||||
@@ -1250,30 +1264,12 @@ describe('FilterEditorComponent', () => {
|
||||
])
|
||||
}))
|
||||
|
||||
it('should convert user input to correct filter rules on custom fields query', fakeAsync(() => {
|
||||
component.textFilterInput.nativeElement.value = 'foo'
|
||||
component.textFilterInput.nativeElement.dispatchEvent(new Event('input'))
|
||||
const textFieldTargetDropdown = fixture.debugElement.queryAll(
|
||||
By.directive(NgbDropdownItem)
|
||||
)[3]
|
||||
textFieldTargetDropdown.triggerEventHandler('click') // TEXT_FILTER_TARGET_CUSTOM_FIELDS
|
||||
fixture.detectChanges()
|
||||
tick(400)
|
||||
expect(component.textFilterTarget).toEqual('custom-fields')
|
||||
expect(component.filterRules).toEqual([
|
||||
{
|
||||
rule_type: FILTER_CUSTOM_FIELDS_TEXT,
|
||||
value: 'foo',
|
||||
},
|
||||
])
|
||||
}))
|
||||
|
||||
it('should convert user input to correct filter rules on mime type', fakeAsync(() => {
|
||||
component.textFilterInput.nativeElement.value = 'pdf'
|
||||
component.textFilterInput.nativeElement.dispatchEvent(new Event('input'))
|
||||
const textFieldTargetDropdown = fixture.debugElement.queryAll(
|
||||
By.directive(NgbDropdownItem)
|
||||
)[4]
|
||||
)[3]
|
||||
textFieldTargetDropdown.triggerEventHandler('click') // TEXT_FILTER_TARGET_MIME_TYPE
|
||||
fixture.detectChanges()
|
||||
tick(400)
|
||||
@@ -1291,8 +1287,8 @@ describe('FilterEditorComponent', () => {
|
||||
component.textFilterInput.nativeElement.dispatchEvent(new Event('input'))
|
||||
const textFieldTargetDropdown = fixture.debugElement.queryAll(
|
||||
By.directive(NgbDropdownItem)
|
||||
)[5]
|
||||
textFieldTargetDropdown.triggerEventHandler('click') // TEXT_FILTER_TARGET_ASN
|
||||
)[4]
|
||||
textFieldTargetDropdown.triggerEventHandler('click') // TEXT_FILTER_TARGET_FULLTEXT_QUERY
|
||||
fixture.detectChanges()
|
||||
tick(400)
|
||||
expect(component.textFilterTarget).toEqual('fulltext-query')
|
||||
@@ -1696,12 +1692,56 @@ describe('FilterEditorComponent', () => {
|
||||
])
|
||||
}))
|
||||
|
||||
it('should convert legacy title filters into full text query when adding a created relative date', fakeAsync(() => {
|
||||
component.filterRules = [
|
||||
{
|
||||
rule_type: FILTER_TITLE,
|
||||
value: 'foo',
|
||||
},
|
||||
]
|
||||
const dateCreatedDropdown = fixture.debugElement.queryAll(
|
||||
By.directive(DatesDropdownComponent)
|
||||
)[0]
|
||||
component.dateCreatedRelativeDate = RelativeDate.WITHIN_1_WEEK
|
||||
dateCreatedDropdown.triggerEventHandler('datesSet')
|
||||
fixture.detectChanges()
|
||||
tick(400)
|
||||
expect(component.filterRules).toEqual([
|
||||
{
|
||||
rule_type: FILTER_FULLTEXT_QUERY,
|
||||
value: 'foo,created:[-1 week to now]',
|
||||
},
|
||||
])
|
||||
}))
|
||||
|
||||
it('should convert simple title filters into full text query when adding a created relative date', fakeAsync(() => {
|
||||
component.filterRules = [
|
||||
{
|
||||
rule_type: FILTER_SIMPLE_TITLE,
|
||||
value: 'foo',
|
||||
},
|
||||
]
|
||||
const dateCreatedDropdown = fixture.debugElement.queryAll(
|
||||
By.directive(DatesDropdownComponent)
|
||||
)[0]
|
||||
component.dateCreatedRelativeDate = RelativeDate.WITHIN_1_WEEK
|
||||
dateCreatedDropdown.triggerEventHandler('datesSet')
|
||||
fixture.detectChanges()
|
||||
tick(400)
|
||||
expect(component.filterRules).toEqual([
|
||||
{
|
||||
rule_type: FILTER_FULLTEXT_QUERY,
|
||||
value: 'foo,created:[-1 week to now]',
|
||||
},
|
||||
])
|
||||
}))
|
||||
|
||||
it('should leave relative dates not in quick list intact', fakeAsync(() => {
|
||||
component.textFilterInput.nativeElement.value = 'created:[-2 week to now]'
|
||||
component.textFilterInput.nativeElement.dispatchEvent(new Event('input'))
|
||||
const textFieldTargetDropdown = fixture.debugElement.queryAll(
|
||||
By.directive(NgbDropdownItem)
|
||||
)[5]
|
||||
)[4]
|
||||
textFieldTargetDropdown.triggerEventHandler('click')
|
||||
fixture.detectChanges()
|
||||
tick(400)
|
||||
@@ -2031,12 +2071,30 @@ describe('FilterEditorComponent', () => {
|
||||
|
||||
component.filterRules = [
|
||||
{
|
||||
rule_type: FILTER_TITLE,
|
||||
rule_type: FILTER_SIMPLE_TITLE,
|
||||
value: 'foo',
|
||||
},
|
||||
]
|
||||
expect(component.generateFilterName()).toEqual('Title: foo')
|
||||
|
||||
component.filterRules = [
|
||||
{
|
||||
rule_type: FILTER_TITLE_CONTENT,
|
||||
value: 'legacy foo',
|
||||
},
|
||||
]
|
||||
expect(component.generateFilterName()).toEqual(
|
||||
'Title & content: legacy foo'
|
||||
)
|
||||
|
||||
component.filterRules = [
|
||||
{
|
||||
rule_type: FILTER_SIMPLE_TEXT,
|
||||
value: 'foo',
|
||||
},
|
||||
]
|
||||
expect(component.generateFilterName()).toEqual('Title & content: foo')
|
||||
|
||||
component.filterRules = [
|
||||
{
|
||||
rule_type: FILTER_ASN,
|
||||
@@ -2156,6 +2214,36 @@ describe('FilterEditorComponent', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('should hide deprecated custom fields target from default text filter targets', () => {
|
||||
expect(component.textFilterTargets).not.toContainEqual({
|
||||
id: 'custom-fields',
|
||||
name: $localize`Custom fields (Deprecated)`,
|
||||
})
|
||||
})
|
||||
|
||||
it('should keep deprecated custom fields target available for legacy filters', fakeAsync(() => {
|
||||
component.filterRules = [
|
||||
{
|
||||
rule_type: FILTER_CUSTOM_FIELDS_TEXT,
|
||||
value: 'foo',
|
||||
},
|
||||
]
|
||||
fixture.detectChanges()
|
||||
tick()
|
||||
|
||||
expect(component.textFilterTarget).toEqual('custom-fields')
|
||||
expect(component.textFilterTargets).toContainEqual({
|
||||
id: 'custom-fields',
|
||||
name: $localize`Custom fields (Deprecated)`,
|
||||
})
|
||||
expect(component.filterRules).toEqual([
|
||||
{
|
||||
rule_type: FILTER_CUSTOM_FIELDS_TEXT,
|
||||
value: 'foo',
|
||||
},
|
||||
])
|
||||
}))
|
||||
|
||||
it('should call autocomplete endpoint on input', fakeAsync(() => {
|
||||
component.textFilterTarget = 'fulltext-query' // TEXT_FILTER_TARGET_FULLTEXT_QUERY
|
||||
const autocompleteSpy = jest.spyOn(searchService, 'autocomplete')
|
||||
|
||||
@@ -71,6 +71,8 @@ import {
|
||||
FILTER_OWNER_DOES_NOT_INCLUDE,
|
||||
FILTER_OWNER_ISNULL,
|
||||
FILTER_SHARED_BY_USER,
|
||||
FILTER_SIMPLE_TEXT,
|
||||
FILTER_SIMPLE_TITLE,
|
||||
FILTER_STORAGE_PATH,
|
||||
FILTER_TITLE,
|
||||
FILTER_TITLE_CONTENT,
|
||||
@@ -195,10 +197,6 @@ const DEFAULT_TEXT_FILTER_TARGET_OPTIONS = [
|
||||
name: $localize`Title & content`,
|
||||
},
|
||||
{ id: TEXT_FILTER_TARGET_ASN, name: $localize`ASN` },
|
||||
{
|
||||
id: TEXT_FILTER_TARGET_CUSTOM_FIELDS,
|
||||
name: $localize`Custom fields`,
|
||||
},
|
||||
{ id: TEXT_FILTER_TARGET_MIME_TYPE, name: $localize`File type` },
|
||||
{
|
||||
id: TEXT_FILTER_TARGET_FULLTEXT_QUERY,
|
||||
@@ -206,6 +204,12 @@ const DEFAULT_TEXT_FILTER_TARGET_OPTIONS = [
|
||||
},
|
||||
]
|
||||
|
||||
const DEPRECATED_CUSTOM_FIELDS_TEXT_FILTER_TARGET_OPTION = {
|
||||
// Kept only so legacy saved views can render and be edited away from, remove me eventually
|
||||
id: TEXT_FILTER_TARGET_CUSTOM_FIELDS,
|
||||
name: $localize`Custom fields (Deprecated)`,
|
||||
}
|
||||
|
||||
const TEXT_FILTER_TARGET_MORELIKE_OPTION = {
|
||||
id: TEXT_FILTER_TARGET_FULLTEXT_MORELIKE,
|
||||
name: $localize`More like`,
|
||||
@@ -318,8 +322,13 @@ export class FilterEditorComponent
|
||||
return $localize`Custom fields query`
|
||||
|
||||
case FILTER_TITLE:
|
||||
case FILTER_SIMPLE_TITLE:
|
||||
return $localize`Title: ${rule.value}`
|
||||
|
||||
case FILTER_TITLE_CONTENT:
|
||||
case FILTER_SIMPLE_TEXT:
|
||||
return $localize`Title & content: ${rule.value}`
|
||||
|
||||
case FILTER_ASN:
|
||||
return $localize`ASN: ${rule.value}`
|
||||
|
||||
@@ -353,12 +362,16 @@ export class FilterEditorComponent
|
||||
_moreLikeDoc: Document
|
||||
|
||||
get textFilterTargets() {
|
||||
let targets = DEFAULT_TEXT_FILTER_TARGET_OPTIONS
|
||||
if (this.textFilterTarget == TEXT_FILTER_TARGET_FULLTEXT_MORELIKE) {
|
||||
return DEFAULT_TEXT_FILTER_TARGET_OPTIONS.concat([
|
||||
TEXT_FILTER_TARGET_MORELIKE_OPTION,
|
||||
targets = targets.concat([TEXT_FILTER_TARGET_MORELIKE_OPTION])
|
||||
}
|
||||
if (this.textFilterTarget == TEXT_FILTER_TARGET_CUSTOM_FIELDS) {
|
||||
targets = targets.concat([
|
||||
DEPRECATED_CUSTOM_FIELDS_TEXT_FILTER_TARGET_OPTION,
|
||||
])
|
||||
}
|
||||
return DEFAULT_TEXT_FILTER_TARGET_OPTIONS
|
||||
return targets
|
||||
}
|
||||
|
||||
textFilterTarget = TEXT_FILTER_TARGET_TITLE_CONTENT
|
||||
@@ -437,10 +450,12 @@ export class FilterEditorComponent
|
||||
value.forEach((rule) => {
|
||||
switch (rule.rule_type) {
|
||||
case FILTER_TITLE:
|
||||
case FILTER_SIMPLE_TITLE:
|
||||
this._textFilter = rule.value
|
||||
this.textFilterTarget = TEXT_FILTER_TARGET_TITLE
|
||||
break
|
||||
case FILTER_TITLE_CONTENT:
|
||||
case FILTER_SIMPLE_TEXT:
|
||||
this._textFilter = rule.value
|
||||
this.textFilterTarget = TEXT_FILTER_TARGET_TITLE_CONTENT
|
||||
break
|
||||
@@ -762,12 +777,15 @@ export class FilterEditorComponent
|
||||
this.textFilterTarget == TEXT_FILTER_TARGET_TITLE_CONTENT
|
||||
) {
|
||||
filterRules.push({
|
||||
rule_type: FILTER_TITLE_CONTENT,
|
||||
rule_type: FILTER_SIMPLE_TEXT,
|
||||
value: this._textFilter.trim(),
|
||||
})
|
||||
}
|
||||
if (this._textFilter && this.textFilterTarget == TEXT_FILTER_TARGET_TITLE) {
|
||||
filterRules.push({ rule_type: FILTER_TITLE, value: this._textFilter })
|
||||
filterRules.push({
|
||||
rule_type: FILTER_SIMPLE_TITLE,
|
||||
value: this._textFilter,
|
||||
})
|
||||
}
|
||||
if (this.textFilterTarget == TEXT_FILTER_TARGET_ASN) {
|
||||
if (
|
||||
@@ -1009,7 +1027,10 @@ export class FilterEditorComponent
|
||||
) {
|
||||
existingRule = filterRules.find(
|
||||
(fr) =>
|
||||
fr.rule_type == FILTER_TITLE_CONTENT || fr.rule_type == FILTER_TITLE
|
||||
fr.rule_type == FILTER_TITLE_CONTENT ||
|
||||
fr.rule_type == FILTER_SIMPLE_TEXT ||
|
||||
fr.rule_type == FILTER_TITLE ||
|
||||
fr.rule_type == FILTER_SIMPLE_TITLE
|
||||
)
|
||||
existingRule.rule_type = FILTER_FULLTEXT_QUERY
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user