mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-20 03:12:15 +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', () => {
|
describe('upsertRows', () => {
|
||||||
it('updates a row if filter matches', async () => {
|
it('updates rows if filter matches', async () => {
|
||||||
// ARRANGE
|
// ARRANGE
|
||||||
const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, {
|
const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, {
|
||||||
name: 'dataStore',
|
name: 'dataStore',
|
||||||
columns: [
|
columns: [
|
||||||
{ name: 'pid', type: 'string' },
|
{ name: 'pid', type: 'string' },
|
||||||
{ name: 'fullName', type: 'string' },
|
{ name: 'name', type: 'string' },
|
||||||
{ name: 'age', type: 'number' },
|
{ name: 'age', type: 'number' },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
// Insert initial row
|
await dataStoreService.insertRows(dataStoreId, project1.id, [
|
||||||
const ids = await dataStoreService.insertRows(dataStoreId, project1.id, [
|
{ pid: '1995-111a', name: 'Alice', age: 30 },
|
||||||
{ pid: '1995-111a', fullName: 'Alice', age: 30 },
|
{ pid: '1994-222a', name: 'John', age: 31 },
|
||||||
|
{ pid: '1993-333a', name: 'Paul', age: 32 },
|
||||||
]);
|
]);
|
||||||
expect(ids).toEqual([1]);
|
|
||||||
|
|
||||||
// ACT
|
// ACT
|
||||||
const result = await dataStoreService.upsertRows(dataStoreId, project1.id, {
|
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'],
|
matchFields: ['pid'],
|
||||||
});
|
});
|
||||||
|
|
||||||
// ASSERT
|
// ASSERT
|
||||||
expect(result).toBe(true);
|
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(count).toEqual(3);
|
||||||
expect(data).toEqual([{ fullName: 'Alicia', age: 31, id: 1, pid: '1995-111a' }]);
|
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 () => {
|
it('inserts a row if filter does not match', async () => {
|
||||||
@@ -1256,32 +1314,36 @@ describe('dataStore', () => {
|
|||||||
name: 'dataStore',
|
name: 'dataStore',
|
||||||
columns: [
|
columns: [
|
||||||
{ name: 'pid', type: 'string' },
|
{ name: 'pid', type: 'string' },
|
||||||
{ name: 'fullName', type: 'string' },
|
{ name: 'name', type: 'string' },
|
||||||
{ name: 'age', type: 'number' },
|
{ name: 'age', type: 'number' },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
// Insert initial row
|
// Insert initial row
|
||||||
const ids = await dataStoreService.insertRows(dataStoreId, project1.id, [
|
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]);
|
expect(ids).toEqual([1]);
|
||||||
|
|
||||||
// ACT
|
// ACT
|
||||||
const result = await dataStoreService.upsertRows(dataStoreId, project1.id, {
|
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'],
|
matchFields: ['pid'],
|
||||||
});
|
});
|
||||||
|
|
||||||
// ASSERT
|
// ASSERT
|
||||||
expect(result).toBe(true);
|
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(count).toEqual(2);
|
||||||
expect(data).toEqual([
|
expect(data).toEqual([
|
||||||
{ fullName: 'Alice', age: 30, id: 1, pid: '1995-111a' },
|
{ name: 'Alice', age: 30, id: 1, pid: '1995-111a' },
|
||||||
{ fullName: 'Alice', age: 30, id: 2, pid: '1992-222b' },
|
{ name: 'Alice', age: 30, id: 2, pid: '1992-222b' },
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -270,29 +270,22 @@ export class DataStoreRowsRepository {
|
|||||||
matchFields: string[],
|
matchFields: string[],
|
||||||
rows: DataStoreRows,
|
rows: DataStoreRows,
|
||||||
): Promise<{ rowsToInsert: DataStoreRows; rowsToUpdate: DataStoreRows }> {
|
): Promise<{ rowsToInsert: DataStoreRows; rowsToUpdate: DataStoreRows }> {
|
||||||
const dbType = this.dataSource.options.type;
|
const queryBuilder = this.dataSource
|
||||||
const whereClauses: string[] = [];
|
.createQueryBuilder()
|
||||||
const params: unknown[] = [];
|
.select(matchFields)
|
||||||
|
.from(this.toTableName(dataStoreId), 'datastore');
|
||||||
|
|
||||||
for (const row of rows) {
|
rows.forEach((row, index) => {
|
||||||
const clause = matchFields
|
const matchData = Object.fromEntries(matchFields.map((field) => [field, row[field]]));
|
||||||
.map((field) => {
|
if (index === 0) {
|
||||||
params.push(row[field]);
|
queryBuilder.where(matchData);
|
||||||
return `${quoteIdentifier(field, dbType)} = ${getPlaceholder(params.length, dbType)}`;
|
} else {
|
||||||
})
|
queryBuilder.orWhere(matchData);
|
||||||
.join(' AND ');
|
|
||||||
whereClauses.push(`(${clause})`);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const quotedFields = matchFields.map((field) => quoteIdentifier(field, dbType)).join(', ');
|
const existing: Array<Record<string, DataStoreColumnJsType | null>> =
|
||||||
const quotedTableName = quoteIdentifier(this.toTableName(dataStoreId), dbType);
|
await queryBuilder.getRawMany();
|
||||||
|
|
||||||
const query = `
|
|
||||||
SELECT ${quotedFields}
|
|
||||||
FROM ${quotedTableName}
|
|
||||||
WHERE ${whereClauses.join(' OR ')}
|
|
||||||
`;
|
|
||||||
const existing: Array<Record<string, unknown>> = await this.dataSource.query(query, params);
|
|
||||||
|
|
||||||
return splitRowsByExistence(existing, matchFields, rows);
|
return splitRowsByExistence(existing, matchFields, rows);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user