Skip to content

Commit c654621

Browse files
dplewisflovilmart
authored andcommitted
PG: Support for nested contains and containedIn (#4109)
1 parent c2c059f commit c654621

File tree

2 files changed

+94
-11
lines changed

2 files changed

+94
-11
lines changed

spec/ParseQuery.spec.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,40 @@ describe('Parse.Query testing', () => {
289289
}, done.fail);
290290
});
291291

292+
it('nested containedIn string', (done) => {
293+
const sender1 = { group: ['A', 'B'] };
294+
const sender2 = { group: ['A', 'C'] };
295+
const sender3 = { group: ['B', 'C'] };
296+
const obj1 = new TestObject({ sender: sender1 });
297+
const obj2 = new TestObject({ sender: sender2 });
298+
const obj3 = new TestObject({ sender: sender3 });
299+
Parse.Object.saveAll([obj1, obj2, obj3]).then(() => {
300+
const query = new Parse.Query(TestObject);
301+
query.containedIn('sender.group', ['A']);
302+
return query.find();
303+
}).then((results) => {
304+
equal(results.length, 2);
305+
done();
306+
}, done.fail);
307+
});
308+
309+
it('nested containedIn number', (done) => {
310+
const sender1 = { group: [1, 2] };
311+
const sender2 = { group: [1, 3] };
312+
const sender3 = { group: [2, 3] };
313+
const obj1 = new TestObject({ sender: sender1 });
314+
const obj2 = new TestObject({ sender: sender2 });
315+
const obj3 = new TestObject({ sender: sender3 });
316+
Parse.Object.saveAll([obj1, obj2, obj3]).then(() => {
317+
const query = new Parse.Query(TestObject);
318+
query.containedIn('sender.group', [1]);
319+
return query.find();
320+
}).then((results) => {
321+
equal(results.length, 2);
322+
done();
323+
}, done.fail);
324+
});
325+
292326
it("containsAll number array queries", function(done) {
293327
var NumberSet = Parse.Object.extend({ className: "NumberSet" });
294328

@@ -1379,6 +1413,23 @@ describe('Parse.Query testing', () => {
13791413
});
13801414
});
13811415

1416+
it('nested contains', (done) => {
1417+
const sender1 = { group: ['A', 'B'] };
1418+
const sender2 = { group: ['A', 'C'] };
1419+
const sender3 = { group: ['B', 'C'] };
1420+
const obj1 = new TestObject({ sender: sender1 });
1421+
const obj2 = new TestObject({ sender: sender2 });
1422+
const obj3 = new TestObject({ sender: sender3 });
1423+
Parse.Object.saveAll([obj1, obj2, obj3]).then(() => {
1424+
const query = new Parse.Query(TestObject);
1425+
query.contains('sender.group', 'A');
1426+
return query.find();
1427+
}).then((results) => {
1428+
equal(results.length, 2);
1429+
done();
1430+
}, done.fail);
1431+
});
1432+
13821433
it("startsWith", function(done) {
13831434
Parse.Object.saveAll([new TestObject({myString: "zax" + someAscii + "qub"}),
13841435
new TestObject({myString: "start" + someAscii}),

src/Adapters/Storage/Postgres/PostgresStorageAdapter.js

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,25 @@ const handleDotFields = (object) => {
146146
return object;
147147
}
148148

149+
const transformDotFieldToComponents = (fieldName) => {
150+
return fieldName.split('.').map((cmpt, index) => {
151+
if (index === 0) {
152+
return `"${cmpt}"`;
153+
}
154+
return `'${cmpt}'`;
155+
});
156+
}
157+
158+
const transformDotField = (fieldName) => {
159+
if (fieldName.indexOf('.') === -1) {
160+
return `"${fieldName}"`;
161+
}
162+
const components = transformDotFieldToComponents(fieldName);
163+
let name = components.slice(0, components.length - 1).join('->');
164+
name += '->>' + components[components.length - 1];
165+
return name;
166+
}
167+
149168
const validateKeys = (object) => {
150169
if (typeof object == 'object') {
151170
for (const key in object) {
@@ -195,18 +214,26 @@ const buildWhereClause = ({ schema, query, index }) => {
195214
}
196215

197216
if (fieldName.indexOf('.') >= 0) {
198-
const components = fieldName.split('.').map((cmpt, index) => {
199-
if (index === 0) {
200-
return `"${cmpt}"`;
201-
}
202-
return `'${cmpt}'`;
203-
});
204-
let name = components.slice(0, components.length - 1).join('->');
205-
name += '->>' + components[components.length - 1];
217+
let name = transformDotField(fieldName);
206218
if (fieldValue === null) {
207219
patterns.push(`${name} IS NULL`);
208220
} else {
209-
patterns.push(`${name} = '${fieldValue}'`);
221+
if (fieldValue.$in) {
222+
const inPatterns = [];
223+
name = transformDotFieldToComponents(fieldName).join('->');
224+
fieldValue.$in.forEach((listElem) => {
225+
if (typeof listElem === 'string') {
226+
inPatterns.push(`"${listElem}"`);
227+
} else {
228+
inPatterns.push(`${listElem}`);
229+
}
230+
});
231+
patterns.push(`(${name})::jsonb @> '[${inPatterns.join(',')}]'::jsonb`);
232+
} else if (fieldValue.$regex) {
233+
// Handle later
234+
} else {
235+
patterns.push(`${name} = '${fieldValue}'`);
236+
}
210237
}
211238
} else if (fieldValue === null) {
212239
patterns.push(`$${index}:name IS NULL`);
@@ -298,6 +325,10 @@ const buildWhereClause = ({ schema, query, index }) => {
298325
values.push(fieldName, JSON.stringify(baseArray));
299326
index += 2;
300327
} else {
328+
// Handle Nested Dot Notation Above
329+
if (fieldName.indexOf('.') >= 0) {
330+
return;
331+
}
301332
const inPatterns = [];
302333
values.push(fieldName);
303334
baseArray.forEach((listElem, listIndex) => {
@@ -466,10 +497,11 @@ const buildWhereClause = ({ schema, query, index }) => {
466497
}
467498
}
468499

500+
const name = transformDotField(fieldName);
469501
regex = processRegexPattern(regex);
470502

471-
patterns.push(`$${index}:name ${operator} '$${index + 1}:raw'`);
472-
values.push(fieldName, regex);
503+
patterns.push(`$${index}:raw ${operator} '$${index + 1}:raw'`);
504+
values.push(name, regex);
473505
index += 2;
474506
}
475507

0 commit comments

Comments
 (0)