diff --git a/lib/contributors.js b/lib/contributors.js index 0e7321de..4ccf874c 100644 --- a/lib/contributors.js +++ b/lib/contributors.js @@ -100,21 +100,21 @@ module.exports = function (app, _private = null) { } /** - * Builds an aggregation query that checks the resource index for counts on the contributorRoleLiteral field for a list of contributors. + * Builds an aggregation query that checks the resource index for counts on the browseableContributorRole_packed field for a list of contributors. */ const buildElasticRoleCountQuery = function (contributorList) { return { size: 0, query: { terms: { - contributorRoleLiteral: contributorList + browseableContributorRole_packed: contributorList } }, aggs: { contributor_role: { terms: { script: { - source: 'def results = []; for (val in doc["contributorRoleLiteral"]) { int pos = val.indexOf("||"); if (pos != -1) { String name = val.substring(0, pos); if (params.targets.contains(name)) { results.add(val); } } } return results;', + source: 'def results = []; for (val in doc["browseableContributorRole_packed"]) { int pos = val.indexOf("||"); if (pos != -1) { String name = val.substring(0, pos); if (params.targets.contains(name)) { results.add(val); } } } return results;', params: { targets: contributorList } diff --git a/lib/display-field-unpacker.js b/lib/display-field-unpacker.js new file mode 100644 index 00000000..5ae5b26e --- /dev/null +++ b/lib/display-field-unpacker.js @@ -0,0 +1,25 @@ +const parseValueAndLabel = (delimitedString) => { + if (!delimitedString.includes('||')) { + return { value: delimitedString, display: null } + } + const [value, display] = delimitedString.split('||') + return { value, display } +} + +module.exports = (elasticSearchResponse) => { + elasticSearchResponse.hits.hits.forEach((bib) => { + // Contributors and creators are packed like so || where + // can have prefix, title, and roles. We'd like to unpack them in a friendly format for the frontend + // to display the full label and use the isolated name for link-building + Object.entries(bib._source).forEach(([key, value]) => { + if (key.endsWith('_displayPacked')) { + const fieldName = key.replace('_displayPacked', '') + bib._source[fieldName + 'Display'] = value.map((packedValue) => parseValueAndLabel(packedValue)) + delete bib._source[key] + } + }) + + return bib + }) + return elasticSearchResponse +} diff --git a/lib/elasticsearch/elastic-query-builder.js b/lib/elasticsearch/elastic-query-builder.js index 70efa58f..0b4452bc 100644 --- a/lib/elasticsearch/elastic-query-builder.js +++ b/lib/elasticsearch/elastic-query-builder.js @@ -181,7 +181,7 @@ class ElasticQueryBuilder { * Concat contributor + role if role param is provided */ applyContributorRole () { - this.query.addMust(termMatch('contributorRoleLiteral', this.request.params.filters.contributorLiteral + '||' + this.request.params.role)) + this.query.addMust(termMatch('browseableContributorRole_packed', this.request.params.filters.contributorLiteral + '||' + this.request.params.role)) } /** diff --git a/lib/response_massager.js b/lib/response_massager.js index a1eb17ec..034580a5 100644 --- a/lib/response_massager.js +++ b/lib/response_massager.js @@ -1,6 +1,7 @@ const LocationLabelUpdater = require('./location_label_updater') const AvailabilityResolver = require('./availability_resolver.js') const parallelFieldsExtractor = require('./parallel-fields-extractor') +const displayFieldUnpacker = require('./display-field-unpacker') const { isAeonUrl, sortOnPropWithUndefinedLast } = require('../lib/util') const FulfillmentResolver = require('./fulfillment_resolver') const fixItemRequestability = require('./requestability_resolver') @@ -73,6 +74,9 @@ class ResponseMassager { // Rename parallel fields: response = parallelFieldsExtractor(response) + // Extract display values and labels from packed fields + response = displayFieldUnpacker(response) + // Update ES response with updated availability from SCSB: const updatedWithAvailability = (new AvailabilityResolver(response)) .responseWithUpdatedAvailability(options) diff --git a/test/display-field-unpacker.test.js b/test/display-field-unpacker.test.js new file mode 100644 index 00000000..3f768393 --- /dev/null +++ b/test/display-field-unpacker.test.js @@ -0,0 +1,22 @@ +const { expect } = require('chai') +const displayFieldsUnpacker = require('../lib/display-field-unpacker') +const packedDisplayBib = require('./fixtures/packed-display-response.json') + +describe('Display field unpacker', () => { + describe('When a bib has a packed display property', () => { + it('adds each of the items in that array as unpacked objects', () => { + const displayFieldsUnpacked = displayFieldsUnpacker(packedDisplayBib).hits.hits[0]._source + expect(Object.keys(displayFieldsUnpacked).length).to.equal(2) + expect(displayFieldsUnpacked).to.deep.equal({ + testDisplay: [ + { value: 'someValue', display: 'someDisplay' }, + { value: 'someValueB', display: 'someDisplayB' }, + { value: 'someValueC', display: null } + ], + testOtherDisplay: [ + { value: 'otherValue', display: 'otherDisplay' } + ] + }) + }) + }) +}) diff --git a/test/elastic-query-builder.test.js b/test/elastic-query-builder.test.js index 48c08440..062a9ec0 100644 --- a/test/elastic-query-builder.test.js +++ b/test/elastic-query-builder.test.js @@ -272,7 +272,7 @@ describe('ElasticQueryBuilder', () => { const inst = ElasticQueryBuilder.forApiRequest(request) expect(inst.query.toJson()).to.nested - .include({ 'bool.must[0].term.contributorRoleLiteral.value': 'Patinkin, Mandy||performer.' }) + .include({ 'bool.must[0].term.browseableContributorRole_packed.value': 'Patinkin, Mandy||performer.' }) }) }) diff --git a/test/fixtures/packed-display-response.json b/test/fixtures/packed-display-response.json new file mode 100644 index 00000000..3a304c50 --- /dev/null +++ b/test/fixtures/packed-display-response.json @@ -0,0 +1,18 @@ +{ + "hits": { + "hits": [ + { + "_source": { + "test_displayPacked": [ + "someValue||someDisplay", + "someValueB||someDisplayB", + "someValueC" + ], + "testOther_displayPacked": [ + "otherValue||otherDisplay" + ] + } + } + ] + } +}