diff --git a/src/lib/Filters.js b/src/lib/Filters.js index 56533e003..9dc738b66 100644 --- a/src/lib/Filters.js +++ b/src/lib/Filters.js @@ -64,6 +64,12 @@ export const Constraints = { composable: true, comparable: true, }, + contains: { + name: 'contains substring', + field: 'String', + composable: true, + comparable: true, + }, before: { name: 'is before', field: 'Date', @@ -185,7 +191,18 @@ export const FieldConstraints = { Pointer: ['exists', 'dne', 'eq', 'neq', 'starts', 'containedIn', 'unique'], Boolean: ['exists', 'dne', 'eq', 'neq', 'containedIn', 'unique'], Number: ['exists', 'dne', 'eq', 'neq', 'lt', 'lte', 'gt', 'gte', 'containedIn', 'unique'], - String: ['exists', 'dne', 'eq', 'neq', 'starts', 'ends', 'matches', 'containedIn', 'unique'], + String: [ + 'exists', + 'dne', + 'eq', + 'neq', + 'starts', + 'ends', + 'matches', + 'contains', + 'containedIn', + 'unique', + ], Date: [ 'exists', 'dne', diff --git a/src/lib/queryFromFilters.js b/src/lib/queryFromFilters.js index 865affd00..168b9c160 100644 --- a/src/lib/queryFromFilters.js +++ b/src/lib/queryFromFilters.js @@ -232,6 +232,9 @@ export function addConstraintFromValues(query, field, constraint, compareTo, mod case 'matches': query.matches(field, String(compareTo), modifiers); break; + case 'contains': + query.contains(field, String(compareTo)); + break; case 'keyExists': query.exists(field + '.' + compareTo); break; diff --git a/src/lib/tests/queryFromFilters.test.js b/src/lib/tests/queryFromFilters.test.js new file mode 100644 index 000000000..dcbb7f8f1 --- /dev/null +++ b/src/lib/tests/queryFromFilters.test.js @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016-present, Parse, LLC + * All rights reserved. + * + * This source code is licensed under the license found in the LICENSE file in + * the root directory of this source tree. + */ +jest.dontMock('../queryFromFilters'); + +const { addConstraintFromValues } = require('../queryFromFilters'); +const Parse = require('parse'); + +function whereFor(field, constraint, compareTo, modifiers) { + const query = new Parse.Query('Test'); + addConstraintFromValues(query, field, constraint, compareTo, modifiers); + return query.toJSON().where; +} + +describe('addConstraintFromValues contains constraint', () => { + it('matches a plain substring literally', () => { + expect(whereFor('name', 'contains', 'foo')).toEqual({ + name: { $regex: '\\Qfoo\\E' }, + }); + }); + + it('escapes regex metacharacters so they match literally', () => { + expect(whereFor('name', 'contains', 'a.b(c)')).toEqual({ + name: { $regex: '\\Qa.b(c)\\E' }, + }); + }); + + it('keeps matches as a raw, unescaped regex with modifiers', () => { + expect(whereFor('name', 'matches', 'a.b(c)', 'i')).toEqual({ + name: { $regex: 'a.b(c)', $options: 'i' }, + }); + }); +});