Skip to content

Commit

Permalink
✨ Allow querying events by multiple keywords using OR condition (#3070)
Browse files Browse the repository at this point in the history
* ✨ Allow querying events by multiple keywords using OR condition

* 📝 Update comment

* ✨ Update operator to ||

* ✅ Fix test

* 🐛 Handle edge cases around search query

* ✨ Codgen
  • Loading branch information
foysalit authored Nov 29, 2024
1 parent 6226546 commit c72145d
Show file tree
Hide file tree
Showing 10 changed files with 183 additions and 8 deletions.
2 changes: 1 addition & 1 deletion lexicons/tools/ozone/moderation/queryEvents.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/client/lexicons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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[]
Expand Down
2 changes: 1 addition & 1 deletion packages/ozone/src/lexicon/lexicons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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[]
Expand Down
14 changes: 13 additions & 1 deletion packages/ozone/src/mod-service/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
123 changes: 123 additions & 0 deletions packages/ozone/tests/__snapshots__/moderation-events.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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",
},
]
`;
40 changes: 40 additions & 0 deletions packages/ozone/tests/moderation-events.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down
2 changes: 1 addition & 1 deletion packages/pds/src/lexicon/lexicons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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[]
Expand Down

0 comments on commit c72145d

Please sign in to comment.