Skip to content

Commit 3d7c97b

Browse files
gofabianmontymxb
authored andcommitted
Add Parse.Query.and() to request that all queries must match (#367)
* Add Parse.Query.and() to request that all queries must match * Add test for Parse.Query.and() * Add integration tests for Parse.Query.and()
1 parent 741958a commit 3d7c97b

File tree

3 files changed

+141
-11
lines changed

3 files changed

+141
-11
lines changed

integration/test/ParseQueryTest.js

+54
Original file line numberDiff line numberDiff line change
@@ -1310,6 +1310,60 @@ describe('Parse Query', () => {
13101310
});
13111311
});
13121312

1313+
it('can build AND queries', (done) => {
1314+
let objects = [];
1315+
for (let i = 0; i < 10; i++) {
1316+
let obj = new Parse.Object('BoxedNumber');
1317+
obj.set({ x: i, and: true });
1318+
objects.push(obj);
1319+
}
1320+
Parse.Object.saveAll(objects).then(() => {
1321+
let q1 = new Parse.Query('BoxedNumber');
1322+
q1.equalTo('and', true);
1323+
q1.greaterThan('x', 2);
1324+
let q2 = new Parse.Query('BoxedNumber');
1325+
q2.equalTo('and', true);
1326+
q2.lessThan('x', 5);
1327+
let andQuery = Parse.Query.and(q1, q2);
1328+
return andQuery.find();
1329+
}).then((results) => {
1330+
assert.equal(results.length, 2);
1331+
results.forEach((number) => {
1332+
assert(number.get('x') > 2 && number.get('x') < 5);
1333+
});
1334+
done();
1335+
}).fail(e => console.log(e));
1336+
});
1337+
1338+
it('can build complex AND queries', (done) => {
1339+
let objects = [];
1340+
for (let i = 0; i < 10; i++) {
1341+
let child = new Parse.Object('Child');
1342+
child.set('x', i);
1343+
child.set('and', true);
1344+
let parent = new Parse.Object('Parent');
1345+
parent.set('child', child);
1346+
parent.set('and', true);
1347+
parent.set('y', i);
1348+
objects.push(parent);
1349+
}
1350+
Parse.Object.saveAll(objects).then(() => {
1351+
let subQuery = new Parse.Query('Child');
1352+
subQuery.equalTo('x', 4);
1353+
subQuery.equalTo('and', true);
1354+
let q1 = new Parse.Query('Parent');
1355+
q1.matchesQuery('child', subQuery);
1356+
let q2 = new Parse.Query('Parent');
1357+
q2.equalTo('and', true);
1358+
q2.equalTo('y', 4);
1359+
let andQuery = new Parse.Query.and(q1, q2);
1360+
return andQuery.find();
1361+
}).then((results) => {
1362+
assert.equal(results.length, 1);
1363+
done();
1364+
}).fail(e => console.log(e));
1365+
});
1366+
13131367
it('can iterate over results with each', (done) => {
13141368
let items = [];
13151369
for (let i = 0; i < 50; i++) {

src/ParseQuery.js

+53-11
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,24 @@ function quote(s: string) {
4646
return '\\Q' + s.replace('\\E', '\\E\\\\E\\Q') + '\\E';
4747
}
4848

49+
/**
50+
* Extracts the class name from queries. If not all queries have the same
51+
* class name an error will be thrown.
52+
*/
53+
function _getClassNameFromQueries(queries: Array<ParseQuery>): string {
54+
var className = null;
55+
queries.forEach((q) => {
56+
if (!className) {
57+
className = q.className;
58+
}
59+
60+
if (className !== q.className) {
61+
throw new Error('All queries must be for the same class.');
62+
}
63+
});
64+
return className;
65+
}
66+
4967
/*
5068
* Handles pre-populating the result data of a query with select fields,
5169
* making sure that the data object contains keys for all objects that have
@@ -229,6 +247,21 @@ class ParseQuery {
229247
return this;
230248
}
231249

250+
/**
251+
* Adds constraint that all of the passed in queries match.
252+
* @method _andQuery
253+
* @param {Array} queries
254+
* @return {Parse.Query} Returns the query, so you can chain this call.
255+
*/
256+
_andQuery(queries: Array<ParseQuery>): ParseQuery {
257+
var queryJSON = queries.map((q) => {
258+
return q.toJSON().where;
259+
});
260+
261+
this._where.$and = queryJSON;
262+
return this;
263+
}
264+
232265
/**
233266
* Helper for condition queries
234267
*/
@@ -1288,21 +1321,30 @@ class ParseQuery {
12881321
* @return {Parse.Query} The query that is the OR of the passed in queries.
12891322
*/
12901323
static or(...queries: Array<ParseQuery>): ParseQuery {
1291-
var className = null;
1292-
queries.forEach((q) => {
1293-
if (!className) {
1294-
className = q.className;
1295-
}
1296-
1297-
if (className !== q.className) {
1298-
throw new Error('All queries must be for the same class.');
1299-
}
1300-
});
1301-
1324+
var className = _getClassNameFromQueries(queries);
13021325
var query = new ParseQuery(className);
13031326
query._orQuery(queries);
13041327
return query;
13051328
}
1329+
1330+
/**
1331+
* Constructs a Parse.Query that is the AND of the passed in queries. For
1332+
* example:
1333+
* <pre>var compoundQuery = Parse.Query.and(query1, query2, query3);</pre>
1334+
*
1335+
* will create a compoundQuery that is an and of the query1, query2, and
1336+
* query3.
1337+
* @method and
1338+
* @param {...Parse.Query} var_args The list of queries to AND.
1339+
* @static
1340+
* @return {Parse.Query} The query that is the AND of the passed in queries.
1341+
*/
1342+
static and(...queries: Array<ParseQuery>): ParseQuery {
1343+
var className = _getClassNameFromQueries(queries);
1344+
var query = new ParseQuery(className);
1345+
query._andQuery(queries);
1346+
return query;
1347+
}
13061348
}
13071349

13081350
var DefaultController = {

src/__tests__/ParseQuery-test.js

+34
Original file line numberDiff line numberDiff line change
@@ -883,6 +883,40 @@ describe('ParseQuery', () => {
883883
});
884884
});
885885

886+
it('can combine queries with an AND clause', () => {
887+
var q = new ParseQuery('Item');
888+
var q2 = new ParseQuery('Purchase');
889+
expect(ParseQuery.and.bind(null, q, q2)).toThrow(
890+
'All queries must be for the same class.'
891+
);
892+
893+
q2 = new ParseQuery('Item');
894+
q.equalTo('size', 'medium');
895+
q2.equalTo('size', 'large');
896+
897+
var mediumOrLarge = ParseQuery.and(q, q2);
898+
expect(mediumOrLarge.toJSON()).toEqual({
899+
where: {
900+
$and: [
901+
{ size: 'medium' },
902+
{ size: 'large' }
903+
]
904+
}
905+
});
906+
907+
// It removes limits, skips, etc
908+
q.limit(10);
909+
mediumOrLarge = ParseQuery.and(q, q2);
910+
expect(mediumOrLarge.toJSON()).toEqual({
911+
where: {
912+
$and: [
913+
{ size: 'medium' },
914+
{ size: 'large' }
915+
]
916+
}
917+
});
918+
});
919+
886920
it('can get the first object of a query', (done) => {
887921
CoreManager.setQueryController({
888922
aggregate() {},

0 commit comments

Comments
 (0)