Skip to content

Commit 5527aea

Browse files
loicsaintrochUroš Marolt
andauthored
C-2161 - Optimize handling of segments activity channels (#1384)
Co-authored-by: Uroš Marolt <[email protected]>
1 parent 4fa3cc9 commit 5527aea

File tree

6 files changed

+183
-116
lines changed

6 files changed

+183
-116
lines changed

backend/package-lock.json

Lines changed: 26 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
DROP TABLE IF EXISTS "public"."segmentActivityChannels" CASCADE;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
CREATE TABLE IF NOT EXISTS "public"."segmentActivityChannels" (
2+
"id" UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
3+
"tenantId" UUID NOT NULL REFERENCES public.tenants(id) ON UPDATE CASCADE ON DELETE CASCADE,
4+
"segmentId" UUID NOT NULL REFERENCES public.segments(id) ON UPDATE CASCADE ON DELETE CASCADE,
5+
"platform" TEXT NOT NULL,
6+
"channel" TEXT NOT NULL
7+
);
8+
9+
CREATE UNIQUE INDEX unique_segment_activity_channel
10+
ON "public"."segmentActivityChannels" ("tenantId", "segmentId", "platform", "channel");
11+
12+
INSERT INTO "public"."segmentActivityChannels" ("segmentId", "tenantId", "platform", "channel")
13+
SELECT
14+
"id",
15+
"tenantId",
16+
jsonb_object_keys("activityChannels"),
17+
TRIM('"' FROM jsonb_array_elements("activityChannels"->jsonb_object_keys("activityChannels"))::TEXT)
18+
FROM "public"."segments";

backend/src/database/repositories/segmentRepository.ts

Lines changed: 129 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -182,39 +182,71 @@ class SegmentRepository extends RepositoryBase<
182182
'sourceId',
183183
'sourceParentId',
184184
'customActivityTypes',
185-
'activityChannels',
186185
].includes(key),
187186
)
188187

189-
let segmentUpdateQuery = `UPDATE segments SET `
190-
const replacements = {} as any
188+
if (updateFields.length > 0) {
189+
let segmentUpdateQuery = `UPDATE segments SET `
190+
const replacements = {} as any
191191

192-
for (const field of updateFields) {
193-
segmentUpdateQuery += ` "${field}" = :${field} `
194-
replacements[field] = data[field]
192+
for (const field of updateFields) {
193+
segmentUpdateQuery += ` "${field}" = :${field} `
194+
replacements[field] = data[field]
195195

196-
if (updateFields[updateFields.length - 1] !== field) {
197-
segmentUpdateQuery += ', '
196+
if (updateFields[updateFields.length - 1] !== field) {
197+
segmentUpdateQuery += ', '
198+
}
198199
}
199-
}
200200

201-
segmentUpdateQuery += ` WHERE id = :id and "tenantId" = :tenantId `
202-
replacements.tenantId = this.options.currentTenant.id
203-
replacements.id = id
201+
segmentUpdateQuery += ` WHERE id = :id and "tenantId" = :tenantId `
202+
replacements.tenantId = this.options.currentTenant.id
203+
replacements.id = id
204204

205-
if (replacements.customActivityTypes) {
206-
replacements.customActivityTypes = JSON.stringify(replacements.customActivityTypes)
207-
}
205+
if (replacements.customActivityTypes) {
206+
replacements.customActivityTypes = JSON.stringify(replacements.customActivityTypes)
207+
}
208208

209-
if (replacements.activityChannels) {
210-
replacements.activityChannels = JSON.stringify(replacements.activityChannels)
209+
await this.options.database.sequelize.query(segmentUpdateQuery, {
210+
replacements,
211+
type: QueryTypes.UPDATE,
212+
transaction,
213+
})
211214
}
212215

213-
await this.options.database.sequelize.query(segmentUpdateQuery, {
214-
replacements,
215-
type: QueryTypes.UPDATE,
216-
transaction,
217-
})
216+
if (data.activityChannels && typeof data.activityChannels === 'object') {
217+
if (Object.keys(data.activityChannels).length > 0) {
218+
const replacements = {}
219+
let valuePlaceholders = ''
220+
Object.keys(data.activityChannels).forEach((platform) => {
221+
data.activityChannels[platform].forEach((channel, i) => {
222+
valuePlaceholders += data.activityChannels[platform]
223+
.map(
224+
() =>
225+
`(:tenantId_${platform}_${i}, :segmentId_${platform}_${i}, :platform_${platform}_${i}, :channel_${platform}_${i})`,
226+
)
227+
.join(', ')
228+
229+
replacements[`tenantId_${platform}_${i}`] = this.options.currentTenant.id
230+
replacements[`segmentId_${platform}_${i}`] = id
231+
replacements[`platform_${platform}_${i}`] = platform
232+
replacements[`channel_${platform}_${i}`] = channel
233+
})
234+
})
235+
236+
await this.options.database.sequelize.query(
237+
`
238+
INSERT INTO "segmentActivityChannels" ("tenantId", "segmentId", "platform", "channel")
239+
VALUES ${valuePlaceholders}
240+
ON CONFLICT DO NOTHING;
241+
`,
242+
{
243+
replacements,
244+
type: QueryTypes.INSERT,
245+
transaction,
246+
},
247+
)
248+
}
249+
}
218250

219251
return this.findById(id)
220252
}
@@ -298,11 +330,22 @@ class SegmentRepository extends RepositoryBase<
298330
const transaction = this.transaction
299331

300332
const records = await this.options.database.sequelize.query(
301-
`SELECT *
302-
FROM segments
303-
WHERE id in (:ids)
304-
and "tenantId" = :tenantId;
305-
`,
333+
`SELECT
334+
s.*,
335+
json_agg(sac."activityChannels") AS "activityChannels"
336+
FROM segments s
337+
LEFT JOIN (
338+
SELECT
339+
"tenantId",
340+
json_build_object(concat(platform), json_agg(sac.channel)) AS "activityChannels"
341+
FROM "segmentActivityChannels" sac
342+
WHERE "tenantId" = :tenantId
343+
GROUP BY "tenantId", "platform"
344+
) sac
345+
ON sac."tenantId" = s."tenantId"
346+
WHERE id in (:ids)
347+
AND s."tenantId" = :tenantId
348+
GROUP BY s.id;`,
306349
{
307350
replacements: {
308351
ids,
@@ -313,6 +356,10 @@ class SegmentRepository extends RepositoryBase<
313356
},
314357
)
315358

359+
records.forEach((row) => {
360+
row.activityChannels = Object.assign({}, ...row.activityChannels)
361+
})
362+
316363
return records.map((sr) => SegmentRepository.populateRelations(sr))
317364
}
318365

@@ -333,11 +380,22 @@ class SegmentRepository extends RepositoryBase<
333380
const transaction = this.transaction
334381

335382
const records = await this.options.database.sequelize.query(
336-
`SELECT *
337-
FROM segments
338-
WHERE id = :id
339-
and "tenantId" = :tenantId;
340-
`,
383+
`SELECT
384+
s.*,
385+
json_agg(sac."activityChannels") AS "activityChannels"
386+
FROM segments s
387+
LEFT JOIN (
388+
SELECT
389+
"tenantId",
390+
json_build_object(concat(platform), json_agg(sac.channel)) AS "activityChannels"
391+
FROM "segmentActivityChannels" sac
392+
WHERE "tenantId" = :tenantId
393+
GROUP BY "tenantId", "platform"
394+
) sac
395+
ON sac."tenantId" = s."tenantId"
396+
WHERE s.id = :id
397+
AND s."tenantId" = :tenantId
398+
GROUP BY s.id;`,
341399
{
342400
replacements: {
343401
id,
@@ -353,6 +411,7 @@ class SegmentRepository extends RepositoryBase<
353411
}
354412

355413
const record = records[0]
414+
record.activityChannels = Object.assign({}, ...record.activityChannels)
356415

357416
if (SegmentRepository.isProjectGroup(record)) {
358417
// find projects
@@ -569,17 +628,27 @@ class SegmentRepository extends RepositoryBase<
569628

570629
const subprojects = await this.options.database.sequelize.query(
571630
`
572-
select s.*,
573-
count(*) over () as "totalCount"
574-
from segments s
575-
where s."grandparentSlug" is not null
576-
and s."parentSlug" is not null
577-
and s."tenantId" = :tenantId
578-
${searchQuery}
579-
GROUP BY s."id"
580-
ORDER BY s."name"
581-
${this.getPaginationString(criteria)};
582-
`,
631+
SELECT
632+
COUNT(DISTINCT s.id) AS count,
633+
s.*,
634+
json_agg(sac."activityChannels") AS "activityChannels"
635+
FROM segments s
636+
LEFT JOIN (
637+
SELECT
638+
"tenantId",
639+
json_build_object(concat(platform), json_agg(sac.channel)) AS "activityChannels"
640+
FROM "segmentActivityChannels" sac
641+
WHERE "tenantId" = :tenantId
642+
GROUP BY "tenantId", "platform"
643+
) sac
644+
ON sac."tenantId" = s."tenantId"
645+
WHERE s."grandparentSlug" IS NOT NULL
646+
AND s."parentSlug" IS NOT NULL
647+
AND s."tenantId" = :tenantId
648+
${searchQuery}
649+
GROUP BY s.id
650+
${this.getPaginationString(criteria)};
651+
`,
583652
{
584653
replacements: {
585654
tenantId: this.currentTenant.id,
@@ -592,15 +661,14 @@ class SegmentRepository extends RepositoryBase<
592661
},
593662
)
594663

595-
const count = subprojects.length > 0 ? Number.parseInt(subprojects[0].totalCount, 10) : 0
664+
const count = subprojects.length > 0 ? Number.parseInt(subprojects[0].count, 10) : 0
596665

597-
const integrationsBySegments = await this.queryIntegrationsForSubprojects(subprojects)
598-
599-
const rows = subprojects.map((i) => {
600-
let subproject = removeFieldsFromObject(i, 'totalCount')
601-
subproject = SegmentRepository.populateRelations(subproject)
602-
subproject.integrations = integrationsBySegments[subproject.id] || []
603-
return subproject
666+
const rows = subprojects
667+
rows.forEach((row, i) => {
668+
rows[i].activityChannels = rows[i].activityChannels[0]
669+
if (rows[i].activityChannels === null) {
670+
rows[i].activityChannels = {}
671+
}
604672
})
605673

606674
// TODO: Add member count to segments after implementing member relations
@@ -657,14 +725,17 @@ class SegmentRepository extends RepositoryBase<
657725

658726
static getActivityChannels(options: IRepositoryOptions) {
659727
const channels = {}
728+
660729
for (const segment of options.currentSegments) {
661-
for (const platform of Object.keys(segment.activityChannels)) {
662-
if (!channels[platform]) {
663-
channels[platform] = new Set<string>(segment.activityChannels[platform])
664-
} else {
665-
segment.activityChannels[platform].forEach((ch) =>
666-
(channels[platform] as Set<string>).add(ch),
667-
)
730+
if (segment.activityChannels) {
731+
for (const platform of Object.keys(segment.activityChannels)) {
732+
if (!channels[platform]) {
733+
channels[platform] = new Set<string>(segment.activityChannels[platform])
734+
} else {
735+
segment.activityChannels[platform].forEach((ch) =>
736+
(channels[platform] as Set<string>).add(ch),
737+
)
738+
}
668739
}
669740
}
670741
}

0 commit comments

Comments
 (0)