Skip to content

Commit

Permalink
feat(api-search): Allow searching for messages by uid (#587)
Browse files Browse the repository at this point in the history
  • Loading branch information
louis-lau committed Jan 2, 2024
1 parent 299cc37 commit a4ae3d7
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 37 deletions.
8 changes: 8 additions & 0 deletions docs/api/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1945,6 +1945,11 @@ paths:
description: ID of the Mailbox
schema:
type: string
- name: id
in: query
description: Message ID values, only applies when used in combination with `mailbox`. Either comma separated numbers (1,2,3) or colon separated range (3:15), or a range from UID to end (3:*)
schema:
type: string
- name: thread
in: query
description: Thread ID
Expand Down Expand Up @@ -2091,6 +2096,9 @@ paths:
mailbox:
description: ID of the Mailbox
type: string
id:
description: Message ID values, only applies when used in combination with `mailbox`. Either comma separated numbers (1,2,3) or colon separated range (3:15), or a range from UID to end (3:*)
type: string
thread:
description: Thread ID
type: string
Expand Down
45 changes: 10 additions & 35 deletions lib/api/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const roles = require('../roles');
const { nextPageCursorSchema, previousPageCursorSchema, pageNrSchema, sessSchema, sessIPSchema, booleanSchema, metaDataSchema } = require('../schemas');
const { preprocessAttachments } = require('../data-url');
const TaskHandler = require('../task-handler');
const prepareSearchFilter = require('../prepare-search-filter');
const { prepareSearchFilter, uidRangeStringToQuery } = require('../prepare-search-filter');
const { getMongoDBQuery /*, getElasticSearchQuery*/ } = require('../search-query');
//const { getClient } = require('../elasticsearch');

Expand Down Expand Up @@ -226,41 +226,9 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
let moveTo = result.value.moveTo ? new ObjectId(result.value.moveTo) : false;
let message = result.value.message;

let messageQuery;
let messageQuery = uidRangeStringToQuery(message);

if (/^\d+$/.test(message)) {
messageQuery = Number(message);
} else if (/^\d+(,\d+)*$/.test(message)) {
messageQuery = {
$in: message
.split(',')
.map(uid => Number(uid))
.sort((a, b) => a - b)
};
} else if (/^\d+:(\d+|\*)$/.test(message)) {
let parts = message
.split(':')
.map(uid => Number(uid))
.sort((a, b) => {
if (a === '*') {
return 1;
}
if (b === '*') {
return -1;
}
return a - b;
});
if (parts[0] === parts[1]) {
messageQuery = parts[0];
} else {
messageQuery = {
$gte: parts[0]
};
if (!isNaN(parts[1])) {
messageQuery.$lte = parts[1];
}
}
} else {
if (!messageQuery) {
res.status(404);
return res.json({
error: 'Invalid message identifier',
Expand Down Expand Up @@ -626,6 +594,13 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
q: Joi.string().trim().empty('').max(1024).optional().description('Additional query string'),

mailbox: Joi.string().hex().length(24).empty('').description('ID of the Mailbox'),
id: Joi.string()
.trim()
.empty('')
.regex(/^\d+(,\d+)*$|^\d+:(\d+|\*)$/i)
.description(
'Message ID values, only applies when used in combination with `mailbox`. Either comma separated numbers (1,2,3) or colon separated range (3:15), or a range from UID to end (3:*)'
),
thread: Joi.string().hex().length(24).empty('').description('Thread ID'),

or: Joi.object({
Expand Down
50 changes: 49 additions & 1 deletion lib/prepare-search-filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,52 @@
const ObjectId = require('mongodb').ObjectId;
const { escapeRegexStr } = require('./tools');

const uidRangeStringToQuery = uidRange => {
if (!uidRange) {
return;
}

let query;

if (/^\d+$/.test(uidRange)) {
query = Number(uidRange);
} else if (/^\d+(,\d+)*$/.test(uidRange)) {
query = {
$in: uidRange
.split(',')
.map(uid => Number(uid))
.sort((a, b) => a - b)
};
} else if (/^\d+:(\d+|\*)$/.test(uidRange)) {
let parts = uidRange
.split(':')
.map(uid => Number(uid))
.sort((a, b) => {
if (a === '*') {
return 1;
}
if (b === '*') {
return -1;
}
return a - b;
});
if (parts[0] === parts[1]) {
query = parts[0];
} else {
query = {
$gte: parts[0]
};
if (!isNaN(parts[1])) {
query.$lte = parts[1];
}
}
}
return query;
};

const prepareSearchFilter = async (db, user, payload) => {
let mailbox = payload.mailbox ? new ObjectId(payload.mailbox) : false;
let idQuery = uidRangeStringToQuery(payload.id);
let thread = payload.thread ? new ObjectId(payload.thread) : false;

let orTerms = payload.or || {};
Expand Down Expand Up @@ -87,6 +131,10 @@ const prepareSearchFilter = async (db, user, payload) => {
filter.mailbox = { $nin: mailboxes.map(m => m._id) };
}

if (filter.mailbox && idQuery) {
filter.uid = idQuery;
}

if (thread) {
filter.thread = thread;
}
Expand Down Expand Up @@ -267,4 +315,4 @@ const prepareSearchFilter = async (db, user, payload) => {
return { filter, query };
};

module.exports = prepareSearchFilter;
module.exports = { uidRangeStringToQuery, prepareSearchFilter };
2 changes: 1 addition & 1 deletion lib/tasks/search-apply.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
const log = require('npmlog');
const db = require('../db');
const util = require('util');
const prepareSearchFilter = require('../prepare-search-filter');
const { prepareSearchFilter } = require('../prepare-search-filter');
const { getMongoDBQuery } = require('../search-query');
const ObjectId = require('mongodb').ObjectId;

Expand Down

0 comments on commit a4ae3d7

Please sign in to comment.