Skip to content

Commit

Permalink
[upstream] feat(search): add availableServices field
Browse files Browse the repository at this point in the history
This needed some modifications in the ES code to generate queries
  • Loading branch information
jahow committed Dec 6, 2024
1 parent 11bee87 commit 25ff56f
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,37 @@ describe('ElasticsearchService', () => {
},
})
})
it('handle values expressed as reg exp', () => {
const query = service['buildPayloadQuery'](
{
Org: {
'/world.*/': true,
'/*country^[fr|en]/': false,
},
},
{},
[]
)
expect(query).toMatchObject({
bool: {
filter: [
{
terms: {
isTemplate: ['n'],
},
},
{
query_string: {
query: 'Org:(/world.*/ OR -/*country^[fr|en]/)',
},
},
{
ids: { values: [] },
},
],
},
})
})
describe('any has special characters', () => {
let query
beforeEach(() => {
Expand Down Expand Up @@ -857,14 +888,16 @@ describe('ElasticsearchService', () => {
).toStrictEqual({
myFilters: {
filters: {
filter1: {
query_string: { query: 'field1:(100)' },
},
filter2: {
query_string: { query: 'field2:("value1" OR "value3")' },
},
filter3: {
query_string: { query: 'my own query' },
filters: {
filter1: {
query_string: { query: 'field1:(100)' },
},
filter2: {
query_string: { query: 'field2:("value1" OR "value3")' },
},
filter3: {
query_string: { query: 'my own query' },
},
},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,16 +213,17 @@ export class ElasticsearchService {
}

private filtersToQueryString(filters: FieldFilters): string {
const addQuote = (key: string) => (/^\/.+\/$/.test(key) ? key : `"${key}"`)
const makeQuery = (filter: FieldFilter): string => {
if (typeof filter === 'string') {
return filter
}
return Object.keys(filter)
.map((key) => {
if (filter[key] === true) {
return `"${key}"`
return addQuote(key)
}
return `-"${key}"`
return `-${addQuote(key)}`
})
.join(' OR ')
}
Expand Down Expand Up @@ -463,20 +464,22 @@ export class ElasticsearchService {
switch (aggregation.type) {
case 'filters':
return {
filters: Object.keys(aggregation.filters).reduce((prev, curr) => {
const filter = aggregation.filters[curr]
return {
...prev,
[curr]: {
query_string: {
query:
typeof filter === 'string'
? filter
: this.filtersToQueryString(filter),
filters: {
filters: Object.keys(aggregation.filters).reduce((prev, curr) => {
const filter = aggregation.filters[curr]
return {
...prev,
[curr]: {
query_string: {
query:
typeof filter === 'string'
? filter
: this.filtersToQueryString(filter),
},
},
},
}
}, {}),
}
}, {}),
},
}
case 'terms':
return {
Expand Down
2 changes: 2 additions & 0 deletions libs/feature/search/src/lib/utils/service/fields.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Injectable, Injector } from '@angular/core'
import {
AbstractSearchField,
AvailableServicesField,
FieldValue,
FullTextSearchField,
IsSpatialSearchField,
Expand Down Expand Up @@ -87,6 +88,7 @@ export class FieldsService {
'key'
),
user: new UserSearchField(this.injector),
availableServices: new AvailableServicesField(this.injector),
} as Record<string, AbstractSearchField>

get supportedFields() {
Expand Down
79 changes: 76 additions & 3 deletions libs/feature/search/src/lib/utils/service/fields.spec.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { lastValueFrom, of } from 'rxjs'
import {
AbstractSearchField,
AvailableServicesField,
FullTextSearchField,
IsSpatialSearchField,
TranslatedSearchField,
LicenseSearchField,
MultilingualSearchField,
OrganizationSearchField,
SimpleSearchField,
MultilingualSearchField,
TranslatedSearchField,
UserSearchField,
} from './fields'
import { TestBed } from '@angular/core/testing'
Expand All @@ -29,7 +30,6 @@ class ElasticsearchServiceMock {
class RecordsRepositoryMock {
aggregate = jest.fn((aggregations) => {
const aggName = Object.keys(aggregations)[0]
const sortType = aggregations[aggName].sort[1]
if (aggName.startsWith('is'))
return of({
[aggName]: {
Expand Down Expand Up @@ -118,6 +118,21 @@ class RecordsRepositoryMock {
],
},
})
if (aggName === 'availableServices')
return of({
availableServices: {
buckets: [
{
term: 'view',
count: 10,
},
{
term: 'download',
count: 5,
},
],
},
})
const buckets = [
{
term: 'First value',
Expand All @@ -136,6 +151,7 @@ class RecordsRepositoryMock {
count: 1,
},
]
const sortType = aggregations[aggName].sort?.[1]
if (sortType === 'count') {
buckets.sort((a, b) => b.count - a.count)
}
Expand Down Expand Up @@ -686,6 +702,7 @@ describe('search fields implementations', () => {
})
})
})

describe('UserSearchField', () => {
beforeEach(() => {
searchField = new UserSearchField(injector)
Expand Down Expand Up @@ -723,4 +740,60 @@ describe('search fields implementations', () => {
})
})
})

describe('AvailableServicesField', () => {
beforeEach(() => {
searchField = new AvailableServicesField(injector)
})
describe('#getAvailableValues', () => {
let values
beforeEach(async () => {
values = await lastValueFrom(searchField.getAvailableValues())
})
it('returns the available values', () => {
expect(values).toEqual([
{
label: 'search.filters.availableServices.view (10)',
value: 'view',
},
{
label: 'search.filters.availableServices.download (5)',
value: 'download',
},
])
})
})
describe('#getFiltersForValues', () => {
let filter
beforeEach(async () => {
filter = await lastValueFrom(
searchField.getFiltersForValues(['view', 'download'])
)
})
it('returns filter for both values', () => {
expect(filter).toEqual({
linkProtocol: {
'/OGC:WFS.*/': true,
'/OGC:WMT?S.*/': true,
},
})
})
})
describe('#getValuesForFilters', () => {
let values
beforeEach(async () => {
values = await lastValueFrom(
searchField.getValuesForFilter({
linkProtocol: {
'/OGC:WFS.*/': false,
'/OGC:WMT?S.*/': true,
},
})
)
})
it('returns value with an enabled filter', () => {
expect(values).toEqual(['view'])
})
})
})
})
55 changes: 55 additions & 0 deletions libs/feature/search/src/lib/utils/service/fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { PlatformServiceInterface } from '@geonetwork-ui/common/domain/platform.
import {
AggregationBuckets,
AggregationsParams,
FieldFilter,
FieldFilterByExpression,
FieldFilters,
TermBucket,
Expand Down Expand Up @@ -372,3 +373,57 @@ export class UserSearchField extends SimpleSearchField {
return undefined
}
}

marker('search.filters.availableServices.view')
marker('search.filters.availableServices.download')

export class AvailableServicesField extends SimpleSearchField {
private translateService = this.injector.get(TranslateService)

constructor(injector: Injector) {
super('availableServices', injector, 'asc')
}

linkProtocolViewFilter = '/OGC:WMT?S.*/'
linkProtocolDownloadFilter = '/OGC:WFS.*/'

protected async getBucketLabel(bucket: TermBucket) {
return firstValueFrom(
this.translateService.get(
`search.filters.availableServices.${bucket.term}`
)
)
}

protected getAggregations(): AggregationsParams {
return {
availableServices: {
type: 'filters',
filters: {
view: `+linkProtocol:${this.linkProtocolViewFilter}`,
download: `+linkProtocol:${this.linkProtocolDownloadFilter}`,
},
},
}
}

getFiltersForValues(values: FieldValue[]): Observable<FieldFilters> {
const filters: FieldFilter = {}
if (values.includes('view')) filters[this.linkProtocolViewFilter] = true
if (values.includes('download'))
filters[this.linkProtocolDownloadFilter] = true

return of({
linkProtocol: filters,
})
}

getValuesForFilter(filters: FieldFilters): Observable<FieldValue[]> {
const linkFilter = filters.linkProtocol
if (!linkFilter) return of([])
const values = []
if (linkFilter[this.linkProtocolViewFilter]) values.push('view')
if (linkFilter[this.linkProtocolDownloadFilter]) values.push('download')
return of(values)
}
}

0 comments on commit 25ff56f

Please sign in to comment.