diff --git a/src/models/database.js b/src/models/database.js index 8576cc96..70fc4d73 100644 --- a/src/models/database.js +++ b/src/models/database.js @@ -19,3 +19,10 @@ export const safeMirrorDbHandler = (callback) => { console.log('Mirror DB not connected'); }); }; + +export const sanitizeSqliteFtsQuery = (query) => { + query = query.replace(/[-](?=.*[-])/g, "+"); // Replace all but the final dash + query = query.replace('-', ''); //Replace the final dash with nothing + query += '*'; // Query should end with asterisk for partial matching + return query; +} \ No newline at end of file diff --git a/src/models/projects/projects.model.js b/src/models/projects/projects.model.js index 762cbf0c..50bccf40 100644 --- a/src/models/projects/projects.model.js +++ b/src/models/projects/projects.model.js @@ -4,7 +4,7 @@ import Sequelize from 'sequelize'; import rxjs from 'rxjs'; const { Model } = Sequelize; -import { sequelize, safeMirrorDbHandler } from '../database'; +import {sequelize, safeMirrorDbHandler, sanitizeSqliteFtsQuery} from '../database'; import { RelatedProject, @@ -170,10 +170,19 @@ class Project extends Model { if (columns.length) { fields = columns.join(', '); } - - // hyphens cause errors in sqlite, but we can replace it with a + and - // the fulltext search will work the same - searchStr = searchStr = searchStr.replaceAll('-', '+'); + + searchStr = sanitizeSqliteFtsQuery(searchStr); + + if (searchStr === '*') { // * isn't a valid matcher on its own. return empty set + return { + count: 0, + rows: [], + } + } + + if (searchStr.startsWith('+')) { + searchStr = searchStr.replace('+', '') // If query starts with +, replace it + } let sql = `SELECT ${fields} FROM projects_fts WHERE projects_fts MATCH :search`; @@ -181,7 +190,7 @@ class Project extends Model { sql = `${sql} AND orgUid = :orgUid`; } - const replacements = { search: `${searchStr}*`, orgUid }; + const replacements = { search: searchStr, orgUid }; const count = ( await sequelize.query(sql, { diff --git a/src/models/units/units.model.js b/src/models/units/units.model.js index c5ff292f..a517028f 100644 --- a/src/models/units/units.model.js +++ b/src/models/units/units.model.js @@ -1,7 +1,7 @@ 'use strict'; import Sequelize from 'sequelize'; -import { sequelize, safeMirrorDbHandler } from '../database'; +import {sequelize, safeMirrorDbHandler, sanitizeSqliteFtsQuery} from '../database'; import { Qualification, Vintage } from '../../models'; import { UnitMirror } from './units.model.mirror'; import ModelTypes from './units.modeltypes.cjs'; @@ -175,7 +175,7 @@ class Unit extends Model { unitMarketplaceLink, cooresponingAdjustmentDeclaration, correspondingAdjustmentStatus - ) AGAINST ":search" + ) AGAINST '":search"' `; if (orgUid) { @@ -215,16 +215,27 @@ class Unit extends Model { if (columns.length) { fields = columns.join(', '); } - - searchStr = searchStr = searchStr.replaceAll('-', '+'); - + + searchStr = sanitizeSqliteFtsQuery(searchStr); + + if (searchStr === '*') { // * isn't a valid matcher on its own. return empty set + return { + count: 0, + rows: [], + } + } + + if (searchStr.startsWith('+')) { + searchStr = searchStr.replace('+', '') // If query starts with +, replace it + } + let sql = `SELECT ${fields} FROM units_fts WHERE units_fts MATCH :search`; if (orgUid) { sql = `${sql} AND orgUid = :orgUid`; } - const replacements = { search: `${searchStr}*`, orgUid }; + const replacements = { search: searchStr, orgUid }; const count = ( await sequelize.query(sql, {