Skip to content

Commit

Permalink
feature(articleSearch/Filter): Implement search/filter article functi…
Browse files Browse the repository at this point in the history
…onality

- Implement filter by author username only
- Implement filter by tag only
- Implement search by keywords
- Implement any combination of supplied filters
[Delivers #163478809]
  • Loading branch information
Ibidapo committed Feb 20, 2019
1 parent 86dd9e5 commit d0159d8
Show file tree
Hide file tree
Showing 9 changed files with 438 additions and 150 deletions.
232 changes: 116 additions & 116 deletions package-lock.json

Large diffs are not rendered by default.

82 changes: 61 additions & 21 deletions server/api/controllers/article.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ import notifyFollowers from '../helpers/notification/followers';
import {
User, Article, LikeDislike, Tag, Rating
} from '../../models';
import {
getArticlesByAllParams, getArticlesBySearchTagParams, getArticlesBySearchAuthorParams,
getArticlesByTagAuthorParams, getArticlesBySearchParam, getArticlesByTagParam,
getArticlesByAuthorParam
} from '../helpers/search';

const {
Op
} = Sequelize;
const { Op } = Sequelize;

const logger = createLogger({
level: 'debug',
Expand All @@ -31,13 +34,9 @@ const insertTag = async (tagArray) => {
insertedTags = tagArray.map(async (tagText) => {
const newTag = await Tag.findOrCreate({
where: {
tagText: {
[Op.eq]: tagText
}
tagText: { [Op.eq]: tagText }
},
defaults: {
tagText
}
defaults: { tagText }
});
return newTag;
});
Expand Down Expand Up @@ -92,9 +91,7 @@ export const createArticle = async (req, res) => {
references,
categoryId
},
user: {
id: userId
}
user: { id: userId }
} = req;
// create the slug from the title by replacing spaces with hyphen
// eg. "Introduction to writing" becomes "introduction-to-writing"
Expand All @@ -111,7 +108,7 @@ export const createArticle = async (req, res) => {

if (req.body.tags) {
const newArticleTags = req.body.tags;
const tagArray = formatTags(newArticleTags);
const tagArray = await formatTags(newArticleTags);
const newTags = await insertTag(tagArray);
newTags.forEach(async (tag) => {
await newArticle.addTags(tag[0].id);
Expand Down Expand Up @@ -146,14 +143,7 @@ export const createArticle = async (req, res) => {
*/
export const likeArticle = async (req, res) => {
try {
const {
params: {
id: articleId
},
user: {
id: userId
}
} = req;
const { params: { id: articleId }, user: { id: userId } } = req;

const foundImpression = await LikeDislike.findOne({
where: {
Expand Down Expand Up @@ -591,3 +581,53 @@ export const deleteArticle = async (req, res) => {
});
}
};

/**
* @export
* @function filterArticle
* @param {Object} req - request received
* @param {Object} res - response object
* @returns {Object} JSON object (JSend format)
*/
export const filterArticle = async (req, res) => {
const { query: { searchStr, tag, author } } = req;
let foundArticles;

try {
if (searchStr && tag && author) {
foundArticles = await getArticlesByAllParams(author, tag, searchStr);
} else if (searchStr && tag) {
foundArticles = await getArticlesBySearchTagParams(tag, searchStr);
} else if (searchStr && author) {
foundArticles = await getArticlesBySearchAuthorParams(author, searchStr);
} else if (tag && author) {
foundArticles = await getArticlesByTagAuthorParams(tag, author);
} else if (searchStr) {
foundArticles = await getArticlesBySearchParam(searchStr);
} else if (tag) {
foundArticles = await getArticlesByTagParam(tag);
} else if (author) {
foundArticles = await getArticlesByAuthorParam(author);
}

if (!foundArticles.length) {
return res.status(404).send({
status: 'fail',
data: { message: 'No Article was found' }
});
}

return res.status(200).send({
status: 'success',
data: {
message: 'Articles found',
articles: foundArticles
}
});
} catch (e) {
return res.status(500).send({
status: 'error',
message: 'Internal server error occurred'
});
}
};
147 changes: 147 additions & 0 deletions server/api/helpers/search.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import Sequelize from 'sequelize';
import { User, Tag, Article } from '../../models';

const { Op } = Sequelize;
let foundAuthorId;
let foundArticles;
let foundTag;

const findAuthorId = async (username) => {
try {
const foundAuthorObj = await User.findOne({
where: {
username: { [Op.eq]: username }
}
});

return foundAuthorObj.id;
} catch (e) {
return undefined;
}
};

const findTag = async (id) => {
try {
const foundTagObj = await Tag.findByPk(id);

return foundTagObj;
} catch (e) {
return undefined;
}
};

export const getArticlesByAllParams = async (username, tagId, searchString) => {
try {
foundAuthorId = await findAuthorId(username);
foundTag = await findTag(tagId);
foundArticles = await foundTag.getArticles({
where: {
authorId: { [Op.eq]: foundAuthorId },
[Op.or]: [
{ body: { [Op.iLike]: `%${searchString}%` } },
{ title: { [Op.iLike]: `%${searchString}%` } }
]
}
});

return foundArticles;
} catch (e) {
return undefined;
}
};

export const getArticlesBySearchTagParams = async (tagId, searchString) => {
try {
foundTag = await findTag(tagId);
foundArticles = await foundTag.getArticles({
where: {
[Op.or]: [
{ body: { [Op.iLike]: `%${searchString}%` } },
{ title: { [Op.iLike]: `%${searchString}%` } }
]
}
});

return foundArticles;
} catch (e) {
return undefined;
}
};

export const getArticlesBySearchAuthorParams = async (username, searchString) => {
try {
foundAuthorId = await findAuthorId(username);
foundArticles = await Article.findAll({
where: {
authorId: { [Op.eq]: foundAuthorId },
[Op.or]: [
{ body: { [Op.iLike]: `%${searchString}%` } },
{ title: { [Op.iLike]: `%${searchString}%` } }
]
}
});

return foundArticles;
} catch (e) {
return undefined;
}
};

export const getArticlesByTagAuthorParams = async (tagId, username) => {
try {
foundTag = await findTag(tagId);
foundAuthorId = await findAuthorId(username);
foundArticles = await foundTag.getArticles({
where: {
authorId: { [Op.eq]: foundAuthorId }
}
});

return foundArticles;
} catch (e) {
return undefined;
}
};

export const getArticlesBySearchParam = async (searchString) => {
try {
foundArticles = Article.findAll({
where: {
[Op.or]: [
{ body: { [Op.iLike]: `%${searchString}%` } },
{ title: { [Op.iLike]: `%${searchString}%` } }
]
}
});

return foundArticles;
} catch (e) {
return undefined;
}
};

export const getArticlesByTagParam = async (tagId) => {
try {
foundTag = await findTag(tagId);
foundArticles = await foundTag.getArticles();

return foundArticles;
} catch (e) {
return undefined;
}
};

export const getArticlesByAuthorParam = async (username) => {
try {
foundAuthorId = await findAuthorId(username);
foundArticles = await Article.findAll({
where: {
authorId: { [Op.eq]: foundAuthorId }
}
});

return foundArticles;
} catch (e) {
return undefined;
}
};
3 changes: 2 additions & 1 deletion server/api/routes/article.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import passport from 'passport';
import { newArticleValidator, ratingValidator, updateArticleValidator } from '../middlewares/validation/article';
import {
createArticle, updateArticle, likeArticle, dislikeArticle, rateArticle,
getAllArticles, getArticle, deleteArticle
getAllArticles, getArticle, deleteArticle, filterArticle
} from '../controllers/article';
import { reportArticle } from '../controllers/reports';
import { shareArticle } from '../controllers/shares';
Expand All @@ -14,6 +14,7 @@ import highlightRouter from './highlight';
const articlesRouter = Router();

articlesRouter.post('/', passport.authenticate('jwt', { session: false }), newArticleValidator, createArticle);
articlesRouter.get('/filter?', passport.authenticate('jwt', { session: false }), filterArticle);
articlesRouter.put('/:articleId', passport.authenticate('jwt', { session: false }), updateArticleValidator, updateArticle);
articlesRouter.delete('/:articleId', passport.authenticate('jwt', { session: false }), deleteArticle);
articlesRouter.patch('/:id/likes', passport.authenticate('jwt', { session: false }), likeArticle);
Expand Down
1 change: 0 additions & 1 deletion server/models/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ const opts = {
};

opts.logging = env === 'development';

const sequelize = new Sequelize(config.url, opts);

fs
Expand Down
Loading

0 comments on commit d0159d8

Please sign in to comment.