-
Notifications
You must be signed in to change notification settings - Fork 0
Scc 5050 2 #622
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Scc 5050 2 #622
Changes from 35 commits
b1b4547
7685722
472331e
579eec5
ae28b72
8b6000f
e5b61dc
b85f3fb
a02a058
0beec4c
a749878
683445f
5ac57ee
d50ea45
2f6c887
1075d65
a0237b7
3340f5e
e072d93
054d1e4
2d60b15
2ec818a
c784247
ba7fcf5
9189c1b
3297b63
03d57dc
1c34469
6e8af8d
cf7edcd
6ca7e33
39c0920
26e0a15
6714e24
9534528
cd4c24f
eefeb44
d26553b
7429ad6
b386ec1
36d56c5
d8249ac
9353887
15a5e5c
b5530eb
6cfbd49
1c769c9
c379ba5
a5e190e
58a0d9f
8b3e4e1
87c1e32
a55444d
05dfb3f
f373383
951a8e8
3a03d81
1952828
7d1612e
5d3bf7d
b2a79b8
ff32619
ffe1b30
c389071
3a83857
d2109f5
baf803c
8842a88
fd8ff37
271c692
ffe585f
5990165
bd098d5
020c75d
7aec0dd
1473e3d
ed4c14a
74f97a4
d5cee7f
e2e43d0
93c042e
9c98372
0ebe3bd
b2ebcab
15ddf08
d99276f
f9a0250
691b974
fd33073
36e6302
a7d78e7
d5ec597
4f26b20
94d0d61
ca8a4d1
e0e8f6b
34a399e
d65a3d3
753ca86
330e170
b0f7585
d4c655a
14d7134
c59f33e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,203 @@ | ||
| const { EXCLUDE_FIELDS, ITEM_FILTER_AGGREGATIONS, SORT_FIELDS, AGGREGATIONS_SPEC } = require('./config') | ||
| const { innerHits, itemsQueryContext, itemsFilterContext } = require('./elastic-query-filter-builder') | ||
| const ApiRequest = require('../api-request') | ||
| const ElasticQueryBuilder = require('../elasticsearch/elastic-query-builder') | ||
|
|
||
| const bodyForFindByUri = function (recapBarcodesByStatus, params) { | ||
| const paramsIncludesItemLevelFiltering = Object.keys(params) | ||
| .filter((param) => param.startsWith('item_')).length > 0 | ||
|
|
||
| const returnAllItems = params.all_items && !paramsIncludesItemLevelFiltering | ||
|
|
||
| const excludes = returnAllItems ? EXCLUDE_FIELDS.filter((field) => field !== '*_sort') : EXCLUDE_FIELDS.concat(['items']) | ||
|
|
||
| const aggregations = params.include_item_aggregations | ||
| ? { aggregations: ITEM_FILTER_AGGREGATIONS } | ||
| : {} | ||
|
|
||
| const itemsOptions = { | ||
| size: params.items_size, | ||
| from: params.items_from, | ||
| merge_checkin_card_items: params.merge_checkin_card_items, | ||
| query: { | ||
| volume: params.item_volume, | ||
| date: params.item_date, | ||
| format: params.item_format, | ||
| location: params.item_location, | ||
| status: params.item_status, | ||
| itemUri: params.itemUri | ||
| }, | ||
| unavailable_recap_barcodes: recapBarcodesByStatus['Not Available'] | ||
| } | ||
|
|
||
| // const filter = returnAllItems ? {} : { filter: [] } | ||
|
|
||
| const queryFilter = { filter: !returnAllItems ? [innerHits(itemsOptions)] : [] } | ||
|
|
||
| // Establish base query: | ||
|
nonword marked this conversation as resolved.
Outdated
|
||
| const body = { | ||
| _source: { | ||
| excludes | ||
| }, | ||
| size: 1, | ||
| query: { | ||
| bool: { | ||
| must: [ | ||
| { | ||
| term: { | ||
| uri: params.uri | ||
| } | ||
| } | ||
| ], | ||
| ...queryFilter | ||
| } | ||
| }, | ||
| ...aggregations | ||
| } | ||
|
|
||
| return body | ||
| } | ||
|
|
||
| /** | ||
| * Given GET params, returns a plainobject suitable for use in a ES query. | ||
| * | ||
| * @param {object} params - A hash of request params including `filters`, | ||
| * `search_scope`, `q` | ||
| * | ||
| * @return {object} ES query object suitable to be POST'd to ES endpoint | ||
| */ | ||
| const buildElasticQuery = function (params, options = {}) { | ||
| const request = ApiRequest.fromParams(params) | ||
|
|
||
| const builder = ElasticQueryBuilder.forApiRequest(request, options) | ||
| return builder.query.toJson() | ||
| } | ||
|
|
||
| /** | ||
| * Given GET params, returns a plainobject with `from`, `size`, `query`, | ||
| * `sort`, and any other params necessary to perform the ES query based | ||
| * on the GET params. | ||
| * | ||
| * @return {object} An object that can be posted directly to ES | ||
| */ | ||
| const buildElasticBody = function (params, options = {}) { | ||
| // Apply sort: | ||
| let direction | ||
| let field | ||
|
|
||
| if (params.sort === 'relevance') { | ||
| field = '_score' | ||
| direction = 'desc' | ||
| } else { | ||
| field = SORT_FIELDS[params.sort].field || params.sort | ||
| direction = params.sort_direction || SORT_FIELDS[params.sort].initialDirection | ||
| } | ||
|
|
||
| const from = params.per_page && params.page ? { from: params.per_page * (params.page - 1) } : {} | ||
| const size = params.per_page ? { size: params.per_page } : {} | ||
|
|
||
| return { | ||
| ...from, | ||
| ...size, | ||
| query: buildElasticQuery(params, options), | ||
| sort: [{ [field]: direction }, { uri: 'asc' }] | ||
| } | ||
|
danamansana marked this conversation as resolved.
|
||
| } | ||
|
|
||
| const bodyForSearch = function (params) { | ||
| const itemsOptions = { merge_checkin_card_items: params.merge_checkin_card_items } | ||
|
|
||
| const body = Object.assign( | ||
| buildElasticBody(params, { items: itemsOptions }), | ||
| { | ||
| _source: { | ||
| excludes: EXCLUDE_FIELDS.concat(['items']) | ||
| } | ||
| } | ||
| ) | ||
|
|
||
| // return withInnerHits | ||
|
nonword marked this conversation as resolved.
Outdated
|
||
| return body | ||
| } | ||
|
|
||
| const buildElasticAggregationsBody = (params, aggregateProps) => { | ||
| // Add an `aggregations` entry to the ES body describing the aggretations | ||
| // we want. Set the `size` property to per_page (default 50) for each. | ||
| // https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-terms-aggregation.html#search-aggregations-bucket-terms-aggregation-size | ||
| const aggregations = aggregateProps.reduce((aggs, prop) => { | ||
| aggs[prop] = AGGREGATIONS_SPEC[prop] | ||
| // Only set size for terms aggs for now: | ||
| if (aggs[prop].terms) { | ||
| aggs[prop].terms.size = params.per_page | ||
| } | ||
| return aggs | ||
| }, {}) | ||
|
|
||
| return Object.assign( | ||
| buildElasticBody(params), | ||
| { size: 0, aggregations } | ||
| ) | ||
| } | ||
|
|
||
| /** | ||
| * Given a params hash, returns an array of ES queries for fetching relevant aggregations. | ||
| */ | ||
| const aggregationQueriesForParams = (params) => { | ||
| // Build the complete set of distinct aggregation queries we need to run | ||
| // depending on active filters. We want: | ||
| // - one agg representing the counts for all properties _not_ used in filter | ||
| // - one agg each for each property that is used in a filter, but counts should exclude that filter | ||
|
|
||
| // Build the standard aggregation: | ||
| const unfilteredAggregationProps = Object.keys(AGGREGATIONS_SPEC) | ||
| // Aggregate on all properties that aren't involved in filters: | ||
| .filter((prop) => !Object.keys(params.filters || {}).includes(prop)) | ||
| const queries = [buildElasticAggregationsBody(params, unfilteredAggregationProps)] | ||
|
|
||
| // Now append all property-specific aggregation queries (one for each | ||
| // distinct property used in a filter): | ||
| return queries.concat( | ||
| Object.entries(params.filters || {}) | ||
| // Only consider filters that are also aggregations: | ||
| .filter(([prop, values]) => Object.keys(AGGREGATIONS_SPEC).includes(prop)) | ||
| .map(([prop, values]) => { | ||
| const aggFilters = structuredClone(params.filters) | ||
| // For this aggregation, don't filter on namesake property: | ||
| delete aggFilters[prop] | ||
|
|
||
| // Build query for single aggregation: | ||
| const modifiedParams = Object.assign({}, params, { filters: aggFilters }) | ||
| return buildElasticAggregationsBody(modifiedParams, [prop]) | ||
| }) | ||
| ) | ||
| } | ||
|
|
||
| const bodyForAggregation = (params) => { | ||
| const aggregations = {} | ||
| aggregations[params.field] = AGGREGATIONS_SPEC[params.field] | ||
|
|
||
| // If it's a terms agg, we can apply per_page: | ||
| if (aggregations[params.field].terms) { | ||
| aggregations[params.field].terms.size = params.per_page | ||
| } | ||
|
|
||
| return Object.assign( | ||
| buildElasticBody(params), | ||
| { | ||
| size: 0, | ||
| aggregations | ||
| } | ||
| ) | ||
| } | ||
|
|
||
| module.exports = { | ||
| bodyForFindByUri, | ||
| itemsFilterContext, | ||
| itemsQueryContext, | ||
| buildElasticQuery, | ||
| buildElasticBody, | ||
| bodyForSearch, | ||
| buildElasticAggregationsBody, | ||
| aggregationQueriesForParams, | ||
| bodyForAggregation | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ const ElasticQuery = require('./elastic-query') | |
| const ApiRequest = require('../api-request') | ||
| const { escapeQuery, namedQuery, prefixMatch, termMatch, phraseMatch } = require('./utils') | ||
| const { regexEscape } = require('../util') | ||
| const { innerHits } = require('./elastic-query-filter-builder') | ||
|
|
||
| const { FILTER_CONFIG, SEARCH_SCOPES } = require('./config') | ||
|
|
||
|
|
@@ -11,7 +12,7 @@ const POPULARITY_BOOSTS = [ | |
| ] | ||
|
|
||
| class ElasticQueryBuilder { | ||
| constructor (apiRequest) { | ||
| constructor (apiRequest, options = {}) { | ||
| this.request = apiRequest | ||
| this.query = new ElasticQuery() | ||
|
|
||
|
|
@@ -44,6 +45,10 @@ class ElasticQueryBuilder { | |
| // Add user filters: | ||
| this.applyFilters() | ||
|
|
||
| if (options.items) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think there is some redundancy here around the items. If the items flag is implemented as a filter, it feels like it should be handled by
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The The point that we probably don't need an additional Also agree we can start scrubbing support for overriding the default setting to
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had a long conversation with Vera offline about this, we agreed to remove the |
||
| this.query.addFilter(innerHits(options.items)) | ||
| } | ||
|
|
||
| // Apply global clauses: | ||
| // Hide specific nypl-sources when configured to do so: | ||
| this.applyHiddenNyplSources() | ||
|
|
@@ -706,8 +711,8 @@ class ElasticQueryBuilder { | |
| /** | ||
| * Create a ElasticQueryBuilder for given ApiRequest instance | ||
| */ | ||
| static forApiRequest (request) { | ||
| return new ElasticQueryBuilder(request) | ||
| static forApiRequest (request, options = {}) { | ||
| return new ElasticQueryBuilder(request, options) | ||
| } | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.