Skip to content

Commit 77ed10f

Browse files
jeremypiednoeldplewis
authored andcommitted
Adding $nor operator support (#4768)
* adding nor to specialQuerykeys * adding nor suport * adding test * CRLF * adding postgres NOR * adding nor validation * adding NOR test * adding test amd fixing NOR cases * revert the nor logic
1 parent a9ebc2b commit 77ed10f

File tree

4 files changed

+74
-6
lines changed

4 files changed

+74
-6
lines changed

spec/ParseQuery.spec.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2567,6 +2567,63 @@ describe('Parse.Query testing', () => {
25672567
});
25682568
});
25692569

2570+
it('$nor valid query', (done) => {
2571+
const objects = Array.from(Array(10).keys()).map((rating) => {
2572+
return new TestObject({ 'rating': rating });
2573+
});
2574+
2575+
const highValue = 5;
2576+
const lowValue = 3;
2577+
const options = Object.assign({}, masterKeyOptions, {
2578+
body: {
2579+
where: {
2580+
$nor: [
2581+
{ rating : { $gt : highValue } },
2582+
{ rating : { $lte : lowValue } },
2583+
]
2584+
},
2585+
}
2586+
});
2587+
2588+
Parse.Object.saveAll(objects).then(() => {
2589+
return rp.get(Parse.serverURL + "/classes/TestObject", options);
2590+
}).then((results) => {
2591+
expect(results.results.length).toBe(highValue - lowValue);
2592+
expect(results.results.every(res => res.rating > lowValue && res.rating <= highValue)).toBe(true);
2593+
done();
2594+
});
2595+
});
2596+
2597+
it('$nor invalid query - empty array', (done) => {
2598+
const options = Object.assign({}, masterKeyOptions, {
2599+
body: {
2600+
where: { $nor: [] },
2601+
}
2602+
});
2603+
const obj = new TestObject();
2604+
obj.save().then(() => {
2605+
return rp.get(Parse.serverURL + "/classes/TestObject", options);
2606+
}).then(done.fail).catch((error) => {
2607+
equal(error.error.code, Parse.Error.INVALID_QUERY);
2608+
done();
2609+
});
2610+
});
2611+
2612+
it('$nor invalid query - wrong type', (done) => {
2613+
const options = Object.assign({}, masterKeyOptions, {
2614+
body: {
2615+
where: { $nor: 1337 },
2616+
}
2617+
});
2618+
const obj = new TestObject();
2619+
obj.save().then(() => {
2620+
return rp.get(Parse.serverURL + "/classes/TestObject", options);
2621+
}).then(done.fail).catch((error) => {
2622+
equal(error.error.code, Parse.Error.INVALID_QUERY);
2623+
done();
2624+
});
2625+
});
2626+
25702627
it("dontSelect query", function(done) {
25712628
const RestaurantObject = Parse.Object.extend("Restaurant");
25722629
const PersonObject = Parse.Object.extend("Person");

src/Adapters/Storage/Mongo/MongoTransform.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,9 +247,9 @@ function transformQueryKeyValue(className, key, value, schema) {
247247
case '_perishable_token':
248248
case '_email_verify_token': return {key, value}
249249
case '$or':
250-
return {key: '$or', value: value.map(subQuery => transformWhere(className, subQuery, schema))};
251250
case '$and':
252-
return {key: '$and', value: value.map(subQuery => transformWhere(className, subQuery, schema))};
251+
case '$nor':
252+
return {key: key, value: value.map(subQuery => transformWhere(className, subQuery, schema))};
253253
case 'lastUsed':
254254
if (valueAsDate(value)) {
255255
return {key: '_last_used', value: valueAsDate(value)}

src/Adapters/Storage/Postgres/PostgresStorageAdapter.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ const buildWhereClause = ({ schema, query, index }): WhereClause => {
306306
patterns.push(`$${index}:name = $${index + 1}`);
307307
values.push(fieldName, fieldValue);
308308
index += 2;
309-
} else if (fieldName === '$or' || fieldName === '$and') {
309+
} else if (['$or', '$nor', '$and'].includes(fieldName)) {
310310
const clauses = [];
311311
const clauseValues = [];
312312
fieldValue.forEach((subQuery) => {
@@ -317,8 +317,11 @@ const buildWhereClause = ({ schema, query, index }): WhereClause => {
317317
index += clause.values.length;
318318
}
319319
});
320-
const orOrAnd = fieldName === '$or' ? ' OR ' : ' AND ';
321-
patterns.push(`(${clauses.join(orOrAnd)})`);
320+
321+
const orOrAnd = fieldName === '$and' ? ' AND ' : ' OR ';
322+
const not = fieldName === '$nor' ? ' NOT ' : '';
323+
324+
patterns.push(`${not}(${clauses.join(orOrAnd)})`);
322325
values.push(...clauseValues);
323326
}
324327

src/Controllers/DatabaseController.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ const transformObjectACL = ({ ACL, ...result }) => {
5050
return result;
5151
}
5252

53-
const specialQuerykeys = ['$and', '$or', '_rperm', '_wperm', '_perishable_token', '_email_verify_token', '_email_verify_token_expires_at', '_account_lockout_expires_at', '_failed_login_count'];
53+
const specialQuerykeys = ['$and', '$or', '$nor', '_rperm', '_wperm', '_perishable_token', '_email_verify_token', '_email_verify_token_expires_at', '_account_lockout_expires_at', '_failed_login_count'];
5454

5555
const isSpecialQueryKey = key => {
5656
return specialQuerykeys.indexOf(key) >= 0;
@@ -111,6 +111,14 @@ const validateQuery = (query: any): void => {
111111
}
112112
}
113113

114+
if (query.$nor) {
115+
if (query.$nor instanceof Array && query.$nor.length > 0) {
116+
query.$nor.forEach(validateQuery);
117+
} else {
118+
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Bad $nor format - use an array of at least 1 value.');
119+
}
120+
}
121+
114122
Object.keys(query).forEach(key => {
115123
if (query && query[key] && query[key].$regex) {
116124
if (typeof query[key].$options === 'string') {

0 commit comments

Comments
 (0)