Skip to content

Commit

Permalink
⚡️ Optimisation de updateProjection
Browse files Browse the repository at this point in the history
  • Loading branch information
benjlevesque committed Sep 27, 2024
1 parent 33b63e8 commit ea6e574
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { describe, test } from 'node:test';

import { expect } from 'chai';

import { Entity } from '@potentiel-domain/entity';

import { prepareUpdateProjectionQuery } from './updateProjection';

type TestEntity = Entity<
'test',
{
foo: string;
bar: number;
baz: boolean;
}
>;

describe('updateProjection', () => {
test(`single string key`, () => {
const [query, values] = prepareUpdateProjectionQuery<TestEntity>({ foo: 'hello' });
expect(query).to.eq(
"update domain_views.projection set value=jsonb_set(value,'{foo}',$2) where key = $1",
);
expect(values).to.deep.eq(['"hello"']);
});

test(`single number key`, () => {
const [query, values] = prepareUpdateProjectionQuery<TestEntity>({ bar: 1 });
expect(query).to.eq(
"update domain_views.projection set value=jsonb_set(value,'{bar}',$2) where key = $1",
);
expect(values).to.deep.eq([1]);
});

test(`single boolean key`, () => {
const [query, values] = prepareUpdateProjectionQuery<TestEntity>({ baz: true });
expect(query).to.eq(
"update domain_views.projection set value=jsonb_set(value,'{baz}',$2) where key = $1",
);
expect(values).to.deep.eq([true]);
});

test(`multiple keys`, () => {
const [query, values] = prepareUpdateProjectionQuery<TestEntity>({
foo: '',
bar: -1,
baz: false,
});
expect(query).to.eq(
"update domain_views.projection set value=jsonb_set(jsonb_set(jsonb_set(value,'{foo}',$2),'{bar}',$3),'{baz}',$4) where key = $1",
);
expect(values).to.deep.eq(['""', -1, false]);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,35 @@ import { Entity } from '@potentiel-domain/entity';
import { flatten } from '@potentiel-libraries/flat';
import { executeQuery } from '@potentiel-libraries/pg-helpers';

const updateQuery =
'update domain_views.projection set value=jsonb_set(value,$2,$3) where key = $1';
type AtLeastOne<T, U = { [K in keyof T]: Pick<T, K> }> = Partial<T> & U[keyof U];

/** */
export const updateProjection = async <TProjection extends Entity>(
id: `${TProjection['type']}|${string}`,
readModel: Partial<Omit<TProjection, 'type'>>,
readModel: AtLeastOne<Omit<TProjection, 'type'>>,
): Promise<void> => {
const flatReadModel = Object.entries(flatten(readModel) as Record<string, unknown>);
for (const [key, value] of flatReadModel) {
await executeQuery(
updateQuery,
id,
`{"${key}"}`,
typeof value === 'string' ? `"${value}"` : value,
);
}
const [updateQuery, values] = prepareUpdateProjectionQuery(readModel);
console.log(updateQuery, values);
await executeQuery(updateQuery, id, ...values);
};

export const prepareUpdateProjectionQuery = <TProjection extends Entity>(
readModel: AtLeastOne<Omit<TProjection, 'type'>>,
): [string, Array<unknown>] => {
const flatReadModel = flatten(readModel) as Record<string, unknown>;
/*
The following generates a recursive update similar to this:
select jsonb_set(jsonb_set(data,'{field1}','"new_value1"'), '{field2}', '"new_value2"')
from (select '{"field1": "a", "field2":"b"}'::jsonb as data ) a
the `i+2` operation is due to `i` starting at 0, and $1 being the key
*/
const jsonb_set = Object.keys(flatReadModel).reduce(
(acc, curr, i) => `jsonb_set(${acc},'{${curr}}',$${i + 2})`,
'value',
);
const values = Object.values(flatReadModel).map((value) =>
typeof value === 'string' ? `"${value}"` : value,
);
return [`update domain_views.projection set value=${jsonb_set} where key = $1`, values];
};

0 comments on commit ea6e574

Please sign in to comment.