From 41a35bcaf53c4e3abcb1e9144ade9b7f35903879 Mon Sep 17 00:00:00 2001 From: missinglink Date: Thu, 21 Apr 2022 11:39:47 +0200 Subject: [PATCH 1/2] feat(search): add wilcard & lang field matching to multimatch queries --- layout/FallbackQuery.js | 17 ++++- layout/StructuredFallbackQuery.js | 18 ++++-- test/fixtures/fallbackQuery1-with-lang.js | 10 +++ test/fixtures/fallbackQuery1.json | 3 +- .../structuredFallbackQuery/query.json | 1 + test/layout/FallbackQuery.js | 30 +++++++++ test/view/phrase.js | 63 ++++++++++--------- view/phrase.js | 21 +++++-- 8 files changed, 122 insertions(+), 41 deletions(-) create mode 100644 test/fixtures/fallbackQuery1-with-lang.js diff --git a/layout/FallbackQuery.js b/layout/FallbackQuery.js index 7e5972e..fa152d2 100644 --- a/layout/FallbackQuery.js +++ b/layout/FallbackQuery.js @@ -206,12 +206,23 @@ function addSecCountry(vs, o) { function addQuery(vs) { + var fields = [ + 'phrase.default', + 'phrase.default_*' + ]; + + var lang = vs.var('lang').get(); + if (_.isString(lang) && !_.isEmpty(lang)) { + fields.push( + `phrase.${lang}`, + `phrase.${lang}_*` + ); + } + var o = addPrimary( vs.var('input:query').toString(), 'venue', - [ - 'phrase.default' - ], + fields, false ); diff --git a/layout/StructuredFallbackQuery.js b/layout/StructuredFallbackQuery.js index 4b7b223..01427f0 100644 --- a/layout/StructuredFallbackQuery.js +++ b/layout/StructuredFallbackQuery.js @@ -164,13 +164,23 @@ function addSecCountry(vs, o) { function addQuery(vs) { + var fields = [ + 'phrase.default', + 'phrase.default_*' + ]; + + var lang = vs.var('lang').get(); + if (_.isString(lang) && !_.isEmpty(lang)) { + fields.push( + `phrase.${lang}`, + `phrase.${lang}_*` + ); + } + var o = addPrimary( vs.var('input:query').toString(), 'venue', - [ - 'phrase.default', - 'category' - ], + fields.concat(['category']), false ); diff --git a/test/fixtures/fallbackQuery1-with-lang.js b/test/fixtures/fallbackQuery1-with-lang.js new file mode 100644 index 0000000..6879f09 --- /dev/null +++ b/test/fixtures/fallbackQuery1-with-lang.js @@ -0,0 +1,10 @@ +const _ = require('lodash'); +const query = _.cloneDeep(require('./fallbackQuery1.json')); +const jpath = 'query.function_score.query.bool.should[0].bool.must[0].multi_match.fields'; + +_.set(query, jpath, _.get(query, jpath).concat( + 'phrase.foo', + 'phrase.foo_*' +)); + +module.exports = query; diff --git a/test/fixtures/fallbackQuery1.json b/test/fixtures/fallbackQuery1.json index 18ec92d..8b38d9d 100644 --- a/test/fixtures/fallbackQuery1.json +++ b/test/fixtures/fallbackQuery1.json @@ -14,7 +14,8 @@ "query": "query value", "type": "phrase", "fields": [ - "phrase.default" + "phrase.default", + "phrase.default_*" ] } }, diff --git a/test/fixtures/structuredFallbackQuery/query.json b/test/fixtures/structuredFallbackQuery/query.json index 17a7e35..09d9472 100644 --- a/test/fixtures/structuredFallbackQuery/query.json +++ b/test/fixtures/structuredFallbackQuery/query.json @@ -15,6 +15,7 @@ "type": "phrase", "fields": [ "phrase.default", + "phrase.default_*", "category" ] } diff --git a/test/layout/FallbackQuery.js b/test/layout/FallbackQuery.js index 32b0eec..1c989aa 100644 --- a/test/layout/FallbackQuery.js +++ b/test/layout/FallbackQuery.js @@ -63,6 +63,36 @@ module.exports.tests.base_render = function(test, common) { }); + test('VariableStore with query AND street should only add query - with lang', function (t) { + var query = new FallbackQuery(); + + var vs = new VariableStore(); + vs.var('size', 'size value'); + vs.var('track_scores', 'track_scores value'); + vs.var('input:query', 'query value'); + vs.var('input:unit', 'unit value'); + vs.var('input:housenumber', 'house number value'); + vs.var('input:street', 'street value'); + vs.var('input:neighbourhood', 'neighbourhood value'); + vs.var('input:borough', 'borough value'); + vs.var('input:locality', 'locality value'); + vs.var('input:county', 'county value'); + vs.var('input:region', 'region value'); + vs.var('input:country', 'country value'); + vs.var('boost:address', 19); + vs.var('boost:street', 17); + + // everything is same as above except lang is set here + vs.var('lang', 'foo'); + + var actual = JSON.parse(JSON.stringify(query.render(vs))); + var expected = require('../fixtures/fallbackQuery1-with-lang.js'); + + t.deepEquals(actual, expected); + t.end(); + + }); + test('VariableStore with number+street and less granular fields should include all others', function(t) { var query = new FallbackQuery(); diff --git a/test/view/phrase.js b/test/view/phrase.js index aa1d861..bf5d0e0 100644 --- a/test/view/phrase.js +++ b/test/view/phrase.js @@ -1,5 +1,6 @@ -var phrase = require('../../view/phrase'); -var VariableStore = require('../../lib/VariableStore'); +const _ = require('lodash'); +const phrase = require('../../view/phrase'); +const VariableStore = require('../../lib/VariableStore'); function getBaseVariableStore(toExclude) { var vs = new VariableStore(); @@ -48,14 +49,16 @@ module.exports.tests.no_exceptions_conditions = function(test, common) { var actual = phrase(getBaseVariableStore()); var expected = { - match: { - 'field value': { - analyzer: { $: 'analyzer value' }, - type: 'phrase', - boost: { $: 'boost value' }, - slop: { $: 'slop value' }, - query: { $: 'name value' } - } + multi_match: { + analyzer: { $: 'analyzer value' }, + type: 'phrase', + boost: { $: 'boost value' }, + slop: { $: 'slop value' }, + query: { $: 'name value' }, + fields: [ + 'field value', + 'field value_*' + ] } }; @@ -74,15 +77,17 @@ module.exports.tests.fuzziness_variable = function(test, common) { var actual = phrase(store); var expected = { - match: { - 'field value': { - analyzer: { $: 'analyzer value' }, - type: 'phrase', - boost: { $: 'boost value' }, - slop: { $: 'slop value' }, - query: { $: 'name value' }, - fuzziness: { $: 'fuzziness value' } - } + multi_match: { + analyzer: { $: 'analyzer value' }, + type: 'phrase', + boost: { $: 'boost value' }, + slop: { $: 'slop value' }, + query: { $: 'name value' }, + fuzziness: { $: 'fuzziness value' }, + fields: [ + 'field value', + 'field value_*' + ] } }; @@ -100,15 +105,17 @@ module.exports.tests.cutoff_frequency = function(test, common) { var actual = phrase(store); var expected = { - match: { - 'field value': { - analyzer: { $: 'analyzer value' }, - type: 'phrase', - boost: { $: 'boost value' }, - slop: { $: 'slop value' }, - query: { $: 'name value' }, - cutoff_frequency: { $: 'cutoff_frequency value' } - } + multi_match: { + analyzer: { $: 'analyzer value' }, + type: 'phrase', + boost: { $: 'boost value' }, + slop: { $: 'slop value' }, + query: { $: 'name value' }, + cutoff_frequency: { $: 'cutoff_frequency value' }, + fields: [ + 'field value', + 'field value_*' + ] } }; diff --git a/view/phrase.js b/view/phrase.js index a6dffff..4722721 100644 --- a/view/phrase.js +++ b/view/phrase.js @@ -1,3 +1,4 @@ +const _ = require('lodash'); module.exports = function( vs ){ @@ -11,23 +12,33 @@ module.exports = function( vs ){ } // base view - var view = { 'match': {} }; + var view = { 'multi_match': {} }; + + var pf = vs.var('phrase:field').get(); + var fields = [pf, `${pf}_*`]; + + var lang = vs.var('lang').get(); + if (_.isString(lang) && !_.isEmpty(lang)) { + var lf = pf.replace('default', lang); + fields.push(lf, `${lf}_*`); + } // match query - view.match[ vs.var('phrase:field') ] = { + view.multi_match = { analyzer: vs.var('phrase:analyzer'), type: 'phrase', boost: vs.var('phrase:boost'), slop: vs.var('phrase:slop'), - query: vs.var('input:name') + query: vs.var('input:name'), + fields: fields }; if (vs.isset('phrase:fuzziness')) { - view.match[ vs.var('phrase:field') ].fuzziness = vs.var('phrase:fuzziness'); + view.multi_match.fuzziness = vs.var('phrase:fuzziness'); } if (vs.isset('phrase:cutoff_frequency')) { - view.match[ vs.var('phrase:field') ].cutoff_frequency = vs.var('phrase:cutoff_frequency'); + view.multi_match.cutoff_frequency = vs.var('phrase:cutoff_frequency'); } return view; From f6ee4fdb8b718dd19f605209217fb135e4372d57 Mon Sep 17 00:00:00 2001 From: missinglink Date: Thu, 21 Apr 2022 12:13:14 +0200 Subject: [PATCH 2/2] feat(search): add wildcard and lang field matching to search queries --- layout/VenuesQuery.js | 13 ++++++++----- test/fixtures/venuesQuery/base_render.json | 13 ++++++++----- test/fixtures/venuesQuery/with_filters.json | 13 ++++++++----- test/fixtures/venuesQuery/with_scores.json | 13 ++++++++----- 4 files changed, 32 insertions(+), 20 deletions(-) diff --git a/layout/VenuesQuery.js b/layout/VenuesQuery.js index 69d24df..8c074c4 100644 --- a/layout/VenuesQuery.js +++ b/layout/VenuesQuery.js @@ -17,11 +17,14 @@ class VenuesQuery extends Query { bool: { must: [ { - match_phrase: { - 'name.default': { - query: vs.var('input:query'), - analyzer: 'standard' - } + multi_match: { + query: vs.var('input:query'), + type: 'phrase', + analyzer: 'standard', + fields: [ + 'name.default', + 'name.default_*' + ] } } ], diff --git a/test/fixtures/venuesQuery/base_render.json b/test/fixtures/venuesQuery/base_render.json index a211ae0..d8b933b 100644 --- a/test/fixtures/venuesQuery/base_render.json +++ b/test/fixtures/venuesQuery/base_render.json @@ -5,11 +5,14 @@ "bool": { "must": [ { - "match_phrase": { - "name.default": { - "query": "query value", - "analyzer": "standard" - } + "multi_match": { + "query": "query value", + "type": "phrase", + "analyzer": "standard", + "fields": [ + "name.default", + "name.default_*" + ] } } ], diff --git a/test/fixtures/venuesQuery/with_filters.json b/test/fixtures/venuesQuery/with_filters.json index 0acdf85..288bfb5 100644 --- a/test/fixtures/venuesQuery/with_filters.json +++ b/test/fixtures/venuesQuery/with_filters.json @@ -5,11 +5,14 @@ "bool": { "must": [ { - "match_phrase": { - "name.default": { - "query": "query value", - "analyzer": "standard" - } + "multi_match": { + "query": "query value", + "type": "phrase", + "analyzer": "standard", + "fields": [ + "name.default", + "name.default_*" + ] } } ], diff --git a/test/fixtures/venuesQuery/with_scores.json b/test/fixtures/venuesQuery/with_scores.json index c492295..991802c 100644 --- a/test/fixtures/venuesQuery/with_scores.json +++ b/test/fixtures/venuesQuery/with_scores.json @@ -5,11 +5,14 @@ "bool": { "must": [ { - "match_phrase": { - "name.default": { - "query": "query value", - "analyzer": "standard" - } + "multi_match": { + "query": "query value", + "type": "phrase", + "analyzer": "standard", + "fields": [ + "name.default", + "name.default_*" + ] } } ],