Skip to content

Commit

Permalink
Merge pull request #35 from andela/ft-search-164139695
Browse files Browse the repository at this point in the history
#164139695 Add search and filter functionality
  • Loading branch information
9jaswag authored Mar 19, 2019
2 parents 7637815 + 445e90f commit 1486942
Show file tree
Hide file tree
Showing 20 changed files with 675 additions and 295 deletions.
1 change: 1 addition & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"plugins": ["transform-es2015-destructuring", "transform-object-rest-spread"],
"presets": [ "es2015" ]
}
137 changes: 133 additions & 4 deletions controllers/articles.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { Article } from '../models';
import { Article, User } from '../models';
import Paginate from '../helpers/paginate';

/**
* @class ArticleController
* @override
* @export
*/
export default class ArticleController {
/**
/**
* @description - Create a new article
* @static
* @param {Object} req - the request object
Expand All @@ -17,13 +18,20 @@ export default class ArticleController {
static async createArticle(req, res) {
const images = req.images || [];
const {
title, description, body, slug
title, description, body, slug, categoryId
} = req.body;
const taglist = req.body.taglist ? req.body.taglist.split(',') : [];
const { id } = req.user;
try {
const result = await Article.create({
title, description, body, slug, images, taglist, userId: id
title,
description,
body,
slug,
images,
taglist,
userId: id,
categoryId: categoryId || 1
});
return res.status(201).json({
success: true,
Expand Down Expand Up @@ -102,4 +110,125 @@ export default class ArticleController {
});
}
}

/**
* @description - Search for articles
* @static
* @param {Object} req - the request object
* @param {Object} res - the response object
* @memberof ArticleController
* @returns {Object} class instance
*/
static async searchForArticles(req, res) {
try {
let { offset, limit } = req.query;
offset = Number(offset) || 0;
limit = Number(limit) || 10;
const searchTerms = ArticleController.generateSearchQuery(req.query);
const results = await Article.findAndCountAll({
where: {
...searchTerms,
},
include: [{
model: User,
attributes: ['username', 'email', 'name', 'bio'],
as: 'author',
}],
offset,
limit,
});
const { count } = results;
const meta = Paginate({ count, limit, offset });
return res.status(200).json({
results,
...meta,
success: true
});
} catch (error) {
return res.status(500).json({
success: false,
errors: ['Oops, something went wrong.']
});
}
}

/**
* @description - Generate queries for search and filter
* @static
* @param {Object} searchTerms - the terms that the user wants to search for
* @memberof ArticleController
* @returns {Object} class instance
*/
static generateSearchQuery(searchTerms) {
const {
author, term, endDate, startDate, tags, categoryId
} = searchTerms;

const filterFields = {
'$author.username$': {
$like: `%${author}%`
},
createdAt: {
$between: [startDate, endDate]
},
title: {
$like: `%${term}%`,
},
description: {
$like: `%${term}%`,
},
taglist: {
$contains: tags ? [...tags.split(',')] : []
},
categoryId: Number(categoryId),
};

if (!author) {
delete filterFields['$author.username$'];
}
if (!startDate || !endDate) {
delete filterFields.createdAt;
}
if (!categoryId) {
delete filterFields.categoryId;
}
return filterFields;
}

/**
* @description - Get article by slug
* @static
* @param {Object} req - the request object
* @param {Object} res - the response object
* @memberof ArticleController
* @returns {Object} class instance
*/
static async getArticleBySlug(req, res) {
try {
const {
params: {
slug
}
} = req;
const article = await Article.findOne({
where: {
slug
},
include: [{
as: 'author',
model: User,
attributes: ['username', 'email', 'name', 'bio'],
}]
});
return res.status(200).json({
success: true,
article: article.dataValues
});
} catch (error) {
return res.status(404).json({
success: false,
errors: ['Article not found.'],
});
}
}
}
4 changes: 2 additions & 2 deletions controllers/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,9 @@ export default class UserController {
message: [error.message]
}));
}
return res.json({
return res.status(404).json({
success: false,
message: 'User not found'
errors: ['User not found.']
});
});
}
Expand Down
4 changes: 4 additions & 0 deletions helpers/paginate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default ({ count, limit, offset }) => ({
totalPages: Math.ceil(count / limit),
currentPage: Math.ceil(offset / limit)
});
6 changes: 6 additions & 0 deletions middleware/validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,9 @@ export const validatePassword = [
.custom(value => !/\s/.test(value))
.withMessage('No spaces are allowed in the password.')
];

export const validateSearch = [
check('term')
.isString()
.withMessage('Please provide a valid search term.')
];
21 changes: 21 additions & 0 deletions migrations/20190314104937-insert-default-category.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module.exports = {
up: queryInterface => queryInterface.bulkInsert(
'Categories',
[
{
categoryName: 'Uncategorized',
createdAt: new Date(),
updatedAt: new Date()
}
]
),

down: queryInterface => queryInterface.bulkDelete(
'Categories',
[
{
categoryName: 'Uncategorized'
}
]
),
};
4 changes: 4 additions & 0 deletions migrations/20190319105746-add-indexes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
up: queryInterface => queryInterface.addIndex('Articles', ['title', 'description']),
down: queryInterface => queryInterface.removeIndex('Articles', ['title', 'description']),
};
1 change: 1 addition & 0 deletions models/article.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ module.exports = (sequelize, DataTypes) => {
Article.belongsTo(models.User, {
foreignKey: 'userId',
onDelete: 'CASCADE',
as: 'author'
});
Article.belongsTo(models.Category, {
foreignKey: 'categoryId',
Expand Down
Loading

0 comments on commit 1486942

Please sign in to comment.