From c72145dbeb2d67068bc28c00a13447e0d382d121 Mon Sep 17 00:00:00 2001 From: Foysal Ahamed Date: Fri, 29 Nov 2024 18:00:48 +0000 Subject: [PATCH] :sparkles: Allow querying events by multiple keywords using OR condition (#3070) * :sparkles: Allow querying events by multiple keywords using OR condition * :memo: Update comment * :sparkles: Update operator to || * :white_check_mark: Fix test * :bug: Handle edge cases around search query * :sparkles: Codgen --- .../tools/ozone/moderation/queryEvents.json | 2 +- packages/api/src/client/lexicons.ts | 2 +- .../tools/ozone/moderation/queryEvents.ts | 2 +- packages/ozone/src/lexicon/lexicons.ts | 2 +- .../tools/ozone/moderation/queryEvents.ts | 2 +- packages/ozone/src/mod-service/index.ts | 14 +- .../moderation-events.test.ts.snap | 123 ++++++++++++++++++ .../ozone/tests/moderation-events.test.ts | 40 ++++++ packages/pds/src/lexicon/lexicons.ts | 2 +- .../tools/ozone/moderation/queryEvents.ts | 2 +- 10 files changed, 183 insertions(+), 8 deletions(-) diff --git a/lexicons/tools/ozone/moderation/queryEvents.json b/lexicons/tools/ozone/moderation/queryEvents.json index 4f45cba221f..9e89a94830a 100644 --- a/lexicons/tools/ozone/moderation/queryEvents.json +++ b/lexicons/tools/ozone/moderation/queryEvents.json @@ -70,7 +70,7 @@ }, "comment": { "type": "string", - "description": "If specified, only events with comments containing the keyword are returned" + "description": "If specified, only events with comments containing the keyword are returned. Apply || separator to use multiple keywords and match using OR condition." }, "addedLabels": { "type": "array", diff --git a/packages/api/src/client/lexicons.ts b/packages/api/src/client/lexicons.ts index 2d99e7afe0e..723586b8f86 100644 --- a/packages/api/src/client/lexicons.ts +++ b/packages/api/src/client/lexicons.ts @@ -12218,7 +12218,7 @@ export const schemaDict = { comment: { type: 'string', description: - 'If specified, only events with comments containing the keyword are returned', + 'If specified, only events with comments containing the keyword are returned. Apply || separator to use multiple keywords and match using OR condition.', }, addedLabels: { type: 'array', diff --git a/packages/api/src/client/types/tools/ozone/moderation/queryEvents.ts b/packages/api/src/client/types/tools/ozone/moderation/queryEvents.ts index 31cdf3d6c7d..a73c5819ce4 100644 --- a/packages/api/src/client/types/tools/ozone/moderation/queryEvents.ts +++ b/packages/api/src/client/types/tools/ozone/moderation/queryEvents.ts @@ -28,7 +28,7 @@ export interface QueryParams { limit?: number /** If true, only events with comments are returned */ hasComment?: boolean - /** If specified, only events with comments containing the keyword are returned */ + /** If specified, only events with comments containing the keyword are returned. Apply || separator to use multiple keywords and match using OR condition. */ comment?: string /** If specified, only events where all of these labels were added are returned */ addedLabels?: string[] diff --git a/packages/ozone/src/lexicon/lexicons.ts b/packages/ozone/src/lexicon/lexicons.ts index 2d99e7afe0e..723586b8f86 100644 --- a/packages/ozone/src/lexicon/lexicons.ts +++ b/packages/ozone/src/lexicon/lexicons.ts @@ -12218,7 +12218,7 @@ export const schemaDict = { comment: { type: 'string', description: - 'If specified, only events with comments containing the keyword are returned', + 'If specified, only events with comments containing the keyword are returned. Apply || separator to use multiple keywords and match using OR condition.', }, addedLabels: { type: 'array', diff --git a/packages/ozone/src/lexicon/types/tools/ozone/moderation/queryEvents.ts b/packages/ozone/src/lexicon/types/tools/ozone/moderation/queryEvents.ts index 752fe333f0b..3dff4ca7cf7 100644 --- a/packages/ozone/src/lexicon/types/tools/ozone/moderation/queryEvents.ts +++ b/packages/ozone/src/lexicon/types/tools/ozone/moderation/queryEvents.ts @@ -29,7 +29,7 @@ export interface QueryParams { limit: number /** If true, only events with comments are returned */ hasComment?: boolean - /** If specified, only events with comments containing the keyword are returned */ + /** If specified, only events with comments containing the keyword are returned. Apply || separator to use multiple keywords and match using OR condition. */ comment?: string /** If specified, only events where all of these labels were added are returned */ addedLabels?: string[] diff --git a/packages/ozone/src/mod-service/index.ts b/packages/ozone/src/mod-service/index.ts index fc24ece0a00..f3dc14ef965 100644 --- a/packages/ozone/src/mod-service/index.ts +++ b/packages/ozone/src/mod-service/index.ts @@ -222,8 +222,20 @@ export class ModerationService { if (createdBefore) { builder = builder.where('createdAt', '<=', createdBefore) } + if (comment) { - builder = builder.where('comment', 'ilike', `%${comment}%`) + // the input may end in || in which case, there may be item in the array which is just '' and we want to ignore those + const keywords = comment.split('||').filter((keyword) => !!keyword.trim()) + if (keywords.length > 1) { + builder = builder.where((qb) => { + keywords.forEach((keyword) => { + qb = qb.orWhere('comment', 'ilike', `%${keyword}%`) + }) + return qb + }) + } else if (keywords.length === 1) { + builder = builder.where('comment', 'ilike', `%${keywords[0]}%`) + } } if (hasComment) { builder = builder.where('comment', 'is not', null) diff --git a/packages/ozone/tests/__snapshots__/moderation-events.test.ts.snap b/packages/ozone/tests/__snapshots__/moderation-events.test.ts.snap index 17dec972658..0e2d414f2df 100644 --- a/packages/ozone/tests/__snapshots__/moderation-events.test.ts.snap +++ b/packages/ozone/tests/__snapshots__/moderation-events.test.ts.snap @@ -197,3 +197,126 @@ Array [ }, ] `; + +exports[`moderation-events query events returns events matching multiple keywords in comment 1`] = ` +Array [ + Object { + "createdAt": "1970-01-01T00:00:00.000Z", + "createdBy": "user(1)", + "creatorHandle": "bob.test", + "event": Object { + "$type": "tools.ozone.moderation.defs#modEventReport", + "comment": "rainy days feel lazy", + "isReporterMuted": false, + "reportType": "com.atproto.moderation.defs#reasonSpam", + }, + "id": 16, + "subject": Object { + "$type": "com.atproto.admin.defs#repoRef", + "did": "user(0)", + }, + "subjectBlobCids": Array [], + "subjectHandle": "alice.test", + }, + Object { + "createdAt": "1970-01-01T00:00:00.000Z", + "createdBy": "user(1)", + "creatorHandle": "bob.test", + "event": Object { + "$type": "tools.ozone.moderation.defs#modEventReport", + "comment": "november rain", + "isReporterMuted": false, + "reportType": "com.atproto.moderation.defs#reasonSpam", + }, + "id": 15, + "subject": Object { + "$type": "com.atproto.admin.defs#repoRef", + "did": "user(0)", + }, + "subjectBlobCids": Array [], + "subjectHandle": "alice.test", + }, +] +`; + +exports[`moderation-events query events returns events matching multiple keywords in comment 2`] = ` +Array [ + Object { + "createdAt": "1970-01-01T00:00:00.000Z", + "createdBy": "user(1)", + "creatorHandle": "bob.test", + "event": Object { + "$type": "tools.ozone.moderation.defs#modEventReport", + "comment": "rainy days feel lazy", + "isReporterMuted": false, + "reportType": "com.atproto.moderation.defs#reasonSpam", + }, + "id": 16, + "subject": Object { + "$type": "com.atproto.admin.defs#repoRef", + "did": "user(0)", + }, + "subjectBlobCids": Array [], + "subjectHandle": "alice.test", + }, + Object { + "createdAt": "1970-01-01T00:00:00.000Z", + "createdBy": "user(1)", + "creatorHandle": "bob.test", + "event": Object { + "$type": "tools.ozone.moderation.defs#modEventReport", + "comment": "november rain", + "isReporterMuted": false, + "reportType": "com.atproto.moderation.defs#reasonSpam", + }, + "id": 15, + "subject": Object { + "$type": "com.atproto.admin.defs#repoRef", + "did": "user(0)", + }, + "subjectBlobCids": Array [], + "subjectHandle": "alice.test", + }, +] +`; + +exports[`moderation-events query events returns events matching multiple keywords in comment 3`] = ` +Array [ + Object { + "createdAt": "1970-01-01T00:00:00.000Z", + "createdBy": "user(1)", + "creatorHandle": "bob.test", + "event": Object { + "$type": "tools.ozone.moderation.defs#modEventReport", + "comment": "rainy days feel lazy", + "isReporterMuted": false, + "reportType": "com.atproto.moderation.defs#reasonSpam", + }, + "id": 16, + "subject": Object { + "$type": "com.atproto.admin.defs#repoRef", + "did": "user(0)", + }, + "subjectBlobCids": Array [], + "subjectHandle": "alice.test", + }, + Object { + "createdAt": "1970-01-01T00:00:00.000Z", + "createdBy": "user(1)", + "creatorHandle": "bob.test", + "event": Object { + "$type": "tools.ozone.moderation.defs#modEventReport", + "comment": "november rain", + "isReporterMuted": false, + "reportType": "com.atproto.moderation.defs#reasonSpam", + }, + "id": 15, + "subject": Object { + "$type": "com.atproto.admin.defs#repoRef", + "did": "user(0)", + }, + "subjectBlobCids": Array [], + "subjectHandle": "alice.test", + }, +] +`; diff --git a/packages/ozone/tests/moderation-events.test.ts b/packages/ozone/tests/moderation-events.test.ts index 986cd10bfc9..181f7e9f3f4 100644 --- a/packages/ozone/tests/moderation-events.test.ts +++ b/packages/ozone/tests/moderation-events.test.ts @@ -224,6 +224,46 @@ describe('moderation-events', () => { expect(eventsWithComment.events.length).toEqual(10) }) + it('returns events matching multiple keywords in comment', async () => { + await sc.createReport({ + reasonType: REASONSPAM, + reason: 'november rain', + subject: { + $type: 'com.atproto.admin.defs#repoRef', + did: sc.dids.alice, + }, + reportedBy: sc.dids.bob, + }) + await sc.createReport({ + reasonType: REASONSPAM, + reason: 'rainy days feel lazy', + subject: { + $type: 'com.atproto.admin.defs#repoRef', + did: sc.dids.alice, + }, + reportedBy: sc.dids.bob, + }) + const [eventsMatchingBothKeywords, unusedTrailingSeparator, extraSpaces] = + await Promise.all([ + modClient.queryEvents({ + hasComment: true, + comment: 'november||lazy', + }), + modClient.queryEvents({ + hasComment: true, + comment: 'november||lazy||', + }), + modClient.queryEvents({ + hasComment: true, + comment: '||november||lazy|| ', + }), + ]) + + expect(forSnapshot(eventsMatchingBothKeywords.events)).toMatchSnapshot() + expect(forSnapshot(unusedTrailingSeparator.events)).toMatchSnapshot() + expect(forSnapshot(extraSpaces.events)).toMatchSnapshot() + }) + it('returns events matching filter params for labels', async () => { const [negatedLabelEvent, createdLabelEvent] = await Promise.all([ modClient.emitEvent({ diff --git a/packages/pds/src/lexicon/lexicons.ts b/packages/pds/src/lexicon/lexicons.ts index 2d99e7afe0e..723586b8f86 100644 --- a/packages/pds/src/lexicon/lexicons.ts +++ b/packages/pds/src/lexicon/lexicons.ts @@ -12218,7 +12218,7 @@ export const schemaDict = { comment: { type: 'string', description: - 'If specified, only events with comments containing the keyword are returned', + 'If specified, only events with comments containing the keyword are returned. Apply || separator to use multiple keywords and match using OR condition.', }, addedLabels: { type: 'array', diff --git a/packages/pds/src/lexicon/types/tools/ozone/moderation/queryEvents.ts b/packages/pds/src/lexicon/types/tools/ozone/moderation/queryEvents.ts index 752fe333f0b..3dff4ca7cf7 100644 --- a/packages/pds/src/lexicon/types/tools/ozone/moderation/queryEvents.ts +++ b/packages/pds/src/lexicon/types/tools/ozone/moderation/queryEvents.ts @@ -29,7 +29,7 @@ export interface QueryParams { limit: number /** If true, only events with comments are returned */ hasComment?: boolean - /** If specified, only events with comments containing the keyword are returned */ + /** If specified, only events with comments containing the keyword are returned. Apply || separator to use multiple keywords and match using OR condition. */ comment?: string /** If specified, only events where all of these labels were added are returned */ addedLabels?: string[]