commit 572c7d36f90e2446ce6608dae9a36c5d157f9f36
parent e04fb7b7508934510f1613dd5168dcb051f93037
Author: Paul Canning <pcanning@gmail.com>
Date: Tue, 31 May 2022 15:40:20 +0100
Merge branch 'finwo:master' into master
Diffstat:
6 files changed, 181 insertions(+), 5 deletions(-)
diff --git a/package-lock.json b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "lucene-filter",
- "version": "1.1.0",
+ "version": "1.3.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "lucene-filter",
- "version": "1.1.0",
+ "version": "1.3.0",
"license": "MIT",
"devDependencies": {
"aaargh": "^1.0.3",
diff --git a/package.json b/package.json
@@ -1,6 +1,6 @@
{
"name": "lucene-filter",
- "version": "1.1.0",
+ "version": "1.3.0",
"description": "Data filter for lucene queries",
"main": "src/index.js",
"browser": "dist/index.js",
diff --git a/src/filters/number/comparison.js b/src/filters/number/comparison.js
@@ -0,0 +1,48 @@
+const field = require('../../field');
+
+module.exports = {
+ detect : function (query) {
+ if (!query) {
+ return false;
+ }
+ if ('object' !== typeof query) {
+ return false;
+ }
+ if (!query.term) {
+ return false;
+ }
+ return Array.isArray(query.term.match(/^[<=>]+/));
+ },
+ compile: function (query) {
+ query.similarity = query.similarity || 0;
+ return function (data) {
+ return field(query.field, data, function (value) {
+ let sign, num;
+ value = parseFloat(value);
+ if (sign = query.term.match(/^[<=>]+/)) {
+ [sign] = sign;
+ }
+ num = query.term.match(/[0-9.]+/);
+ if (!num) {
+ return false;
+ }
+ num = Number(num[0]);
+ if (sign) {
+ if (sign === '>') {
+ return value > num;
+ } else if (sign === '>=') {
+ return value >= num;
+ } else if (sign === '<') {
+ return value < num;
+ } else if (sign === '<=') {
+ return value <= num;
+ } else {
+ throw new Error(`Invalid sign: ${sign}`);
+ }
+ } else {
+ return value === num;
+ }
+ });
+ };
+ },
+};
diff --git a/src/filters/number/comparison.test.js b/src/filters/number/comparison.test.js
@@ -0,0 +1,60 @@
+const tape = require('tape');
+const comparison = require('./comparison');
+
+tape('filter.number.comparison -- structure', async t => {
+ t.plan(2);
+ t.ok('detect' in comparison, 'comparison filter has detect method');
+ t.ok('compile' in comparison, 'comparison filter has compile method');
+});
+
+tape('filter.number.comparison -- detect', async t => {
+ t.plan(9);
+ t.notOk(comparison.detect(false), 'contains.detect doesn\'t trigger on false query');
+ t.notOk(comparison.detect(null), 'contains.detect doesn\'t trigger on null query');
+ t.notOk(comparison.detect(true), 'contains.detect doesn\'t trigger on true query');
+ t.notOk(comparison.detect([]), 'contains.detect doesn\'t trigger on empty array query');
+ t.notOk(comparison.detect({}), 'contains.detect doesn\'t trigger on empty object query');
+ t.notOk(comparison.detect({ field: 'something' }), 'contains.detect doesn\'t trigger on termless query');
+ t.notOk(comparison.detect({ term: 'something' }), 'contains.detect doesn\'t trigger on fieldless query');
+ t.ok(comparison.detect({ field: 'hello', term: '>1' }), 'contains.detect triggers on properly structured query');
+ t.notOk(comparison.detect({ field: 'hello', term: '1' }), 'contains.detect doesn\'t trigger on query with no comparator');
+});
+
+tape('filter.number.comparison, gt -- compile', async t => {
+ t.plan(3);
+
+ const extractor = comparison.compile({ field: 'age', term: '>21' });
+ t.equal(typeof extractor, 'function', 'comparison.compile returns a function');
+
+ t.ok(extractor({ age: 22 }), 'finds term in comparison');
+ t.notOk(extractor({ age: 21 }), 'doesn\'t find term in comparison');
+});
+tape('filter.number.comparison, gte -- compile', async t => {
+ t.plan(4);
+
+ const extractor = comparison.compile({ field: 'age', term: '>=21' });
+ t.equal(typeof extractor, 'function', 'comparison.compile returns a function');
+
+ t.ok(extractor({ age: 22 }), 'finds term in comparison');
+ t.ok(extractor({ age: 21 }), 'finds term in comparison');
+ t.notOk(extractor({ age: 20 }), 'doesn\'t find term in comparison');
+});
+tape('filter.number.comparison, lt -- compile', async t => {
+ t.plan(3);
+
+ const extractor = comparison.compile({ field: 'age', term: '<21' });
+ t.equal(typeof extractor, 'function', 'comparison.compile returns a function');
+
+ t.ok(extractor({ age: 20 }), 'finds term in comparison');
+ t.notOk(extractor({ age: 21 }), 'doesn\'t find term in comparison');
+});
+tape('filter.number.comparison, lte -- compile', async t => {
+ t.plan(4);
+
+ const extractor = comparison.compile({ field: 'age', term: '<=21' });
+ t.equal(typeof extractor, 'function', 'comparison.compile returns a function');
+
+ t.ok(extractor({ age: 20 }), 'finds term in comparison');
+ t.ok(extractor({ age: 21 }), 'finds term in comparison');
+ t.notOk(extractor({ age: 22 }), 'doesn\'t find term in comparison');
+});
diff --git a/src/filters/number/range.js b/src/filters/number/range.js
@@ -11,8 +11,21 @@ module.exports = {
compile: function (query) {
return function (data) {
return field(query.field, data, function (value) {
- console.log('range value', query, value);
- }) ? query.boost : 0;
+ // console.log('range value', query, value);
+ let min = parseFloat(query.term_min);
+ let max = parseFloat(query.term_max);
+ let inclusive = query.inclusive;
+ if (inclusive === 'both') {
+ return (value >= min) && (value <= max);
+ }
+ if (inclusive === 'left') {
+ return (value >= min) && (value < max);
+ }
+ if (inclusive === 'right') {
+ return (value > min) && (value <= max);
+ }
+ return false;
+ });
};
},
};
diff --git a/src/filters/number/range.test.js b/src/filters/number/range.test.js
@@ -0,0 +1,55 @@
+const tape = require('tape');
+const range = require('./range');
+
+tape('filter.number.range -- structure', async t => {
+ t.plan(2);
+ t.ok('detect' in range, 'range filter has detect method');
+ t.ok('compile' in range, 'range filter has compile method');
+});
+
+tape('filter.number.range -- detect', async t => {
+ t.plan(8);
+ t.notOk(range.detect(false), 'contains.detect doesn\'t trigger on false query');
+ t.notOk(range.detect(null), 'contains.detect doesn\'t trigger on null query');
+ t.notOk(range.detect(true), 'contains.detect doesn\'t trigger on true query');
+ t.notOk(range.detect([]), 'contains.detect doesn\'t trigger on empty array query');
+ t.notOk(range.detect({}), 'contains.detect doesn\'t trigger on empty object query');
+ t.notOk(range.detect({ field: 'something' }), 'contains.detect doesn\'t trigger on termless query');
+ t.notOk(range.detect({ term: 'something' }), 'contains.detect doesn\'t trigger on fieldless query');
+ t.ok(range.detect({ field: 'hello', term_min: 1, term_max: 10, inclusive: 'both' }), 'contains.detect triggers on properly structured query');
+});
+
+tape('filter.number.range, both inclusive -- compile', async t => {
+ t.plan(6);
+
+ const extractor = range.compile({ field: 'age', term_min: 18, term_max: 21, inclusive: 'both' });
+ t.equal(typeof extractor, 'function', 'range.compile returns a function');
+
+ t.ok(extractor({ age: 20 }), 'finds term in range');
+ t.ok(extractor({ age: 21 }), 'finds term in range');
+ t.ok(extractor({ age: 18 }), 'finds term in range');
+ t.notOk(extractor({ age: 17 }), 'doesn\'t find term in range');
+ t.notOk(extractor({ age: 22 }), 'doesn\'t find term in range');
+});
+
+tape('filter.number.range, left inclusive -- compile', async t => {
+ t.plan(4);
+
+ const extractor = range.compile({ field: 'age', term_min: 18, term_max: 21, inclusive: 'left' });
+ t.equal(typeof extractor, 'function', 'range.compile returns a function');
+
+ t.ok(extractor({ age: 18 }), 'finds term in range');
+ t.ok(extractor({ age: 20 }), 'finds term in range');
+ t.notOk(extractor({ age: 21 }), 'doesn\'t find term in range');
+});
+
+tape('filter.number.range, right inclusive -- compile', async t => {
+ t.plan(4);
+
+ const extractor = range.compile({ field: 'age', term_min: 18, term_max: 21, inclusive: 'right' });
+ t.equal(typeof extractor, 'function', 'range.compile returns a function');
+
+ t.ok(extractor({ age: 20 }), 'finds term in range');
+ t.ok(extractor({ age: 21 }), 'finds term in range');
+ t.notOk(extractor({ age: 18 }), 'doesn\'t find term in range');
+});