Skip to content

Commit 1fd1991

Browse files
committed
Feature: Add search field for mongo queries
1 parent 63b6f03 commit 1fd1991

File tree

3 files changed

+82
-64
lines changed

3 files changed

+82
-64
lines changed

src/models/eventsFactory.js

Lines changed: 62 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,15 @@ class EventsFactory extends Factory {
156156
limit = 10,
157157
skip = 0,
158158
sort = 'BY_DATE',
159-
filters = {}
159+
filters = {},
160+
search = ''
160161
) {
162+
if (typeof search !== 'string') {
163+
throw new Error('Search parameter must be a string');
164+
}
165+
166+
const escapedSearch = search.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
167+
161168
limit = this.validateLimit(limit);
162169

163170
switch (sort) {
@@ -184,71 +191,64 @@ class EventsFactory extends Factory {
184191
},
185192
];
186193

187-
/**
188-
* If some events should be omitted, use alternative pipeline
189-
*/
190-
if (Object.values(filters).length > 0) {
191-
pipeline.push(
192-
/**
193-
* Lookup events object for each daily event
194-
*/
195-
{
196-
$lookup: {
197-
from: 'events:' + this.projectId,
198-
localField: 'groupHash',
199-
foreignField: 'groupHash',
200-
as: 'event',
194+
const searchFilter = search
195+
? {
196+
$or: [
197+
{
198+
'event.payload.title': {
199+
$regex: escapedSearch,
200+
$options: 'i',
201+
},
201202
},
202-
},
203-
{
204-
$unwind: '$event',
205-
},
206-
/**
207-
* Match filters
208-
*/
209-
{
210-
$match: {
211-
...Object.fromEntries(
212-
Object
213-
.entries(filters)
214-
.map(([mark, exists]) => [`event.marks.${mark}`, { $exists: exists } ])
215-
),
203+
{
204+
'event.payload.backtrace.file': {
205+
$regex: escapedSearch,
206+
$options: 'i',
207+
},
216208
},
209+
],
210+
}
211+
: {};
212+
213+
const matchFilter = filters
214+
? Object.fromEntries(
215+
Object
216+
.entries(filters)
217+
.map(([mark, exists]) => [`event.marks.${mark}`, { $exists: exists } ])
218+
)
219+
: {};
220+
221+
pipeline.push(
222+
{
223+
$lookup: {
224+
from: 'events:' + this.projectId,
225+
localField: 'groupHash',
226+
foreignField: 'groupHash',
227+
as: 'event',
217228
},
218-
{ $skip: skip },
219-
{ $limit: limit },
220-
{
221-
$group: {
222-
_id: null,
223-
dailyInfo: { $push: '$$ROOT' },
224-
events: { $push: '$event' },
225-
},
229+
},
230+
{
231+
$unwind: '$event',
232+
},
233+
{
234+
$match: {
235+
...matchFilter,
236+
...searchFilter,
226237
},
227-
{
228-
$unset: 'dailyInfo.event',
229-
}
230-
);
231-
} else {
232-
pipeline.push(
233-
{ $skip: skip },
234-
{ $limit: limit },
235-
{
236-
$group: {
237-
_id: null,
238-
groupHash: { $addToSet: '$groupHash' },
239-
dailyInfo: { $push: '$$ROOT' },
240-
},
238+
},
239+
{ $skip: skip },
240+
{ $limit: limit },
241+
{
242+
$group: {
243+
_id: null,
244+
dailyInfo: { $push: '$$ROOT' },
245+
events: { $push: '$event' },
241246
},
242-
{
243-
$lookup: {
244-
from: 'events:' + this.projectId,
245-
localField: 'groupHash',
246-
foreignField: 'groupHash',
247-
as: 'events',
248-
},
249-
}
250-
);
251-
}
247+
},
248+
{
249+
$unset: 'dailyInfo.event',
250+
}
251+
);
252252

253253
const cursor = this.getCollection(this.TYPES.DAILY_EVENTS).aggregate(pipeline);
254254

@@ -316,7 +316,7 @@ class EventsFactory extends Factory {
316316
});
317317

318318
/**
319-
* Group events using 'groupByTimestamp:NNNNNNNN' key
319+
* Group events using 'groupingTimestamp:NNNNNNNN' key
320320
* @type {ProjectChartItem[]}
321321
*/
322322
const groupedData = groupBy('groupingTimestamp')(dailyEvents);

src/resolvers/project.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,13 +304,20 @@ module.exports = {
304304
* @param {Number} skip - certain number of documents to skip
305305
* @param {'BY_DATE' | 'BY_COUNT'} sort - events sort order
306306
* @param {EventsFilters} filters - marks by which events should be filtered
307+
* @param {String} search - search query
307308
*
308309
* @return {Promise<RecentEventSchema[]>}
309310
*/
310-
async recentEvents(project, { limit, skip, sort, filters }) {
311+
async recentEvents(project, { limit, skip, sort, filters, search }) {
312+
if (search) {
313+
if (search.length > 100) {
314+
throw new UserInputError('Search query is too long. Maximum length is 100 characters');
315+
}
316+
}
317+
311318
const factory = new EventsFactory(project._id);
312319

313-
return factory.findRecent(limit, skip, sort, filters);
320+
return factory.findRecent(limit, skip, sort, filters, search);
314321
},
315322

316323
/**

src/typeDefs/project.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ type Project {
124124
125125
"Event marks by which events should be sorted"
126126
filters: EventsFiltersInput
127+
128+
"Search query"
129+
search: String
127130
): RecentEvents
128131
"""
129132
Return events that occurred after a certain timestamp
@@ -230,4 +233,12 @@ extend type Mutation {
230233
projectId: ID!
231234
): DateTime! @requireUserInWorkspace
232235
}
236+
237+
input EventsFilter {
238+
"""
239+
Search query string. Only alphanumeric characters, spaces, and some special characters are allowed.
240+
Max length: 100 characters
241+
"""
242+
search: String
243+
}
233244
`;

0 commit comments

Comments
 (0)