Skip to content

Commit 8d16afc

Browse files
gsi-alejandroejscribner
authored andcommitted
fix: update merge doc behavior
1 parent 45f68c5 commit 8d16afc

8 files changed

Lines changed: 63 additions & 11 deletions

File tree

__test__/collection-operators.spec.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -296,12 +296,10 @@ describe('Collection operators', () => {
296296
})
297297
.limit(2)
298298
.build();
299-
const response2 = await ottoman.query(query);
300299
const response3 = await Airline.find({ $and: [{ country: { $in: 'United Kingdom' } }] });
301300
expect(response3.rows).toStrictEqual([]);
302301
expect(query).toBe(
303302
`SELECT country,icao,name FROM ${fromClause} WHERE type="airline" AND (country IN ["United Kingdom","France"]) AND callsign IS NOT NULL AND ANY description WITHIN ["EU"] SATISFIES icao LIKE "%"||description END LIMIT 2`,
304303
);
305-
expect(response1.rows).toStrictEqual(response2.rows);
306304
});
307305
});

__test__/model-crud.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,17 @@ describe('Test Document Access Functions', () => {
8080
expect(user.isActive).toBe(true);
8181
});
8282

83+
test('UserModel.update -> Update a document with array props', async () => {
84+
const addressSchema = new Schema({ city: String });
85+
const personSchema = new Schema({ address: [addressSchema] });
86+
const Model = model('Person', personSchema);
87+
await startInTest(getDefaultInstance());
88+
const result = await Model.create({ address: [{ city: 'xxx1' }, { city: 'xxx2' }] });
89+
await Model.updateById(result.id, { address: [{ city: 'xxx3' }] });
90+
const doc = await Model.findById(result.id);
91+
expect(doc.address?.length).toBe(1);
92+
});
93+
8394
test('UserModel.update -> Update a document, throw DocumentNotFound', async () => {
8495
const UserModel = model<IUser>('User', schema);
8596
await startInTest(getDefaultInstance());

__test__/query-array.spec.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { model, Schema, SearchConsistency, getDefaultInstance, set } from '../src';
1+
import { model, Schema, SearchConsistency, getDefaultInstance, set, Query } from '../src';
22
import { startInTest } from './testData';
33

44
describe('Query Builder Array', () => {
@@ -23,15 +23,28 @@ describe('Query Builder Array', () => {
2323
});
2424
await house.save();
2525

26+
const house2 = new House({
27+
title: 'City House',
28+
address: { line1: 'city house', state: 'FL' },
29+
numbers: [1, 5],
30+
});
31+
await house2.save();
32+
33+
const house3 = new House({
34+
title: 'Highway House',
35+
address: { line1: 'Highway house', state: 'FL' },
36+
numbers: [1, 5, 4000000],
37+
});
38+
await house3.save();
39+
2640
const filter = {
27-
title: 'Beach House',
2841
$any: {
2942
$expr: [{ search: { $in: 'numbers' } }],
30-
$satisfies: { search: 10 },
43+
$satisfies: { search: { $gte: 5, $lte: 15 } },
3144
},
3245
};
3346

34-
const results = await House.find(filter, { consistency: SearchConsistency.LOCAL });
47+
const results = await House.find(filter, { sort: { 'numbers[-1]': 'DESC' }, consistency: SearchConsistency.LOCAL });
3548
expect(results.rows.length).toBeGreaterThanOrEqual(1);
3649
});
3750
});

__test__/utils.spec.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { _keyGenerator, KEY_GENERATOR, MODEL_KEY } from '../src/utils/constants'
88
import { canBePopulated } from '../src/utils/populate/can-be-populated';
99
import { pathToN1QL, PathToN1QLItemType } from '../src/utils/path-to-n1ql';
1010
import { BadKeyGeneratorDelimiterError, PathN1qlError } from '../src/exceptions/ottoman-errors';
11+
import { mergeDoc } from '../src/utils/merge';
1112

1213
test('Build connection options from string', () => {
1314
const result = extractConnectionString(connectUri);
@@ -254,4 +255,20 @@ describe('nested object accessor', () => {
254255
const valueInPath = getValueByPath(doc, 'meta.l1.l2');
255256
expect(valueInPath).toBe('nested value');
256257
});
258+
259+
test('mergeDoc', () => {
260+
const result = mergeDoc(
261+
{ name: 'Jane', lastName: 'Smith', address: [{ city: 'xxx1' }, { city: 'xxx2' }] },
262+
{ address: [{ city: 'xxx3' }], lastName: 'Jhonson', age: 15 },
263+
);
264+
expect(result.name).toBe('Jane');
265+
expect(result.lastName).toBe('Jhonson');
266+
expect(result.age).toBe(15);
267+
expect(result.address.length).toBe(1);
268+
});
269+
270+
test('mergeDoc arrays', () => {
271+
const result = mergeDoc({ address: [{ city: 'xxx1' }, { city: 'xxx2' }] }, { address: [{ city: 'xxx3' }] });
272+
expect(result.address.length).toBe(1);
273+
});
257274
});

src/model/create-model.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { UpdateManyOptions } from './interfaces/update-many.interface';
2020
import { ModelTypes, saveOptions } from './model.types';
2121
import { getModelMetadata, getPopulated, setModelMetadata } from './utils/model.utils';
2222
import { IConditionExpr } from '../query';
23-
import _ from 'lodash';
23+
import { mergeDoc } from '../utils/merge';
2424

2525
/**
2626
* @ignore
@@ -247,7 +247,7 @@ export const _buildModel = (metadata: ModelMetadata) => {
247247
const value = await _Model.findById(key, { withExpiry: !!options.maxExpiry });
248248
if (value[ID_KEY]) {
249249
const strategy = CAST_STRATEGY.THROW;
250-
const obj = _.merge({}, value, data);
250+
const obj = mergeDoc(value, data);
251251
(value as Model)._applyData(obj, options.strict);
252252
const instance = new _Model({ ...value }, { strategy });
253253
const _options: any = {};

src/model/document.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { DocumentExistsError, DocumentNotFoundError } from 'couchbase';
2-
import _ from 'lodash';
32
import { ImmutableError } from '../exceptions/ottoman-errors';
43
import { validate } from '../schema';
54
import { ApplyStrategy, CAST_STRATEGY, CastOptions } from '../utils/cast-strategy';
@@ -16,6 +15,7 @@ import { getModelMetadata, getPopulated } from './utils/model.utils';
1615
import { removeLifeCycle } from './utils/remove-life-cycle';
1716
import { storeLifeCycle } from './utils/store-life-cycle';
1817
import { setValueByPath } from '../utils';
18+
import { mergeDoc } from '../utils/merge';
1919

2020
type CleanDocument = Omit<Document, '$' | '$isNew'>;
2121
export type IDocument<T = any> = CleanDocument & T;
@@ -187,7 +187,7 @@ export abstract class Document {
187187
}
188188
const modelKeyObj = {};
189189
setValueByPath(modelKeyObj, modelKey, modelName);
190-
const addedMetadata = _.merge({}, data, modelKeyObj);
190+
const addedMetadata = mergeDoc(data, modelKeyObj);
191191
const { document } = await storeLifeCycle({ key, id, data: addedMetadata, options: _options, metadata, refKeys });
192192
return this._applyData(document).$wasNew();
193193
}

src/query/helpers/builders.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ const _buildLetExpr = (letExpr?: LetExprType, clause = 'LET') => {
190190
const _buildOrderByExpr = (orderExpr: Record<string, SortType> | undefined) => {
191191
return !!orderExpr
192192
? ` ORDER BY ${Object.keys(orderExpr)
193-
.map((value: string) => `${escapeReservedWords(value)} ${orderExpr[value]}`)
193+
.map((value: string) => `${value.includes('[') ? value : escapeReservedWords(value)} ${orderExpr[value]}`)
194194
.join(',')}`
195195
: '';
196196
};

src/utils/merge.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import * as _ from 'lodash';
2+
3+
function customizer(objValue, srcValue) {
4+
if (_.isArray(objValue)) {
5+
return srcValue;
6+
}
7+
}
8+
9+
const mergeDoc = (dest, source) => {
10+
return _.mergeWith({}, dest, source, customizer);
11+
};
12+
13+
export { mergeDoc };

0 commit comments

Comments
 (0)