diff --git a/integration/test/ParseQueryTest.js b/integration/test/ParseQueryTest.js index 16ec9d92e..6c681673a 100644 --- a/integration/test/ParseQueryTest.js +++ b/integration/test/ParseQueryTest.js @@ -1454,6 +1454,55 @@ describe('Parse Query', () => { }).catch(e => console.log(e)); }); + it('can build NOR queries', async () => { + const objects = []; + for (let i = 0; i < 10; i += 1) { + const obj = new Parse.Object('NORTest'); + obj.set({ x: i }); + objects.push(obj); + } + await Parse.Object.saveAll(objects); + + const q1 = new Parse.Query('NORTest'); + q1.greaterThan('x', 5); + const q2 = new Parse.Query('NORTest'); + q2.lessThan('x', 3); + const norQuery = Parse.Query.nor(q1, q2); + const results = await norQuery.find(); + + assert.equal(results.length, 3); + results.forEach((number) => { + assert(number.get('x') >= 3 && number.get('x') <= 5); + }); + }); + + it('can build complex NOR queries', async () => { + const objects = []; + for (let i = 0; i < 10; i += 1) { + const child = new Parse.Object('Child'); + child.set('x', i); + const parent = new Parse.Object('Parent'); + parent.set('child', child); + parent.set('y', i); + objects.push(parent); + } + await Parse.Object.saveAll(objects); + + const subQuery = new Parse.Query('Child'); + subQuery.equalTo('x', 4); + const q1 = new Parse.Query('Parent'); + q1.matchesQuery('child', subQuery); + const q2 = new Parse.Query('Parent'); + q2.equalTo('y', 5); + const norQuery = new Parse.Query.nor(q1, q2); + const results = await norQuery.find(); + + assert.equal(results.length, 8); + results.forEach((number) => { + assert(number.get('x') !== 4 || number.get('x') !== 5); + }); + }); + it('can iterate over results with each', (done) => { let items = []; for (let i = 0; i < 50; i++) { diff --git a/src/ParseQuery.js b/src/ParseQuery.js index 714954d58..86eda3f56 100644 --- a/src/ParseQuery.js +++ b/src/ParseQuery.js @@ -237,7 +237,6 @@ class ParseQuery { /** * Adds constraint that all of the passed in queries match. - * @method _andQuery * @param {Array} queries * @return {Parse.Query} Returns the query, so you can chain this call. */ @@ -250,6 +249,20 @@ class ParseQuery { return this; } + /** + * Adds constraint that none of the passed in queries match. + * @param {Array} queries + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + _norQuery(queries: Array): ParseQuery { + var queryJSON = queries.map((q) => { + return q.toJSON().where; + }); + + this._where.$nor = queryJSON; + return this; + } + /** * Helper for condition queries */ @@ -1422,6 +1435,24 @@ class ParseQuery { query._andQuery(queries); return query; } + + /** + * Constructs a Parse.Query that is the NOR of the passed in queries. For + * example: + *
const compoundQuery = Parse.Query.nor(query1, query2, query3);
+ * + * will create a compoundQuery that is a nor of the query1, query2, and + * query3. + * @param {...Parse.Query} var_args The list of queries to NOR. + * @static + * @return {Parse.Query} The query that is the NOR of the passed in queries. + */ + static nor(...queries: Array): ParseQuery { + const className = _getClassNameFromQueries(queries); + const query = new ParseQuery(className); + query._norQuery(queries); + return query; + } } var DefaultController = { diff --git a/src/__tests__/ParseQuery-test.js b/src/__tests__/ParseQuery-test.js index 459933c67..3e50767cb 100644 --- a/src/__tests__/ParseQuery-test.js +++ b/src/__tests__/ParseQuery-test.js @@ -1035,6 +1035,40 @@ describe('ParseQuery', () => { }); }); + it('can combine queries with a NOR clause', () => { + const q = new ParseQuery('Item'); + let q2 = new ParseQuery('Purchase'); + expect(ParseQuery.nor.bind(null, q, q2)).toThrow( + 'All queries must be for the same class.', + ); + + q2 = new ParseQuery('Item'); + q.equalTo('size', 'medium'); + q2.equalTo('size', 'large'); + + let mediumOrLarge = ParseQuery.nor(q, q2); + expect(mediumOrLarge.toJSON()).toEqual({ + where: { + $nor: [ + { size: 'medium' }, + { size: 'large' }, + ], + }, + }); + + // It removes limits, skips, etc + q.limit(10); + mediumOrLarge = ParseQuery.nor(q, q2); + expect(mediumOrLarge.toJSON()).toEqual({ + where: { + $nor: [ + { size: 'medium' }, + { size: 'large' }, + ], + }, + }); + }); + it('can get the first object of a query', (done) => { CoreManager.setQueryController({ aggregate() {},