Skip to content

Commit

Permalink
added advanced route with params
Browse files Browse the repository at this point in the history
separated query validation based on route pathname

cleaned up the files

added validation

cleaned up validator

added oneof in declarations

fixed validation

changed query fields and added if statements for options passed
  • Loading branch information
AmasiaNalbandian committed Dec 16, 2021
1 parent cf5c7b8 commit ad0209e
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 50 deletions.
57 changes: 54 additions & 3 deletions src/api/search/src/bin/validation.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { check, validationResult } = require('express-validator');
const { oneOf, check, validationResult } = require('express-validator');

const queryValidationRules = [
// text must be between 1 and 256 and not empty
Expand Down Expand Up @@ -31,8 +31,59 @@ const queryValidationRules = [
.bail(),
];

const validateQuery = (rules) => {
/**
* Advanced search is more flexible, only needs at least ONE field, but can run without any too.
* Date formats must be YYYY-MM-DD
*/
const advancedQueryValidationRules = [
oneOf([
check('post')
.exists({ checkFalsy: true })
.withMessage('post should not be empty')
.bail()
.isLength({ max: 256, min: 1 })
.withMessage('post should be between 1 to 256 characters')
.bail(),
check('author')
.exists({ checkFalsy: true })
.withMessage('author should exist')
.bail()
.isLength({ max: 100, min: 2 })
.withMessage('invalid author value')
.bail(),
check('title')
.exists({ checkFalsy: true })
.withMessage('title should exist')
.bail()
.isLength({ max: 100, min: 2 })
.withMessage('invalid title value')
.bail(),
]),
check('to').optional().isISO8601().withMessage('invalid date format').bail(),

check('from').optional().isISO8601().withMessage('invalid date format').bail(),
check('perPage')
.optional()
.isInt({ min: 1, max: 10 })
.withMessage('perPage should be empty or a number between 1 to 10')
.bail(),

check('page')
.optional()
.isInt({ min: 0, max: 999 })
.withMessage('page should be empty or a number between 0 to 999')
.bail(),
];

/**
* Validates query by passing rules. The rules are different based on the pathname
* of the request. If the pathname is '/' it is the basic route.
* Otherwise, if '/advanced/' it is the advanced search
*/
const validateQuery = () => {
return async (req, res, next) => {
const rules = req.baseUrl === '/' ? queryValidationRules : advancedQueryValidationRules;

await Promise.all(rules.map((rule) => rule.run(req)));

const result = validationResult(req);
Expand All @@ -45,4 +96,4 @@ const validateQuery = (rules) => {
};
};

module.exports.validateQuery = validateQuery(queryValidationRules);
module.exports.validateQuery = validateQuery();
11 changes: 10 additions & 1 deletion src/api/search/src/routes/query.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const { Router, createError } = require('@senecacdot/satellite');
const { validateQuery } = require('../bin/validation');
const search = require('../search');
const { search, advancedSearch } = require('../search');

const router = Router();

Expand All @@ -13,4 +13,13 @@ router.get('/', validateQuery, async (req, res, next) => {
}
});

// route for advanced
router.get('/advanced', validateQuery, async (req, res, next) => {
try {
res.send(await advancedSearch(req.query));
} catch (error) {
next(createError(503, error));
}
});

module.exports = router;
104 changes: 58 additions & 46 deletions src/api/search/src/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,63 +85,75 @@ const search = async (
* @param dateEnd - published before this date
* @return all the results matching the fields text
* Range queries: https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html#_ranges
* Match field queries: https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query.html#query-dsl-match-query-zero
* Match field queries: https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-ppmatch-query.html#query-dsl-match-query-zero
*/
const advancedSearch = async (
postText = '',
authorText = '',
titleText = '',
dateStart = '2000-01-01',
dateEnd = 'now',
page = 0,
perPage = ELASTIC_MAX_RESULTS_PER_PAGE
) => {
const advancedSearch = async (options) => {
const query = {
query: {
bool: {
must: [
{
match: {
author: {
query: authorText,
zero_terms_query: 'all',
},
},
},
{
match: {
text: {
query: postText,
zero_terms_query: 'all',
},
},
},
{
match: {
title: {
query: titleText,
zero_terms_query: 'all',
},
},
},
{
range: {
published: {
gte: dateStart,
lte: dateEnd,
},
},
},
],
must: [],
},
},
};

const must = query.query.bool.must;

if (options.author) {
must.push({
match: {
author: {
query: options.author,
zero_terms_query: 'all',
},
},
});
}

if (options.post) {
must.push({
match: {
text: {
query: options.post,
zero_terms_query: 'all',
},
},
});
}

if (options.title) {
must.push({
match: {
title: {
query: options.title,
zero_terms_query: 'all',
},
},
});
}

if (options.from || options.to) {
must.push({
range: {
published: {
gte: options.from || '2000-01-01',
lte: options.to || Date.now,
},
},
});
}

if (!options.perPage) {
options.perPage = ELASTIC_MAX_RESULTS_PER_PAGE;
}

if (!options.page) {
options.page = 0;
}
const {
body: { hits },
} = await client.search({
from: calculateFrom(page, perPage),
size: perPage,
from: calculateFrom(options.page, options.perPage),
size: options.perPage,
_source: ['id'],
index,
type,
Expand Down

0 comments on commit ad0209e

Please sign in to comment.