feat: Add support for dot notation on array fields of Parse Object (#9115)
This commit is contained in:
@@ -568,6 +568,70 @@ describe('Parse.Object testing', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it_only_db('mongo')('can increment array nested fields', async () => {
|
||||||
|
const obj = new TestObject();
|
||||||
|
obj.set('items', [ { value: 'a', count: 5 }, { value: 'b', count: 1 } ]);
|
||||||
|
await obj.save();
|
||||||
|
obj.increment('items.0.count', 15);
|
||||||
|
obj.increment('items.1.count', 4);
|
||||||
|
await obj.save();
|
||||||
|
expect(obj.toJSON().items[0].value).toBe('a');
|
||||||
|
expect(obj.toJSON().items[1].value).toBe('b');
|
||||||
|
expect(obj.toJSON().items[0].count).toBe(20);
|
||||||
|
expect(obj.toJSON().items[1].count).toBe(5);
|
||||||
|
const query = new Parse.Query(TestObject);
|
||||||
|
const result = await query.get(obj.id);
|
||||||
|
expect(result.get('items')[0].value).toBe('a');
|
||||||
|
expect(result.get('items')[1].value).toBe('b');
|
||||||
|
expect(result.get('items')[0].count).toBe(20);
|
||||||
|
expect(result.get('items')[1].count).toBe(5);
|
||||||
|
expect(result.get('items')).toEqual(obj.get('items'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it_only_db('mongo')('can increment array nested fields missing index', async () => {
|
||||||
|
const obj = new TestObject();
|
||||||
|
obj.set('items', []);
|
||||||
|
await obj.save();
|
||||||
|
obj.increment('items.1.count', 15);
|
||||||
|
await obj.save();
|
||||||
|
expect(obj.toJSON().items[0]).toBe(null);
|
||||||
|
expect(obj.toJSON().items[1].count).toBe(15);
|
||||||
|
const query = new Parse.Query(TestObject);
|
||||||
|
const result = await query.get(obj.id);
|
||||||
|
expect(result.get('items')[0]).toBe(null);
|
||||||
|
expect(result.get('items')[1].count).toBe(15);
|
||||||
|
expect(result.get('items')).toEqual(obj.get('items'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can query array nested fields', async () => {
|
||||||
|
const objects = [];
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
const obj = new TestObject();
|
||||||
|
obj.set('items', [i, { value: i }]);
|
||||||
|
objects.push(obj);
|
||||||
|
}
|
||||||
|
await Parse.Object.saveAll(objects);
|
||||||
|
let query = new Parse.Query(TestObject);
|
||||||
|
query.greaterThan('items.1.value', 5);
|
||||||
|
let result = await query.find();
|
||||||
|
expect(result.length).toBe(4);
|
||||||
|
|
||||||
|
query = new Parse.Query(TestObject);
|
||||||
|
query.lessThan('items.0', 3);
|
||||||
|
result = await query.find();
|
||||||
|
expect(result.length).toBe(3);
|
||||||
|
|
||||||
|
query = new Parse.Query(TestObject);
|
||||||
|
query.equalTo('items.0', 5);
|
||||||
|
result = await query.find();
|
||||||
|
expect(result.length).toBe(1);
|
||||||
|
|
||||||
|
query = new Parse.Query(TestObject);
|
||||||
|
query.notEqualTo('items.0', 5);
|
||||||
|
result = await query.find();
|
||||||
|
expect(result.length).toBe(9);
|
||||||
|
});
|
||||||
|
|
||||||
it('addUnique with object', function (done) {
|
it('addUnique with object', function (done) {
|
||||||
const x1 = new Parse.Object('X');
|
const x1 = new Parse.Object('X');
|
||||||
x1.set('stuff', [1, { hello: 'world' }, { foo: 'bar' }]);
|
x1.set('stuff', [1, { hello: 'world' }, { foo: 'bar' }]);
|
||||||
|
|||||||
@@ -175,6 +175,8 @@ const toPostgresSchema = schema => {
|
|||||||
return schema;
|
return schema;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isArrayIndex = (arrayIndex) => Array.from(arrayIndex).every(c => c >= '0' && c <= '9');
|
||||||
|
|
||||||
const handleDotFields = object => {
|
const handleDotFields = object => {
|
||||||
Object.keys(object).forEach(fieldName => {
|
Object.keys(object).forEach(fieldName => {
|
||||||
if (fieldName.indexOf('.') > -1) {
|
if (fieldName.indexOf('.') > -1) {
|
||||||
@@ -207,7 +209,11 @@ const transformDotFieldToComponents = fieldName => {
|
|||||||
if (index === 0) {
|
if (index === 0) {
|
||||||
return `"${cmpt}"`;
|
return `"${cmpt}"`;
|
||||||
}
|
}
|
||||||
return `'${cmpt}'`;
|
if (isArrayIndex(cmpt)) {
|
||||||
|
return Number(cmpt);
|
||||||
|
} else {
|
||||||
|
return `'${cmpt}'`;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1851,6 +1851,14 @@ class DatabaseController {
|
|||||||
// only valid ops that produce an actionable result
|
// only valid ops that produce an actionable result
|
||||||
// the op may have happened on a keypath
|
// the op may have happened on a keypath
|
||||||
this._expandResultOnKeyPath(response, key, result);
|
this._expandResultOnKeyPath(response, key, result);
|
||||||
|
// Revert array to object conversion on dot notation for arrays (e.g. "field.0.key")
|
||||||
|
if (key.includes('.')) {
|
||||||
|
const [field, index] = key.split('.');
|
||||||
|
const isArrayIndex = Array.from(index).every(c => c >= '0' && c <= '9');
|
||||||
|
if (isArrayIndex && Array.isArray(result[field]) && !Array.isArray(response[field])) {
|
||||||
|
response[field] = result[field];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return Promise.resolve(response);
|
return Promise.resolve(response);
|
||||||
|
|||||||
@@ -1096,9 +1096,17 @@ export default class SchemaController {
|
|||||||
maintenance?: boolean
|
maintenance?: boolean
|
||||||
) {
|
) {
|
||||||
if (fieldName.indexOf('.') > 0) {
|
if (fieldName.indexOf('.') > 0) {
|
||||||
// subdocument key (x.y) => ok if x is of type 'object'
|
// "<array>.<index>" for Nested Arrays
|
||||||
fieldName = fieldName.split('.')[0];
|
// "<embedded document>.<field>" for Nested Objects
|
||||||
type = 'Object';
|
// JSON Arrays are treated as Nested Objects
|
||||||
|
const [x, y] = fieldName.split('.');
|
||||||
|
fieldName = x;
|
||||||
|
const isArrayIndex = Array.from(y).every(c => c >= '0' && c <= '9');
|
||||||
|
if (isArrayIndex && !['sentPerUTCOffset', 'failedPerUTCOffset'].includes(fieldName)) {
|
||||||
|
type = 'Array';
|
||||||
|
} else {
|
||||||
|
type = 'Object';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let fieldNameToValidate = `${fieldName}`;
|
let fieldNameToValidate = `${fieldName}`;
|
||||||
if (maintenance && fieldNameToValidate.charAt(0) === '_') {
|
if (maintenance && fieldNameToValidate.charAt(0) === '_') {
|
||||||
|
|||||||
Reference in New Issue
Block a user