Skip to content

Commit af6e878

Browse files
committed
fix for projects, organizations segments table
1 parent 374ea3d commit af6e878

10 files changed

Lines changed: 256 additions & 99 deletions

File tree

backend/src/database/migrations/V1683110675__segments.sql

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,19 @@ ALTER TABLE "reports" ADD COLUMN "segmentId" uuid;
3737

3838
ALTER TABLE "widgets" ADD COLUMN "segmentId" uuid;
3939

40+
ALTER TABLE "memberToMerge" ADD COLUMN "segmentId" uuid;
41+
42+
ALTER TABLE "memberToMerge" DROP CONSTRAINT "memberToMerge_pkey";
43+
44+
ALTER TABLE public."memberToMerge" ADD CONSTRAINT "memberToMerge_pkey" PRIMARY KEY ("memberId", "toMergeId", "segmentId");
45+
46+
47+
ALTER TABLE "memberNoMerge" ADD COLUMN "segmentId" uuid;
48+
49+
ALTER TABLE "memberNoMerge" DROP CONSTRAINT "memberNoMerge_pkey";
50+
51+
ALTER TABLE public."memberNoMerge" ADD CONSTRAINT "memberNoMerge_pkey" PRIMARY KEY ("memberId", "noMergeId", "segmentId");
52+
4053

4154
CREATE TABLE public."memberSegments" (
4255
"memberId" uuid NOT NULL,
@@ -50,4 +63,20 @@ CREATE TABLE public."memberSegments" (
5063
-- create index for memberId, segmentId and tenantId
5164
);
5265

66+
CREATE TABLE public."organizationSegments" (
67+
"organizationId" uuid NOT NULL,
68+
"segmentId" uuid NOT NULL,
69+
"tenantId" uuid NOT NULL,
70+
"createdAt" timestamp with time zone NOT NULL,
71+
foreign key ("tenantId") references tenants (id),
72+
foreign key ("segmentId") references segments (id),
73+
foreign key ("organizationId") references organizations (id),
74+
unique("organizationId", "segmentId", "tenantId")
75+
-- create index for organizationId, segmentId and tenantId
76+
);
77+
78+
79+
80+
81+
5382

backend/src/database/models/member.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ export default (sequelize) => {
104104
models.member.belongsToMany(models.segment, {
105105
as: 'segments',
106106
through: 'memberSegments',
107+
timestamps: false,
107108
})
108109

109110
models.member.hasOne(models.memberActivityAggregatesMV, {

backend/src/database/models/organization.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,12 @@ export default (sequelize) => {
126126
foreignKey: 'organizationId',
127127
})
128128

129+
models.organization.belongsToMany(models.segment, {
130+
as: 'segments',
131+
through: 'organizationSegments',
132+
timestamps: false,
133+
})
134+
129135
models.organization.belongsTo(models.tenant, {
130136
as: 'tenant',
131137
foreignKey: {

backend/src/database/models/segment.ts

Lines changed: 2 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -46,57 +46,6 @@ export default (sequelize) => {
4646
sourceParentId: {
4747
type: DataTypes.TEXT,
4848
},
49-
50-
platform: {
51-
type: DataTypes.TEXT,
52-
allowNull: false,
53-
validate: {
54-
notEmpty: true,
55-
},
56-
},
57-
isContribution: {
58-
type: DataTypes.BOOLEAN,
59-
allowNull: false,
60-
defaultValue: false,
61-
},
62-
score: {
63-
type: DataTypes.INTEGER,
64-
defaultValue: 2,
65-
},
66-
username: {
67-
type: DataTypes.TEXT,
68-
},
69-
objectMemberUsername: {
70-
type: DataTypes.TEXT,
71-
},
72-
attributes: {
73-
type: DataTypes.JSONB,
74-
allowNull: false,
75-
defaultValue: {},
76-
},
77-
channel: {
78-
type: DataTypes.TEXT,
79-
},
80-
body: {
81-
type: DataTypes.TEXT,
82-
},
83-
title: {
84-
type: DataTypes.TEXT,
85-
},
86-
url: {
87-
type: DataTypes.TEXT,
88-
},
89-
sentiment: {
90-
type: DataTypes.JSONB,
91-
defaultValue: {},
92-
},
93-
importHash: {
94-
type: DataTypes.STRING(255),
95-
allowNull: true,
96-
validate: {
97-
len: [0, 255],
98-
},
99-
},
10049
},
10150
{
10251
indexes: [
@@ -105,8 +54,8 @@ export default (sequelize) => {
10554
fields: ['slug', 'parentSlug', 'grandparentSlug', 'tenantId'],
10655
},
10756
],
108-
timestamps: true,
109-
paranoid: true,
57+
timestamps: false,
58+
paranoid: false,
11059
},
11160
)
11261

backend/src/database/repositories/activityRepository.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,15 @@ class ActivityRepository {
562562
to: 'tagId',
563563
},
564564
},
565+
segments: {
566+
table: 'members',
567+
model: 'member',
568+
relationTable: {
569+
name: 'memberSegments',
570+
from: 'memberId',
571+
to: 'segmentId',
572+
},
573+
},
565574
organizations: {
566575
table: 'members',
567576
model: 'member',

backend/src/database/repositories/filters/queryParser.ts

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -401,13 +401,37 @@ class QueryParser {
401401
// eslint-disable-next-line prefer-const
402402
let { filter, orderBy, limit, offset, include, fields } = query
403403

404-
const dbQuery: any = {
405-
where: {
406-
tenantId: SequelizeRepository.getCurrentTenant(this.options).id,
407-
segmentId: this.options.currentSegments.map((s) => s.id),
408-
},
409-
limit: QueryParser.defaultPageSize,
410-
offset: 0,
404+
let dbQuery: any
405+
406+
if (this.manyToMany.segments) {
407+
const segmentsQuery = this.replaceWithManyToMany(
408+
{
409+
segments: this.options.currentSegments.map((s) => s.id),
410+
},
411+
'segments',
412+
)
413+
414+
dbQuery = {
415+
where: {
416+
[Op.and]: [
417+
{
418+
tenantId: SequelizeRepository.getCurrentTenant(this.options).id,
419+
},
420+
segmentsQuery,
421+
],
422+
},
423+
limit: QueryParser.defaultPageSize,
424+
offset: 0,
425+
}
426+
} else {
427+
dbQuery = {
428+
where: {
429+
tenantId: SequelizeRepository.getCurrentTenant(this.options).id,
430+
segmentId: this.options.currentSegments.map((s) => s.id),
431+
},
432+
limit: QueryParser.defaultPageSize,
433+
offset: 0,
434+
}
411435
}
412436

413437
if (fields) {

backend/src/database/repositories/memberRepository.ts

Lines changed: 85 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import {
2626
} from './types/memberTypes'
2727
import { ActivityDisplayVariant } from '../../types/activityTypes'
2828
import SegmentRepository from './segmentRepository'
29-
import { SegmentData } from '../../types/segmentTypes'
3029

3130
const { Op } = Sequelize
3231

@@ -162,6 +161,25 @@ class MemberRepository {
162161
})
163162
}
164163

164+
static async excludeMemberFromSegments(memberId: string, options: IRepositoryOptions) {
165+
const seq = SequelizeRepository.getSequelize(options)
166+
167+
const transaction = SequelizeRepository.getTransaction(options)
168+
169+
const bulkDeleteMemberSegments = `DELETE FROM "memberSegments" WHERE "memberId" = :memberId and "segmentId" in (:segmentIds);`
170+
171+
await seq.query(bulkDeleteMemberSegments, {
172+
replacements: {
173+
memberId,
174+
segmentIds: options.currentSegments.map((s) => s.id),
175+
},
176+
type: QueryTypes.DELETE,
177+
transaction,
178+
})
179+
180+
return this.findById(memberId, options, true, false)
181+
}
182+
165183
static async findSampleDataMemberIds(options: IRepositoryOptions) {
166184
const transaction = SequelizeRepository.getTransaction(options)
167185
const currentTenant = SequelizeRepository.getCurrentTenant(options)
@@ -432,7 +450,13 @@ class MemberRepository {
432450
const currentTenant = SequelizeRepository.getCurrentTenant(options)
433451

434452
const query = `
435-
with identities as (select mi."memberId",
453+
with segment_ids as (
454+
select "memberId", array_agg("segmentId") as "segmentIds" from
455+
"memberSegments"
456+
where "tenantId" = :tenantId
457+
group by "memberId"
458+
),
459+
identities as (select mi."memberId",
436460
array_agg(distinct mi.platform) as identities,
437461
jsonb_object_agg(mi.platform, mi.usernames) as username
438462
from (select "memberId",
@@ -464,10 +488,12 @@ class MemberRepository {
464488
m."tenantId",
465489
m."createdById",
466490
m."updatedById",
467-
i.username
491+
i.username,
492+
si."segmentIds" as segments
468493
from members m
469494
inner join "memberIdentities" mi on m.id = mi."memberId"
470495
inner join identities i on i."memberId" = m.id
496+
inner join segment_ids si on si."memberId" = m.id
471497
where mi."tenantId" = :tenantId
472498
and mi.platform = :platform
473499
and mi.username in (:usernames)
@@ -515,7 +541,6 @@ class MemberRepository {
515541
where: {
516542
id,
517543
tenantId: currentTenant.id,
518-
segmentId: options.currentSegments.map((s) => s.id),
519544
},
520545
transaction,
521546
})
@@ -587,6 +612,11 @@ class MemberRepository {
587612
transaction,
588613
})
589614
}
615+
616+
if (data.segments) {
617+
await MemberRepository.includeMemberToSegments(record.id, { ...options, transaction })
618+
}
619+
590620
const seq = SequelizeRepository.getSequelize(options)
591621

592622
if (data.username) {
@@ -664,25 +694,30 @@ class MemberRepository {
664694

665695
const currentTenant = SequelizeRepository.getCurrentTenant(options)
666696

667-
const record = await options.database.member.findOne({
668-
where: {
669-
id,
670-
tenantId: currentTenant.id,
671-
segmentId: options.currentSegments.map((s) => s.id),
672-
},
673-
transaction,
674-
})
697+
const member = await MemberRepository.excludeMemberFromSegments(id, { ...options, transaction })
675698

676-
if (!record) {
677-
throw new Error404()
678-
}
699+
// if member doesn't belong to any other segment anymore, remove it
679700

680-
await record.destroy({
681-
force,
682-
transaction,
683-
})
701+
if (member.segments.length === 0) {
702+
const record = await options.database.member.findOne({
703+
where: {
704+
id,
705+
tenantId: currentTenant.id,
706+
segmentId: options.currentSegments.map((s) => s.id),
707+
},
708+
transaction,
709+
})
684710

685-
await this._createAuditLog(AuditLogRepository.DELETE, record, record, options)
711+
if (!record) {
712+
throw new Error404()
713+
}
714+
715+
await record.destroy({
716+
force,
717+
transaction,
718+
})
719+
await this._createAuditLog(AuditLogRepository.DELETE, record, record, options)
720+
}
686721
}
687722

688723
static async destroyBulk(ids, options: IRepositoryOptions, force = false) {
@@ -701,6 +736,7 @@ class MemberRepository {
701736
})
702737
}
703738

739+
/*
704740
static async getSegments(
705741
memberIds: string[],
706742
options: IRepositoryOptions,
@@ -714,18 +750,37 @@ class MemberRepository {
714750
select "memberId", "segmentId", "tenantId", "createdAt" from "memberSegments" where "memberId" in (:memberIds) order by "createdAt" asc;
715751
`
716752
717-
await seq.query(query, {
753+
const data = await seq.query(query, {
718754
replacements: {
719755
memberIds,
720756
},
721757
type: QueryTypes.SELECT,
722758
transaction,
723759
})
724760
761+
for (const id of memberIds) {
762+
results.set(id, [])
763+
}
764+
765+
for (const res of data as any[]) {
766+
const { memberId, segmentId, username, sourceId, integrationId, createdAt } = res
767+
const identities = results.get(memberId)
768+
769+
identities.push({
770+
platform,
771+
username,
772+
sourceId,
773+
integrationId,
774+
createdAt,
775+
})
776+
}
777+
778+
725779
// TODO: complete
726780
727781
return results
728782
}
783+
*/
729784

730785
static async getIdentities(
731786
memberIds: string[],
@@ -778,7 +833,15 @@ class MemberRepository {
778833
) {
779834
const transaction = SequelizeRepository.getTransaction(options)
780835

781-
const include = []
836+
const include = [
837+
{
838+
model: options.database.segment,
839+
as: 'segments',
840+
through: {
841+
attributes: [],
842+
},
843+
},
844+
]
782845

783846
const where: any = {
784847
id,
@@ -787,7 +850,6 @@ class MemberRepository {
787850
if (!ignoreTenant) {
788851
const currentTenant = SequelizeRepository.getCurrentTenant(options)
789852
where.tenantId = currentTenant.id
790-
where.segmentId = options.currentSegments.map((s) => s.id)
791853
}
792854

793855
const record = await options.database.member.findOne({

0 commit comments

Comments
 (0)