@@ -51,7 +51,11 @@ import {
5151} from '@crowd/data-access-layer/src/members/segments'
5252import { IDbMemberData } from '@crowd/data-access-layer/src/members/types'
5353import { optionsQx } from '@crowd/data-access-layer/src/queryExecutor'
54- import { fetchManySegments , getSegmentSubprojectIds } from '@crowd/data-access-layer/src/segments'
54+ import {
55+ fetchManySegments ,
56+ getSegmentMergeSuggestionCounts ,
57+ getSegmentSubprojectIds ,
58+ } from '@crowd/data-access-layer/src/segments'
5559import { ActivityDisplayService } from '@crowd/integrations'
5660import {
5761 ALL_PLATFORM_TYPES ,
@@ -63,6 +67,8 @@ import {
6367 MemberIdentityType ,
6468 MemberSegmentAffiliation ,
6569 MemberSegmentAffiliationJoined ,
70+ MergeActionState ,
71+ MergeActionType ,
6672 PlatformType ,
6773 SegmentType ,
6874 TemporalWorkflowId ,
@@ -277,13 +283,27 @@ class MemberRepository {
277283 AND EXISTS (
278284 SELECT 1 FROM "memberSegmentsAgg" ms2
279285 WHERE ms2."memberId" = mtm."toMergeId" AND ms2."segmentId" IN (:segmentIds)
286+ )
287+ AND NOT EXISTS (
288+ SELECT 1
289+ FROM "mergeActions" ma
290+ WHERE ma.type = :mergeActionType
291+ AND ma.state <> :mergeActionState
292+ AND (
293+ (ma."primaryId" = mtm."memberId" AND ma."secondaryId" = mtm."toMergeId")
294+ OR (ma."primaryId" = mtm."toMergeId" AND ma."secondaryId" = mtm."memberId")
295+ )
280296 )
281297 ${ memberFilter }
282298 ${ similarityFilter }
283299 ${ displayNameFilter }
284300 ` ,
285301 {
286- replacements,
302+ replacements : {
303+ ...replacements ,
304+ mergeActionType : MergeActionType . MEMBER ,
305+ mergeActionState : MergeActionState . ERROR ,
306+ } ,
287307 type : QueryTypes . SELECT ,
288308 } ,
289309 )
@@ -299,18 +319,16 @@ class MemberRepository {
299319 const MEDIUM_CONFIDENCE_LOWER_BOUND = 0.7
300320
301321 // Member segments are aggregated at each hierarchy level (group -> project -> subproject).
302- // Match the selected segment ID directly; do not expand to leaf subprojects.
303- const segmentIds = SequelizeRepository . getSegmentIds ( options )
304-
305- if ( segmentIds . length === 0 ) {
306- return args . countOnly
307- ? { count : '0' }
308- : {
309- rows : [ { members : [ ] , similarity : 0 } ] ,
310- count : 0 ,
311- limit : args . limit ,
312- offset : args . offset ,
313- }
322+ const projectGroupSegment = SequelizeRepository . getStrictlySingleProjectGroupSegment ( options )
323+
324+ let segmentIds : string [ ]
325+
326+ if ( args . filter ?. projectIds ?. length ) {
327+ segmentIds = args . filter . projectIds
328+ } else if ( args . filter ?. subprojectIds ?. length ) {
329+ segmentIds = args . filter . subprojectIds
330+ } else {
331+ segmentIds = [ projectGroupSegment . id ]
314332 }
315333
316334 let similarityFilter = ''
@@ -357,8 +375,25 @@ class MemberRepository {
357375 order += 'mtm."memberId", mtm."toMergeId"'
358376 }
359377
360- if ( args . countOnly ) {
361- const totalCount = await this . countMemberMergeSuggestions (
378+ const hasProjectFilter = Boolean (
379+ args . filter ?. projectIds ?. length || args . filter ?. subprojectIds ?. length ,
380+ )
381+
382+ const hasCountFilters = Boolean (
383+ args . filter ?. memberId || args . filter ?. displayName || args . filter ?. similarity ?. length ,
384+ )
385+
386+ const getTotalCount = async ( ) : Promise < number > => {
387+ if ( ! hasCountFilters && ! hasProjectFilter ) {
388+ const counts = await getSegmentMergeSuggestionCounts (
389+ SequelizeRepository . getQueryExecutor ( options ) ,
390+ projectGroupSegment . id ,
391+ )
392+
393+ return counts ?. memberMergeSuggestionsCount ?? 0
394+ }
395+
396+ return this . countMemberMergeSuggestions (
362397 memberFilter ,
363398 similarityFilter ,
364399 displayNameFilter ,
@@ -369,8 +404,10 @@ class MemberRepository {
369404 } ,
370405 options ,
371406 )
407+ }
372408
373- return { count : totalCount }
409+ if ( args . countOnly ) {
410+ return { count : await getTotalCount ( ) }
374411 }
375412
376413 const mems = await options . database . sequelize . query (
@@ -395,7 +432,16 @@ class MemberRepository {
395432 SELECT 1 FROM "memberSegmentsAgg" ms2
396433 WHERE ms2."memberId" = mtm."toMergeId" AND ms2."segmentId" IN (:segmentIds)
397434 )
398- AND mtm.similarity IS NOT NULL
435+ AND NOT EXISTS (
436+ SELECT 1
437+ FROM "mergeActions" ma
438+ WHERE ma.type = :mergeActionType
439+ AND ma.state <> :mergeActionState
440+ AND (
441+ (ma."primaryId" = mtm."memberId" AND ma."secondaryId" = mtm."toMergeId")
442+ OR (ma."primaryId" = mtm."toMergeId" AND ma."secondaryId" = mtm."memberId")
443+ )
444+ )
399445 ${ memberFilter }
400446 ${ similarityFilter }
401447 ${ displayNameFilter }
@@ -410,6 +456,8 @@ class MemberRepository {
410456 offset : args . offset ,
411457 displayName : args ?. filter ?. displayName ? `${ args . filter . displayName } %` : undefined ,
412458 memberId : args ?. filter ?. memberId ,
459+ mergeActionType : MergeActionType . MEMBER ,
460+ mergeActionState : MergeActionState . ERROR ,
413461 } ,
414462 type : QueryTypes . SELECT ,
415463 } ,
@@ -506,24 +554,12 @@ class MemberRepository {
506554 } ) )
507555 }
508556
509- const totalCount = await this . countMemberMergeSuggestions (
510- memberFilter ,
511- similarityFilter ,
512- displayNameFilter ,
513- {
514- segmentIds,
515- memberId : args ?. filter ?. memberId ,
516- displayName : args ?. filter ?. displayName ? `${ args . filter . displayName } %` : undefined ,
517- } ,
518- options ,
519- )
520-
521- return { rows : result , count : totalCount , limit : args . limit , offset : args . offset }
557+ return { rows : result , count : await getTotalCount ( ) , limit : args . limit , offset : args . offset }
522558 }
523559
524560 return {
525561 rows : [ { members : [ ] , similarity : 0 } ] ,
526- count : 0 ,
562+ count : await getTotalCount ( ) ,
527563 limit : args . limit ,
528564 offset : args . offset ,
529565 }
0 commit comments