Test Report

0
0
178
53

ID Title Duration (ms)
1 Basic throws an error if the operation is invalid 5
2 Basic allows overriding existing operators 1
3 Basic works with custom operations 1
4 Basic returns a valid matcher if not passed something to compare against 0
5 Basic returns true for a basic match 1
6 Basic returns false for no basic match 0
7 Basic returns true for a basic curried match 0
8 Basic returns false for no basic curried match 1
9 Basic can match empty objects 0
10 Basic $ne: null returns true when field is different 0
11 Basic $ne: null returns false when field is not present 1
12 Basic $ne: null returns false when field is null 0
13 Basic can be used with Array.filter to create something that looks like sift 1
14 Basic accepts a regular expression as a filter 0
15 Basic suports get 0
16 Basic $eq takes function 1
17 Basic $eq works for empty arrays 0
18 Basic $eq works for empty arrays against false 0
19 Basic works with multiple nested values 1
20 Basic works with multiple nested objects with values 0
21 Filter curry basic should return object against object 1
22 Filter curry basic should return empty array for no match 0
23 Filter curry basic should return only matches 0
24 Filter curry basic should return empty array when no matches 1
25 Filter basic should return object against object 0
26 Filter basic should return empty array for no match 0
27 Filter basic should return only matches 0
28 Filter basic should return empty array when no matches 1
29 Sift-Filter 0: {"$eq":5} 0
30 Sift-Filter 1: "5" 0
31 Sift-Filter 2: false 0
32 Sift-Filter 3: true 0
33 Sift-Filter 4: 0 1
34 Sift-Filter 5: null 0
35 Sift-Filter 6: undefined 0
36 Sift-Filter 7: 1 0
37 Sift-Filter 8: 1 1
38 Sift-Filter 9: "1970-01-01T00:00:00.001Z" 0
39 Sift-Filter 10: {} 0
40 Sift-Filter 11: {"$ne":5} 0
41 Sift-Filter 12: {"$ne":"5"} 1
42 Sift-Filter 13: {"$ne":false} 0
43 Sift-Filter 14: {} 0
44 Sift-Filter 15: {"$ne":{}} 1
45 Sift-Filter 16: {"$ne":1} 0
46 Sift-Filter 17: {"groups":{"$ne":111}} 1
47 Sift-Filter 18: {"$lt":5} 0
48 Sift-Filter 19: {"$lt":"c"} 0
49 Sift-Filter 20: {"$lt":null} 0
50 Sift-Filter 21: {"$lt":"1970-01-01T00:00:00.003Z"} 0
51 Sift-Filter 22: {"$lte":5} 1
52 Sift-Filter 23: {"groups":{"$lt":5}} 0
53 Sift-Filter 24: {"$gt":5} 0
54 Sift-Filter 25: {"$gt":null} 0
55 Sift-Filter 26: {"groups":{"$gt":5}} 0
56 Sift-Filter 27: {"$gte":5} 1
57 Sift-Filter 28: {"groups":{"$gte":5}} 1
58 Sift-Filter 29: {"$mod":[2,1]} 0
59 Sift-Filter 30: {"groups":{"$mod":[2,0]}} 0
60 Sift-Filter 31: {"$exists":false} 1
61 Sift-Filter 32: {"$exists":true} 0
62 Sift-Filter 33: {"$in":[0,false,1,"1"]} 0
63 Sift-Filter 34: {"$in":[1,"1","2"]} 0
64 Sift-Filter 35: {"$in":["1970-01-01T00:00:00.001Z"]} 1
65 Sift-Filter 36: {"a.b.status":{"$in":[0]}} 0
66 Sift-Filter 37: {"a.b.status":{"$in":[0,2]}} 0
67 Sift-Filter 38: {"$nin":[0,false,1,"1"]} 1
68 Sift-Filter 39: {"$nin":[1,"1","2"]} 0
69 Sift-Filter 40: {"$nin":["1970-01-01T00:00:00.001Z"]} 0
70 Sift-Filter 41: {"$not":false} 0
71 Sift-Filter 42: {"$not":0} 0
72 Sift-Filter 43: {"$not":{"$in":[1,2,3]}} 0
73 Sift-Filter 44: {} 1
74 Sift-Filter 45: {} 0
75 Sift-Filter 46: {} 0
76 Sift-Filter 47: {} 0
77 Sift-Filter 48: {"$all":[1,2,3]} 0
78 Sift-Filter 49: {"$all":[0,false]} 1
79 Sift-Filter 50: {"$all":["1"]} 0
80 Sift-Filter 51: {"$all":["1970-01-01T00:00:00.001Z","1970-01-01T00:00:00.002Z"]} 0
81 Sift-Filter 52: {"$size":3} 0
82 Sift-Filter 53: {"$size":1} 0
83 Sift-Filter 54: {"$or":[1,2,3]} 1
84 Sift-Filter 55: {"$or":[{"$ne":1},2]} 0
85 Sift-Filter 56: {"$nor":[1,2,3]} 0
86 Sift-Filter 57: {"$nor":[{"$ne":1},2]} 1
87 Sift-Filter 58: {"$and":[{"$gt":1},{"$lt":4}]} 0
88 Sift-Filter 59: {"$regex":"^a"} 0
89 Sift-Filter 60: {"a":{"$regex":"b|c"}} 1
90 Sift-Filter 61: {"folder":{"$regex":"^[0-9]{4}$"}} 0
91 Sift-Filter 62: {"$regex":"^a","$options":"i"} 0
92 Sift-Filter 63: {"text":{"$regex":".*lis.*","$options":"i"}} 0
93 Sift-Filter 64: {"$regex":"a"} 0
94 Sift-Filter 65: {} 1
95 Sift-Filter 66: {} 0
96 Sift-Filter 67: {} 0
97 Sift-Filter 68: {"$where":"this.v === 1"} 0
98 Sift-Filter 69: {"$where":"obj.v === 1"} 0
99 Sift-Filter 70: {"a":{"$elemMatch":{"b":1,"c":2}}} 1
100 Sift-Filter 71: {"a":{"$elemMatch":{"b":2,"c":{"$gt":2}}}} 0
101 Sift-Filter 72: {"a.b":{}} 0
102 Sift-Filter 73: {"foo.0":"baz"} 1
103 Sift-Filter 74: {"foo.0.name":"baz"} 0
104 Sift 0: {"$eq":5} 0
105 Sift 1: "5" 1
106 Sift 2: false 0
107 Sift 3: true 0
108 Sift 4: 0 0
109 Sift 5: null 0
110 Sift 6: undefined 0
111 Sift 7: 1 0
112 Sift 8: 1 1
113 Sift 9: "1970-01-01T00:00:00.001Z" 0
114 Sift 10: {} 0
115 Sift 11: {"$ne":5} 0
116 Sift 12: {"$ne":"5"} 0
117 Sift 13: {"$ne":false} 1
118 Sift 14: {} 0
119 Sift 15: {"$ne":{}} 0
120 Sift 16: {"$ne":1} 0
121 Sift 17: {"groups":{"$ne":111}} 0
122 Sift 18: {"$lt":5} 0
123 Sift 19: {"$lt":"c"} 1
124 Sift 20: {"$lt":null} 0
125 Sift 21: {"$lt":"1970-01-01T00:00:00.003Z"} 0
126 Sift 22: {"$lte":5} 0
127 Sift 23: {"groups":{"$lt":5}} 0
128 Sift 24: {"$gt":5} 1
129 Sift 25: {"$gt":null} 0
130 Sift 26: {"groups":{"$gt":5}} 1
131 Sift 27: {"$gte":5} 0
132 Sift 28: {"groups":{"$gte":5}} 2
133 Sift 29: {"$mod":[2,1]} 0
134 Sift 30: {"groups":{"$mod":[2,0]}} 1
135 Sift 31: {"$exists":false} 0
136 Sift 32: {"$exists":true} 0
137 Sift 33: {"$in":[0,false,1,"1"]} 0
138 Sift 34: {"$in":[1,"1","2"]} 0
139 Sift 35: {"$in":["1970-01-01T00:00:00.001Z"]} 0
140 Sift 36: {"a.b.status":{"$in":[0]}} 1
141 Sift 37: {"a.b.status":{"$in":[0,2]}} 0
142 Sift 38: {"$nin":[0,false,1,"1"]} 0
143 Sift 39: {"$nin":[1,"1","2"]} 0
144 Sift 40: {"$nin":["1970-01-01T00:00:00.001Z"]} 1
145 Sift 41: {"$not":false} 0
146 Sift 42: {"$not":0} 1
147 Sift 43: {"$not":{"$in":[1,2,3]}} 0
148 Sift 44: {} 0
149 Sift 45: {} 0
150 Sift 46: {} 0
151 Sift 47: {} 0
152 Sift 48: {"$all":[1,2,3]} 1
153 Sift 49: {"$all":[0,false]} 0
154 Sift 50: {"$all":["1"]} 0
155 Sift 51: {"$all":["1970-01-01T00:00:00.001Z","1970-01-01T00:00:00.002Z"]} 0
156 Sift 52: {"$size":3} 0
157 Sift 53: {"$size":1} 0
158 Sift 54: {"$or":[1,2,3]} 0
159 Sift 55: {"$or":[{"$ne":1},2]} 1
160 Sift 56: {"$nor":[1,2,3]} 0
161 Sift 57: {"$nor":[{"$ne":1},2]} 0
162 Sift 58: {"$and":[{"$gt":1},{"$lt":4}]} 0
163 Sift 59: {"$regex":"^a"} 0
164 Sift 60: {"a":{"$regex":"b|c"}} 1
165 Sift 61: {"folder":{"$regex":"^[0-9]{4}$"}} 0
166 Sift 62: {"$regex":"^a","$options":"i"} 0
167 Sift 63: {"text":{"$regex":".*lis.*","$options":"i"}} 0
168 Sift 64: {"$regex":"a"} 0
169 Sift 65: {} 0
170 Sift 66: {} 0
171 Sift 67: {} 1
172 Sift 68: {"$where":"this.v === 1"} 0
173 Sift 69: {"$where":"obj.v === 1"} 0
174 Sift 70: {"a":{"$elemMatch":{"b":1,"c":2}}} 0
175 Sift 71: {"a":{"$elemMatch":{"b":2,"c":{"$gt":2}}}} 0
176 Sift 72: {"a.b":{}} 1
177 Sift 73: {"foo.0":"baz"} 0
178 Sift 74: {"foo.0.name":"baz"} 0

Code Coverage Report

100%
339
339
0

index.js

100%
13
13
0
Line Lint Hits Source
1 1 const {
2 Bout,
3 matches,
4 filter,
5 } = require('./lib/bout');
6
7 1 module.exports = {
8 curry: require('./lib/curry'),
9 utils: require('./lib/utils'),
10 Parser: require('./lib/parser'),
11 Bout,
12 matches,
13 filter,
14 };
15

lib/bout.js

100%
46
46
0
Line Lint Hits Source
1 1 const {
2 parse,
3 } = require('./parser');
4 1 const {
5 validate,
6 } = require('./utils');
7 1 const {
8 OPERATORS,
9 } = require('./operators');
10 1 const {
11 PREPARERS,
12 } = require('./preparers');
13 1 const curry = require('./curry');
14
15 class Bout{
16 constructor({pattern = {}, operators = {}, preparers = {}} = {}){
17 this.operators = Object.assign({}, OPERATORS, operators);
18 3 this.preparers = Object.assign({}, PREPARERS, preparers);
19 }
20
21 matches(pattern, ...args){
22 const {
23 operators,
24 preparers,
25 } = this;
26 179 const tree = parse(pattern, {operators, preparers});
27 178 if(args.length > 0){
28 14 return validate(tree, args[0]);
29 }
30 164 return curry(validate, tree);
31 }
32
33 filter(pattern, ...args){
34 const matcher = this.matches(pattern);
35 85 if(args.length > 0){
36 81 const arr = Array.isArray(args[0])?args[0]:[args[0]];
37 81 return arr.filter(matcher);
38 }
39 4 return (arr)=>Array.isArray(arr)?arr.filter(matcher):[arr].filter(matcher);
40 }
41 }
42
43 1 const bout = new Bout();
44 1 const matches = (pattern, ...args)=>bout.matches(pattern, ...args);
45 1 const filter = (pattern, ...args)=>bout.filter(pattern, ...args);
46
47 1 module.exports = {
48 Bout,
49 matches,
50 filter,
51 };
52

lib/curry.js

100%
2
2
0
Line Lint Hits Source
1 1 const curry = (fn, ...args1)=>(...args2)=>fn(...args1, ...args2);
2
3 1 module.exports = curry;
4

lib/operators.js

100%
79
79
0
Line Lint Hits Source
1 1 const {
2 compare,
3 comparable,
4 search,
5 validate,
6 and,
7 or,
8 isString,
9 } = require('./utils');
10
11 1 const OPERATORS = {
12 $eq: or((a, b)=>a(b)),
13
14 $ne: and((a, b)=>!a(b)),
15
16 $or(a, b){
17 return a.find((elem)=>{
18 return validate(elem, b);
19 })!==undefined;
20 },
21
22 $gt: or((a, b)=>compare(comparable(b), a)>0),
23
24 $gte: or((a, b)=>compare(comparable(b), a)>=0),
25
26 $lt: or((a, b)=>compare(comparable(b), a)<0),
27
28 $lte: or((a, b)=>compare(comparable(b), a)<=0),
29
30 $mod: or((a, b)=>b%a[0] == a[1]),
31
32 $in(a, b){
33 if(Array.isArray(b)){
34 8 return b.find((elem)=>~a.indexOf(comparable(elem)))!==undefined;
35 }
36 56 return !!~a.indexOf(comparable(b));
37 },
38
39 $nin(a, b){
40 return !OPERATORS.$in(a, b);
41 },
42
43 $not(a, b){
44 return !validate(a, b);
45 },
46
47 $type(a, b){
48 return b!=void(0)?b instanceof a || b.constructor == a : false;
49 },
50
51 $all(a, b){
52 if(!b){
53 2 b = [];
54 }
55 18 return a.every((elem)=>comparable(b).indexOf(elem)>-1);
56 },
57
58 $size(a, b){
59 return b ? a === b.length : false;
60 },
61
62 $nor(a, b){
63 //return !OPERATORS.$in(a, b);
64 for(let i = 0, n = a.length; i<n; i++){
65 32 if(validate(a[i], b)){
66 16 return false;
67 }
68 }
69 4 return true;
70 },
71
72 $and(a, b){
73 return a.every((elem)=>validate(elem, b));
74 },
75
76 $regex: or((a, b)=>{
77 return isString(b) && a.test(b);
78 }),
79
80 $where(a, b){
81 return a.call(b, b);
82 },
83
84 $elemMatch(a, b){
85 if(Array.isArray(b)){
86 4 return search(b, a)!==-1;
87 }
88 8 return validate(a, b);
89 },
90
91 $exists(a, b){
92 return (b != void 0) === a;
93 },
94 };
95
96 1 module.exports = {
97 and,
98 or,
99 OPERATORS,
100 };
101

lib/parser.js

100%
68
68
0
Line Lint Hits Source
1 1 const {
2 isFunction,
3 comparable,
4 search,
5 validate,
6 findValues,
7 } = require('./utils');
8 1 const {
9 OPERATORS,
10 } = require('./operators');
11 1 const {
12 PREPARERS,
13 } = require('./preparers');
14
15 1 const getQuery = (query)=>{
16 const cQuery = comparable(query);
17 271 if (!cQuery || (cQuery.constructor.toString().replace(/\n/g,'').replace(/ /g, '') !== 'functionObject(){[nativecode]}')) { // cross browser support
18 74 return { $eq: cQuery };
19 }
20 197 return cQuery;
21 };
22
23 1 const createValidator = (a, validate)=>{
24 return {
25 a,
26 validate
27 };
28 };
29
30 1 const nestedValidator = (a, b)=>{
31 const values = findValues(b, a.k, 0);
32 110 if(values.length === 1){
33 109 return validate(a.nv, values[0]);
34 }
35 1 return !!~search(values, a.nv);
36 };
37
38 1 const createNestedValidator = (path, a)=>{
39 return {
40 a: {
41 k: path,
42 nv: a
43 },
44 validate: nestedValidator
45 }
46 };
47
48 1 const getValidator = ({key, value, query, parse, operators = OPERATORS, preparers = PREPARERS})=>{
49 if(key === '$options'){
50 4 return false;
51 }
52 274 if(operators[key]){
53 215 if(preparers[key]){
54 142 const prepared = preparers[key](value, {query, parse});
55 142 return createValidator(comparable(prepared), operators[key]);
56 }
57 73 return createValidator(comparable(value), operators[key]);
58 }
59 59 if(key.charCodeAt(0)===36){
60 1 throw new Error(`Unkonwn operation ${key}`);
61 }
62 58 return createNestedValidator(key.split('.'), parse(value, {operators, preparers}));
63 };
64
65 1 const parse = (query, {operators = OPERATORS, preparers = PREPARERS} = {})=>{
66 const cQuery = getQuery(query);
67 271 const validators = Object.keys(cQuery)
68 .map((key)=>getValidator({key, value: cQuery[key], query: cQuery, parse, operators, preparers}))
69 .filter((v)=>v!==false);
70
71 270 return validators.length === 1?validators[0] : createValidator(validators, operators.$and);
72 };
73
74 1 module.exports = {
75 parse,
76 };
77

lib/preparers.js

100%
52
52
0
Line Lint Hits Source
1 1 const {
2 compare,
3 comparable,
4 isString,
5 } = require('./utils');
6
7 1 const PREPARERS = {
8 $eq(a){
9 if(a instanceof RegExp){
10 12 return (b)=>typeof(b)==='string' && a.test(b);
11 }
12 88 if(a instanceof Function){
13 1 return a;
14 }
15 87 if(Array.isArray(a) && !a.length){
16 2 return (b)=>Array.isArray(b) && !b.length;
17 }
18 85 if(a === null){
19 5 return (b)=>b == null;
20 }
21 80 return (b)=>compare(comparable(b), a)===0;
22 },
23
24 $ne(a){
25 return PREPARERS.$eq(a);
26 },
27
28 $and(a, {parse}){
29 return a.map(parse);
30 },
31
32 $or(a, {parse}){
33 return a.map(parse);
34 },
35
36 $nor(a, {parse}){
37 return a.map(parse);
38 },
39
40 $not(a, {parse}){
41 return parse(a);
42 },
43
44 $regex(a, {query}){
45 return new RegExp(a, query.$options);
46 },
47
48 $where(a){
49 return isString(a)?new Function('obj', `return ${a}`):a;
50 },
51
52 $elemMatch(a, {parse}){
53 return parse(a);
54 },
55
56 $exists(a){
57 return !!a;
58 },
59 };
60
61 1 module.exports = {
62 PREPARERS,
63 };
64

lib/utils.js

100%
79
79
0
Line Lint Hits Source
1 1 const curry = require('./curry');
2
3 1 const isType = (type, val)=>typeof(val)===type;
4
5 1 const isFunction = curry(isType, 'function');
6 1 const isString = curry(isType, 'string');
7
8 1 const compare = (a, b)=>{
9 if(a === b){
10 88 return 0;
11 }
12 214 if(typeof(a) === typeof(b)){
13 171 return (a > b)?1:-1;
14 }
15 };
16
17 1 const comparable = (value)=>{
18 if(value instanceof Date){
19 46 return value.getTime();
20 }
21 1015 if(Array.isArray(value)){
22 75 return value.map(comparable);
23 }
24 940 return value;
25 };
26
27 1 const search = (arr, validator)=>{
28 return arr.findIndex((item)=>validate(validator, item));
29 };
30
31 1 const validate = (validator, b)=>{
32 return validator.validate(validator.a, b);
33 };
34
35 1 const get = (obj, key)=>{
36 if(isFunction(obj.get)){
37 1 return obj.get(key);
38 }
39 209 return obj[key];
40 };
41
42 1 const or = (predicate)=>(a, b)=>{
43 if(!Array.isArray(b)){
44 312 return predicate(a, b);
45 }
46 31 for(let i = 0, n = b.length; i<n; i++){
47 51 if(predicate(a, get(b, i))){
48 21 return true;
49 }
50 }
51 10 return false;
52 };
53
54 1 const and = (predicate)=>(a, b)=>{
55 if(!Array.isArray(b) || !b.length){
56 57 return predicate(a, b);
57 }
58 8 for(let i = 0, n = b.length; i<n; i++){
59 12 if(!predicate(a, get(b, i))){
60 4 return false;
61 }
62 }
63 4 return true;
64 };
65
66 1 const findValues = (current, path, index, values = [])=>{
67 if(index === path.length || current == void 0){
68 111 return [...values, current];
69 }
70 158 const key = path[index];
71
72 158 if(Array.isArray(current) && isNaN(Number(key))){
73 11 return [...values, ...current.map((item)=>findValues(item, path, index, values)).filter((v)=>v !== void 0)];
74 }
75 147 return findValues(get(current, key), path, index+1, values);
76 };
77
78 1 module.exports = {
79 get,
80 curry,
81 isType,
82 isFunction,
83 isString,
84 compare,
85 comparable,
86 search,
87 validate,
88 and,
89 or,
90 findValues,
91 };
92

Linting Report

Nothing to show here, linting is disabled.