Skip to content

Commit a65184a

Browse files
committed
fix: use addConstraint in rql and handle repeated properties with an $and
1 parent 7e9dda8 commit a65184a

File tree

4 files changed

+42
-33
lines changed

4 files changed

+42
-33
lines changed

src/mongo/rql.ts

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ObjectId } from 'bson';
2-
import * as _ from 'lodash';
2+
import { addConstraint } from './util';
33

44
const rqlParser = require('rql/parser').parseQuery;
55

@@ -8,69 +8,74 @@ export default function (query: any, opts: any, data: string, objectId?: string)
88
switch (data.name) {
99
case 'in':
1010
const key = data.args.shift();
11-
query[key] = { $in: data.args.map((i) => (key === objectId ? new ObjectId(i) : i)) };
11+
query = addConstraint(query, { [key]: { $in: data.args.map((i) => (key === objectId ? new ObjectId(i) : i)) } });
1212
break;
1313
case 'contains':
14-
query[data.args[0]] = data.args.length > 2 ? data.args.slice(1) : data.args[1];
14+
query = addConstraint(query, { [data.args[0]]: data.args.length > 2 ? data.args.slice(1) : data.args[1] });
1515
break;
1616
case 'and':
1717
if (data.args.length === 1) {
18-
query = _rqlToMongo(query, opts, data.args[0]);
19-
} else if (_.find(data.args, (i: any) => i.name === 'or' || i.name === 'and')) {
20-
query.$and = [];
21-
_.each(data.args, function (i) {
18+
query = addConstraint(query, _rqlToMongo({}, opts, data.args[0]));
19+
} else if (data.args.find((i: any) => i.name === 'or' || i.name === 'and')) {
20+
let constraint: any = { $and: [] };
21+
data.args.forEach((i) => {
2222
let _p = _rqlToMongo({}, opts, i);
23-
if (_p) query.$and.push(_p);
23+
if (_p) constraint.$and.push(_p);
2424
});
25-
if (query.$and.length === 1) {
26-
query = query.$and[0];
25+
if (constraint.$and.length === 1) {
26+
constraint = constraint.$and[0];
2727
}
28+
query = addConstraint(query, constraint);
2829
} else {
29-
_.each(data.args, function (i) {
30-
query = _rqlToMongo(query, opts, i);
30+
data.args.forEach((i) => {
31+
query = addConstraint(query, _rqlToMongo({}, opts, i));
3132
});
3233
}
3334
break;
3435
case 'or':
3536
if (data.args.length === 1) {
36-
query = _rqlToMongo(query, opts, data.args[0]);
37+
query = addConstraint(query, _rqlToMongo({}, opts, data.args[0]));
3738
} else {
38-
query.$or = [];
39-
_.each(data.args, function (i) {
39+
let constraint: any = { $or: [] };
40+
data.args.forEach((i) => {
4041
let _p = _rqlToMongo({}, opts, i);
41-
if (_p) query.$or.push(_p);
42+
if (_p) constraint.$or.push(_p);
4243
});
43-
if (query.$or.length === 1) {
44-
query = query.$or[0];
44+
if (constraint.$or.length === 1) {
45+
constraint = constraint.$or[0];
4546
}
47+
query = addConstraint(query, constraint);
4648
}
4749
break;
4850
case 'eq':
49-
query[data.args[0]] = data.args[0] === objectId ? new ObjectId(data.args[1]) : data.args[1];
51+
query = addConstraint(query, { [data.args[0]]: data.args[0] === objectId ? new ObjectId(data.args[1]) : data.args[1] });
5052
break;
5153
case 'lt':
52-
query[data.args[0]] = { $lt: data.args[1] };
54+
query = addConstraint(query, { [data.args[0]]: { $lt: data.args[1] } });
5355
break;
5456
case 'le':
55-
query[data.args[0]] = { $lte: data.args[1] };
57+
query = addConstraint(query, { [data.args[0]]: { $lte: data.args[1] } });
5658
break;
5759
case 'gt':
58-
query[data.args[0]] = { $gt: data.args[1] };
60+
query = addConstraint(query, { [data.args[0]]: { $gt: data.args[1] } });
5961
break;
6062
case 'ge':
61-
query[data.args[0]] = { $gte: data.args[1] };
63+
query = addConstraint(query, { [data.args[0]]: { $gte: data.args[1] } });
6264
break;
6365
case 'ne':
64-
query[data.args[0]] = { $ne: data.args[1] };
66+
query = addConstraint(query, { [data.args[0]]: { $ne: data.args[1] } });
6567
break;
6668
case 'matches':
67-
query[data.args[0]] = new RegExp(data.args[1], data.args[2]);
69+
query = addConstraint(query, { [data.args[0]]: new RegExp(data.args[1], data.args[2]) });
6870
break;
6971
case 'text':
70-
query.$text = {
71-
$search: data.args[0],
72+
const constraint: any = {
73+
$text: {
74+
$search: data.args[0],
75+
},
7276
};
73-
if (data.args[1]) query.$text.$language = data.args[1];
77+
if (data.args[1]) constraint.$text.$language = data.args[1];
78+
query = addConstraint(query, constraint);
7479
break;
7580
case 'sort':
7681
query = null;

src/mongo/util.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export function addConstraint(query: any, constraint: any): any {
77
if (!query) {
88
query = {};
99
}
10-
if (typeof constraint === 'object') {
10+
if (typeof constraint === 'object' && constraint !== null) {
1111
if (Array.isArray(query)) {
1212
if (Array.isArray(constraint)) {
1313
query = query.concat(constraint);
@@ -29,7 +29,11 @@ export function addConstraint(query: any, constraint: any): any {
2929
query.$and.push(constraint);
3030
}
3131
} else {
32-
Object.assign(query, constraint);
32+
if (Object.keys(constraint).find((k) => (typeof query[k] !== 'undefined' ? k : undefined))) {
33+
query = { $and: [query, constraint] };
34+
} else {
35+
Object.assign(query, constraint);
36+
}
3337
}
3438
}
3539
}

test/ts/mongo.test.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -609,7 +609,6 @@ describe('mongo', function () {
609609
});
610610

611611
it('should return selected objects in the collection as CSV', function () {
612-
debugger;
613612
return request
614613
.get('/tests?q=gt(y,0)&sort=y&format=csv&csv_fields=myid,y,z')
615614
.expect(200)
@@ -621,7 +620,6 @@ describe('mongo', function () {
621620
});
622621

623622
it('should return selected objects in the collection as CSV with a header', function () {
624-
debugger;
625623
return request
626624
.get('/tests?q=gt(y,0)&sort=y&format=csv&csv_fields=myid,y,z&csv_options=header=true')
627625
.expect(200)
@@ -633,7 +631,6 @@ describe('mongo', function () {
633631
});
634632

635633
it('should return selected objects in the collection as CSV with names remapped', function () {
636-
debugger;
637634
return request
638635
.get('/tests?q=gt(y,0)&sort=y&format=csv&csv_fields=myid,y,z&csv_names=a,b,c&csv_options=header=true')
639636
.expect(200)

test/ts/mongo.util.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ describe('util', function () {
7171
it('ahould transform a query into a pipeline if the constraint is a pipeline', function () {
7272
addConstraint({ a: 1 }, [{ $somestage: {} }]).should.deep.equal([{ $match: { a: 1 } }, { $somestage: {} }]);
7373
});
74+
it('should handle multiple constraints on a property producing an $and', function () {
75+
addConstraint({ a: 1 }, { a: 2 }).should.deep.equal({ $and: [{ a: 1 }, { a: 2 }] });
76+
});
7477
});
7578
});
7679

0 commit comments

Comments
 (0)