From d638f2c8ff1c29640de09f57248d23e48798bfd1 Mon Sep 17 00:00:00 2001 From: Christiaan Scheermeijer Date: Tue, 10 Nov 2020 16:49:54 +0100 Subject: [PATCH] feat(search): improve the search algorithm The current search algorithm needs a few characters in order to find something. This is because it is only using a fuzzy search. This PR adds a second search pass using the OR operator. --- src/queries/SearchQuery.js | 42 +++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/src/queries/SearchQuery.js b/src/queries/SearchQuery.js index 222cd9d..a826bbf 100644 --- a/src/queries/SearchQuery.js +++ b/src/queries/SearchQuery.js @@ -16,7 +16,7 @@ class SearchQuery { */ get query () { return [ - `CALL db.index.fulltext.queryNodes("metadataSearchFields", "${this._generateIndexQueryClause(this._generateSubStringClause())}")`, + `CALL db.index.fulltext.queryNodes("metadataSearchFields", "${this._generateQueryClause()}")`, `YIELD \`node\`, \`score\``, this._generateTypeClause(), `RETURN node { .*, FRAGMENT_TYPE: HEAD(labels(node)), _searchScore: \`score\` }`, @@ -27,43 +27,53 @@ class SearchQuery { } /** + * @param field * @returns {string} * @private */ - _generateSubStringClause () { + _generateFieldSubStringClause (field) { // empty substring if (!this.params.substring) { - return '/.*/' + return `${field}:/.*/` } + const subString = this.params.substring.replace(/[^A-Za-z0-9]/g, '') + // prepare search substring - let subStringClause = `${this.params.substring.replace(/[^A-Za-z0-9]/g, ' ')}~` + return `${field}:'/${subString}.*/' OR ${field}:'${subString}~'` + } - if (subStringClause.includes(' ')) { - subStringClause = `'${subStringClause}'` + /** + * @returns {string} + * @private + */ + _generateSubStringClause () { + // empty substring + if (!this.params.substring) { + return `/.*/` } - return subStringClause + const subString = this.params.substring.replace(/[^A-Za-z0-9\s]/g, '') + + // prepare search substring + return `'/${subString}.*/' OR '${subString}~'` } /** - * @param subStringClause * @returns {*|string} * @private */ - _generateIndexQueryClause (subStringClause) { + _generateQueryClause () { // if only a subset of fields need to be evaluated: build query clause for [substring]~ on all eligible fields - let indexQueryClause = subStringClause if (this.doEvaluateFieldSubset) { const fieldNames = this.doEvaluateFieldSubset ? this.params.onFields : this.resolveInfo.schema._typeMap.SearchableMetadataFields._values.map(field => { return field.name }) - let queryClauses = [] - fieldNames.map(field => { - queryClauses.push(`${field}:${subStringClause}`) - }) - indexQueryClause = queryClauses.join(' OR ') + + return fieldNames + .map(field => this._generateFieldSubStringClause(field)) + .join(' OR ') } - return indexQueryClause + return this._generateSubStringClause() } /**