mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
feat(core): Use query builder for upsert fetch and split rows (no-changelog) (#18659)
This commit is contained in:
@@ -1218,36 +1218,94 @@ describe('dataStore', () => {
|
||||
});
|
||||
|
||||
describe('upsertRows', () => {
|
||||
it('updates a row if filter matches', async () => {
|
||||
it('updates rows if filter matches', async () => {
|
||||
// ARRANGE
|
||||
const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, {
|
||||
name: 'dataStore',
|
||||
columns: [
|
||||
{ name: 'pid', type: 'string' },
|
||||
{ name: 'fullName', type: 'string' },
|
||||
{ name: 'name', type: 'string' },
|
||||
{ name: 'age', type: 'number' },
|
||||
],
|
||||
});
|
||||
|
||||
// Insert initial row
|
||||
const ids = await dataStoreService.insertRows(dataStoreId, project1.id, [
|
||||
{ pid: '1995-111a', fullName: 'Alice', age: 30 },
|
||||
await dataStoreService.insertRows(dataStoreId, project1.id, [
|
||||
{ pid: '1995-111a', name: 'Alice', age: 30 },
|
||||
{ pid: '1994-222a', name: 'John', age: 31 },
|
||||
{ pid: '1993-333a', name: 'Paul', age: 32 },
|
||||
]);
|
||||
expect(ids).toEqual([1]);
|
||||
|
||||
// ACT
|
||||
const result = await dataStoreService.upsertRows(dataStoreId, project1.id, {
|
||||
rows: [{ pid: '1995-111a', fullName: 'Alicia', age: 31 }],
|
||||
rows: [
|
||||
{ pid: '1995-111a', name: 'Alicia', age: 31 },
|
||||
{ pid: '1994-222a', name: 'John', age: 32 },
|
||||
],
|
||||
matchFields: ['pid'],
|
||||
});
|
||||
|
||||
// ASSERT
|
||||
expect(result).toBe(true);
|
||||
|
||||
const { count, data } = await dataStoreRowsRepository.getManyAndCount(dataStoreId, {});
|
||||
const { count, data } = await dataStoreService.getManyRowsAndCount(
|
||||
dataStoreId,
|
||||
project1.id,
|
||||
{},
|
||||
);
|
||||
|
||||
expect(count).toEqual(1);
|
||||
expect(data).toEqual([{ fullName: 'Alicia', age: 31, id: 1, pid: '1995-111a' }]);
|
||||
expect(count).toEqual(3);
|
||||
expect(data).toEqual(
|
||||
expect.arrayContaining([
|
||||
{ pid: '1995-111a', name: 'Alicia', age: 31, id: 1 },
|
||||
{ pid: '1994-222a', name: 'John', age: 32, id: 2 },
|
||||
{ pid: '1993-333a', name: 'Paul', age: 32, id: 3 }, // unchanged
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it('works correctly with multiple filters', async () => {
|
||||
// ARRANGE
|
||||
const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, {
|
||||
name: 'dataStore',
|
||||
columns: [
|
||||
{ name: 'city', type: 'string' },
|
||||
{ name: 'age', type: 'number' },
|
||||
{ name: 'isEligible', type: 'boolean' },
|
||||
],
|
||||
});
|
||||
|
||||
await dataStoreService.insertRows(dataStoreId, project1.id, [
|
||||
{ city: 'Berlin', age: 30, isEligible: false },
|
||||
{ city: 'Amsterdam', age: 32, isEligible: false },
|
||||
{ city: 'Oslo', age: 28, isEligible: false },
|
||||
]);
|
||||
|
||||
// ACT
|
||||
const result = await dataStoreService.upsertRows(dataStoreId, project1.id, {
|
||||
rows: [
|
||||
{ city: 'Berlin', age: 30, isEligible: true },
|
||||
{ city: 'Amsterdam', age: 32, isEligible: true },
|
||||
],
|
||||
matchFields: ['age', 'city'],
|
||||
});
|
||||
|
||||
// ASSERT
|
||||
expect(result).toBe(true);
|
||||
|
||||
const { count, data } = await dataStoreService.getManyRowsAndCount(
|
||||
dataStoreId,
|
||||
project1.id,
|
||||
{},
|
||||
);
|
||||
|
||||
expect(count).toEqual(3);
|
||||
expect(data).toEqual(
|
||||
expect.arrayContaining([
|
||||
{ city: 'Berlin', age: 30, isEligible: true, id: 1 },
|
||||
{ city: 'Amsterdam', age: 32, isEligible: true, id: 2 },
|
||||
{ city: 'Oslo', age: 28, isEligible: false, id: 3 },
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it('inserts a row if filter does not match', async () => {
|
||||
@@ -1256,32 +1314,36 @@ describe('dataStore', () => {
|
||||
name: 'dataStore',
|
||||
columns: [
|
||||
{ name: 'pid', type: 'string' },
|
||||
{ name: 'fullName', type: 'string' },
|
||||
{ name: 'name', type: 'string' },
|
||||
{ name: 'age', type: 'number' },
|
||||
],
|
||||
});
|
||||
|
||||
// Insert initial row
|
||||
const ids = await dataStoreService.insertRows(dataStoreId, project1.id, [
|
||||
{ pid: '1995-111a', fullName: 'Alice', age: 30 },
|
||||
{ pid: '1995-111a', name: 'Alice', age: 30 },
|
||||
]);
|
||||
expect(ids).toEqual([1]);
|
||||
|
||||
// ACT
|
||||
const result = await dataStoreService.upsertRows(dataStoreId, project1.id, {
|
||||
rows: [{ pid: '1992-222b', fullName: 'Alice', age: 30 }],
|
||||
rows: [{ pid: '1992-222b', name: 'Alice', age: 30 }],
|
||||
matchFields: ['pid'],
|
||||
});
|
||||
|
||||
// ASSERT
|
||||
expect(result).toBe(true);
|
||||
|
||||
const { count, data } = await dataStoreRowsRepository.getManyAndCount(dataStoreId, {});
|
||||
const { count, data } = await dataStoreService.getManyRowsAndCount(
|
||||
dataStoreId,
|
||||
project1.id,
|
||||
{},
|
||||
);
|
||||
|
||||
expect(count).toEqual(2);
|
||||
expect(data).toEqual([
|
||||
{ fullName: 'Alice', age: 30, id: 1, pid: '1995-111a' },
|
||||
{ fullName: 'Alice', age: 30, id: 2, pid: '1992-222b' },
|
||||
{ name: 'Alice', age: 30, id: 1, pid: '1995-111a' },
|
||||
{ name: 'Alice', age: 30, id: 2, pid: '1992-222b' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -270,29 +270,22 @@ export class DataStoreRowsRepository {
|
||||
matchFields: string[],
|
||||
rows: DataStoreRows,
|
||||
): Promise<{ rowsToInsert: DataStoreRows; rowsToUpdate: DataStoreRows }> {
|
||||
const dbType = this.dataSource.options.type;
|
||||
const whereClauses: string[] = [];
|
||||
const params: unknown[] = [];
|
||||
const queryBuilder = this.dataSource
|
||||
.createQueryBuilder()
|
||||
.select(matchFields)
|
||||
.from(this.toTableName(dataStoreId), 'datastore');
|
||||
|
||||
for (const row of rows) {
|
||||
const clause = matchFields
|
||||
.map((field) => {
|
||||
params.push(row[field]);
|
||||
return `${quoteIdentifier(field, dbType)} = ${getPlaceholder(params.length, dbType)}`;
|
||||
})
|
||||
.join(' AND ');
|
||||
whereClauses.push(`(${clause})`);
|
||||
}
|
||||
rows.forEach((row, index) => {
|
||||
const matchData = Object.fromEntries(matchFields.map((field) => [field, row[field]]));
|
||||
if (index === 0) {
|
||||
queryBuilder.where(matchData);
|
||||
} else {
|
||||
queryBuilder.orWhere(matchData);
|
||||
}
|
||||
});
|
||||
|
||||
const quotedFields = matchFields.map((field) => quoteIdentifier(field, dbType)).join(', ');
|
||||
const quotedTableName = quoteIdentifier(this.toTableName(dataStoreId), dbType);
|
||||
|
||||
const query = `
|
||||
SELECT ${quotedFields}
|
||||
FROM ${quotedTableName}
|
||||
WHERE ${whereClauses.join(' OR ')}
|
||||
`;
|
||||
const existing: Array<Record<string, unknown>> = await this.dataSource.query(query, params);
|
||||
const existing: Array<Record<string, DataStoreColumnJsType | null>> =
|
||||
await queryBuilder.getRawMany();
|
||||
|
||||
return splitRowsByExistence(existing, matchFields, rows);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user