Skip to content

Commit

Permalink
filters building
Browse files Browse the repository at this point in the history
  • Loading branch information
charmingduchess committed Dec 9, 2024
1 parent f3657b4 commit 338ef7b
Showing 4 changed files with 79 additions and 43 deletions.
28 changes: 14 additions & 14 deletions lib/elasticsearch/config.js
Original file line number Diff line number Diff line change
@@ -74,20 +74,20 @@ const SEARCH_SCOPES = {
}

const FILTER_CONFIG = {
recordType: { operator: 'match', field: 'recordTypeId', repeatable: true },
owner: { operator: 'match', field: 'items.owner_packed', repeatable: true, path: 'items' },
subjectLiteral: { operator: 'match', field: 'subjectLiteral_exploded', repeatable: true },
holdingLocation: { operator: 'match', field: 'items.holdingLocation_packed', repeatable: true, path: 'items' },
buildingLocation: { operator: 'match', field: 'buildingLocationIds', repeatable: true },
language: { operator: 'match', field: 'language_packed', repeatable: true },
materialType: { operator: 'match', field: 'materialType_packed', repeatable: true },
mediaType: { operator: 'match', field: 'mediaType_packed', repeatable: true },
carrierType: { operator: 'match', field: 'carrierType_packed', repeatable: true },
publisher: { operator: 'match', field: 'publisherLiteral.raw', repeatable: true },
contributorLiteral: { operator: 'match', field: 'contributorLiteral.raw', repeatable: true },
creatorLiteral: { operator: 'match', field: 'creatorLiteral.raw', repeatable: true },
issuance: { operator: 'match', field: 'issuance_packed', repeatable: true },
createdYear: { operator: 'match', field: 'createdYear', repeatable: true },
recordType: { operator: 'match', field: ['recordTypeId'], repeatable: true },
owner: { operator: 'match', field: ['items.owner_packed'], repeatable: true, path: 'items' },
subjectLiteral: { operator: 'match', field: ['subjectLiteral_exploded'], repeatable: true },
holdingLocation: { operator: 'match', field: ['items.holdingLocation_packed'], repeatable: true, path: 'items' },
buildingLocation: { operator: 'match', field: ['buildingLocationIds'], repeatable: true },
language: { operator: 'match', field: ['language_packed'], repeatable: true },
materialType: { operator: 'match', field: ['materialType_packed'], repeatable: true },
mediaType: { operator: 'match', field: ['mediaType_packed'], repeatable: true },
carrierType: { operator: 'match', field: ['carrierType_packed'], repeatable: true },
publisher: { operator: 'match', field: ['publisherLiteral.raw'], repeatable: true },
contributorLiteral: { operator: 'match', field: ['contributorLiteral.raw'], repeatable: true },
creatorLiteral: { operator: 'match', field: ['creatorLiteral.raw'], repeatable: true },
issuance: { operator: 'match', field: ['issuance_packed'], repeatable: true },
createdYear: { operator: 'match', field: ['createdYear'], repeatable: true },
dateAfter: {
operator: 'custom',
type: 'int'
29 changes: 20 additions & 9 deletions lib/elasticsearch/elastic-query-builder.js
Original file line number Diff line number Diff line change
@@ -488,7 +488,7 @@ class ElasticQueryBuilder {
}
}

buildPackedFieldClause (field, value) {
buildPackedFieldClause (value, field) {
// Figure out the base property (e.g. 'owner')
const baseField = field.replace(/_packed$/, '')
// Allow supplied val to match against either id or value:
@@ -502,27 +502,38 @@ class ElasticQueryBuilder {
}
}

buildMultiFieldClause (value, fields) {
return {
bool:
{ should: fields.map(field => ({ term: { [field]: value } })) }
}
}

// This builds a filter cause from the value:
buildClause (value, field) {
// If filtering on a packed field and value isn't a packed value:
if (value.indexOf('||') < 0 && field.match(/_packed$/)) {
return this.buildPackedFieldClause(field, value)
const filterMatchesOnMoreThanOneField = field.length > 1
if (filterMatchesOnMoreThanOneField) {
return this.buildMultiFieldClause(value, field)
}
field = field[0]
const valueIsNotPackedValue = value.indexOf('||') < 0
const isPackedField = field.match(/_packed$/)
if (isPackedField && valueIsNotPackedValue) {
return this.buildPackedFieldClause(value, field)
} else return { term: { [field]: value } }
}

buildSimpleMatchFilters (simpleMatchFilters) {
return simpleMatchFilters.map((prop) => {
const config = FILTER_CONFIG[prop]
const value = this.request.params.filters[prop]
let value = this.request.params.filters[prop]

// If multiple values given, let's join them with 'should', causing it to operate as a boolean OR
// Note: using 'must' here makes it a boolean AND
const booleanOperator = 'should'

let clause
const singleValueArray = (Array.isArray(value) && value.length === 1)
if (singleValueArray) clause = this.buildClause(value[0], config.field)
else clause = { bool: { [booleanOperator]: value.map((value) => this.buildClause(value, config.field)) } }
if (Array.isArray(value) && value.length === 1) value = value.shift()
const clause = (Array.isArray(value)) ? { bool: { [booleanOperator]: value.map((value) => this.buildClause(config.field, value)) } } : this.buildClause(config.field, value)

return { path: config.path, clause }
})
Empty file.
65 changes: 45 additions & 20 deletions test/elastic-query-builder.test.js
Original file line number Diff line number Diff line change
@@ -4,9 +4,38 @@ const ElasticQueryBuilder = require('../lib/elasticsearch/elastic-query-builder'
const ApiRequest = require('../lib/api-request')

describe('ElasticQueryBuilder', () => {
describe.only('buildSimpleMatchFilters', () => {
describe('buildClause', () => {
it('can handle multiple fields', () => {
expect(ElasticQueryBuilder.prototype.buildClause('value', ['field', 'parallelField']))
.to.deep.equal({
bool:
{
should: [
{ term: { field: 'value' } },
{ term: { parallelField: 'value' } }]
}
})
})
it('can handle packed fields', () => {
expect(ElasticQueryBuilder.prototype.buildClause('not packed value', ['field_packed']))
.to.deep.equal({
bool: {
should: [
{ term: { 'field.id': 'not packed value' } },
{ term: { 'field.label': 'not packed value' } }
]
}
})
})
it('can handle the simple case', () => {
expect(ElasticQueryBuilder.prototype.buildClause('value', ['field']))
.to.deep.equal({ term: { field: 'value' } })
})
})
describe('buildSimpleMatchFilters', () => {
const mockQueryBuilderFactory = (request) => ({
request,
buildMultiFieldClause: ElasticQueryBuilder.prototype.buildMultiFieldClause,
buildSimpleMatchFilters: ElasticQueryBuilder.prototype.buildSimpleMatchFilters,
buildClause: ElasticQueryBuilder.prototype.buildClause,
buildPackedFieldClause: ElasticQueryBuilder.prototype.buildPackedFieldClause
@@ -26,25 +55,21 @@ describe('ElasticQueryBuilder', () => {
}
])
})
// it('can handle (multiple) single value, single match field filters, strings', () => {
// const request = new ApiRequest({ filters: { buildingLocation: 'toast', subjectLiteral: 'spaghetti' } })
// const mockQueryBuilder = {
// request,
// buildSimpleMatchFilters: ElasticQueryBuilder.prototype.buildSimpleMatchFilters,
// buildClause: ElasticQueryBuilder.prototype.buildClause
// }
// const simpleMatchFilters = mockQueryBuilder.buildSimpleMatchFilters(['buildingLocation', 'subjectLiteral'])
// expect(simpleMatchFilters).to.deep.equal([
// {
// path: undefined,
// clause: { term: { buildingLocationIds: 'toast' } }
// },
// {
// path: undefined,
// clause: { term: { subjectLiteral_exploded: 'spaghetti' } }
// }
// ])
// })
it('can handle (multiple) single value, single match field filters, strings', () => {
const request = new ApiRequest({ filters: { buildingLocation: 'toast', subjectLiteral: 'spaghetti' } })
const mockQueryBuilder = mockQueryBuilderFactory(request)
const simpleMatchFilters = mockQueryBuilder.buildSimpleMatchFilters(['buildingLocation', 'subjectLiteral'])
expect(simpleMatchFilters).to.deep.equal([
{
path: undefined,
clause: { term: { buildingLocationIds: 'toast' } }
},
{
path: undefined,
clause: { term: { subjectLiteral_exploded: 'spaghetti' } }
}
])
})
it('can handle multiple values', () => {
const request = new ApiRequest({ filters: { subjectLiteral: ['spaghetti', 'meatballs'] } })
const mockQueryBuilder = mockQueryBuilderFactory(request)

0 comments on commit 338ef7b

Please sign in to comment.