Add seach plugin for products
This commit is contained in:
parent
78cee39ca9
commit
a96c1d9c07
@ -10,9 +10,9 @@ const createProduct = catchAsync(async (req, res) => {
|
||||
});
|
||||
|
||||
const getProducts = catchAsync(async (req, res) => {
|
||||
const filter = pick(req.query, ['label', 'barcode']);
|
||||
const options = pick(req.query, ['sortBy', 'limit', 'page']);
|
||||
const result = await productService.queryProducts(filter, options);
|
||||
const query = pick(req.query, ['label', 'barcode']);
|
||||
const options = pick(req.query, ['limit']);
|
||||
const result = await productService.queryProducts(query, options);
|
||||
res.send(result);
|
||||
});
|
||||
|
||||
@ -34,10 +34,16 @@ const deleteProduct = catchAsync(async (req, res) => {
|
||||
res.status(httpStatus.NO_CONTENT).send();
|
||||
});
|
||||
|
||||
const getAllProducts = catchAsync(async (req, res) => {
|
||||
const products = await productService.getAllProducts();
|
||||
res.send(products);
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
createProduct,
|
||||
getProducts,
|
||||
getProduct,
|
||||
updateProduct,
|
||||
deleteProduct,
|
||||
getAllProducts,
|
||||
};
|
||||
|
@ -5,7 +5,7 @@ const catchAsync = require('../utils/catchAsync');
|
||||
const { profileService } = require('../services');
|
||||
|
||||
const createProfile = catchAsync(async (req, res) => {
|
||||
req.body.userId = req.user._id;
|
||||
req.body.user = req.user._id;
|
||||
const profile = await profileService.createProfile(req.body);
|
||||
res.status(httpStatus.CREATED).send(profile);
|
||||
});
|
||||
|
@ -1,2 +1,3 @@
|
||||
module.exports.toJSON = require('./toJSON.plugin');
|
||||
module.exports.paginate = require('./paginate.plugin');
|
||||
module.exports.search = require('./search.plugin');
|
||||
|
@ -1,23 +1,6 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
|
||||
const paginate = (schema) => {
|
||||
/**
|
||||
* @typedef {Object} QueryResult
|
||||
* @property {Document[]} results - Results found
|
||||
* @property {number} page - Current page
|
||||
* @property {number} limit - Maximum number of results per page
|
||||
* @property {number} totalPages - Total number of pages
|
||||
* @property {number} totalResults - Total number of documents
|
||||
*/
|
||||
/**
|
||||
* Query for documents with pagination
|
||||
* @param {Object} [filter] - Mongo filter
|
||||
* @param {Object} [options] - Query options
|
||||
* @param {string} [options.sortBy] - Sorting criteria using the format: sortField:(desc|asc). Multiple sorting criteria should be separated by commas (,)
|
||||
* @param {number} [options.limit] - Maximum number of results per page (default = 10)
|
||||
* @param {number} [options.page] - Current page (default = 1)
|
||||
* @returns {Promise<QueryResult>}
|
||||
*/
|
||||
schema.statics.paginate = async function (filter, options) {
|
||||
let sort = '';
|
||||
if (options.sortBy) {
|
||||
|
18
src/models/plugins/search.plugin.js
Normal file
18
src/models/plugins/search.plugin.js
Normal file
@ -0,0 +1,18 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
|
||||
const search = (schema) => {
|
||||
schema.statics.search = async function (query, options) {
|
||||
const [[key, pattern]] = Object.entries(query);
|
||||
|
||||
const searchRegex = Number.isInteger(pattern) ? pattern : new RegExp(`.*${pattern}.*`);
|
||||
const limit = options.limit && parseInt(options.limit, 10) > 0 ? parseInt(options.limit, 10) : 10;
|
||||
|
||||
const docsPromise = this.find({ [key]: searchRegex })
|
||||
.limit(limit)
|
||||
.exec();
|
||||
|
||||
return Promise.resolve(docsPromise);
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = search;
|
@ -1,10 +1,5 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
|
||||
/**
|
||||
* A mongoose schema plugin which applies the following in the toJSON transform call:
|
||||
* - removes __v, createdAt, updatedAt, and any path that has private: true
|
||||
* - replaces _id with id
|
||||
*/
|
||||
const toJSON = (schema) => {
|
||||
let transform;
|
||||
if (schema.options.toJSON && schema.options.toJSON.transform) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
const mongoose = require('mongoose');
|
||||
const { toJSON, paginate } = require('./plugins');
|
||||
const { toJSON, paginate, search } = require('./plugins');
|
||||
|
||||
const productSchema = mongoose.Schema(
|
||||
{
|
||||
@ -65,9 +65,10 @@ const productSchema = mongoose.Schema(
|
||||
|
||||
productSchema.plugin(toJSON);
|
||||
productSchema.plugin(paginate);
|
||||
productSchema.plugin(search);
|
||||
|
||||
productSchema.statics.isNameTaken = async function (name, excludeProductId) {
|
||||
const product = await this.findOne({ name, _id: { $ne: excludeProductId } });
|
||||
productSchema.statics.isNameTaken = async function (label, excludeProductId) {
|
||||
const product = await this.findOne({ label, _id: { $ne: excludeProductId } });
|
||||
return !!product;
|
||||
};
|
||||
|
||||
|
@ -1,16 +1,27 @@
|
||||
const mongoose = require('mongoose');
|
||||
const { toJSON, paginate } = require('./plugins');
|
||||
const { genderTypes } = require('../config/genders');
|
||||
const { goalTypes } = require('../config/goals');
|
||||
const { activityTypes } = require('../config/activities');
|
||||
|
||||
const profileSchema = mongoose.Schema(
|
||||
{
|
||||
gender: {
|
||||
type: String,
|
||||
enum: ['male', 'female'],
|
||||
enum: Object.values(genderTypes),
|
||||
required: true,
|
||||
},
|
||||
goal: {
|
||||
type: String,
|
||||
enum: ['lose_weight', 'maintain_weight', 'put_on_weight'],
|
||||
enum: Object.values(goalTypes),
|
||||
required: true,
|
||||
},
|
||||
dailyCalories: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
weeksToGoal: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
birthday: {
|
||||
@ -36,10 +47,10 @@ const profileSchema = mongoose.Schema(
|
||||
},
|
||||
activity: {
|
||||
type: Number,
|
||||
enum: [1.2, 1.375, 1.55, 1.725, 1.9],
|
||||
enum: Object.values(activityTypes),
|
||||
required: true,
|
||||
},
|
||||
userId: {
|
||||
user: {
|
||||
type: mongoose.SchemaTypes.ObjectId,
|
||||
ref: 'User',
|
||||
required: true,
|
||||
@ -55,7 +66,7 @@ profileSchema.plugin(toJSON);
|
||||
profileSchema.plugin(paginate);
|
||||
|
||||
profileSchema.statics.hasUser = async function (userId, excludeProfileId) {
|
||||
const user = await this.findOne({ userId, _id: { $ne: excludeProfileId } });
|
||||
const user = await this.findOne({ user: userId, _id: { $ne: excludeProfileId } });
|
||||
return !!user;
|
||||
};
|
||||
|
||||
|
@ -11,6 +11,10 @@ router
|
||||
.post(auth(), validate(productValidation.createProduct), productController.createProduct)
|
||||
.get(auth(), validate(productValidation.getProducts), productController.getProducts);
|
||||
|
||||
router
|
||||
.route('/all')
|
||||
.get(productController.getAllProducts)
|
||||
|
||||
router
|
||||
.route('/:productId')
|
||||
.get(auth(), validate(productValidation.getProduct), productController.getProduct)
|
||||
|
@ -14,7 +14,7 @@ router
|
||||
.delete(auth(), validate(profileValidation.deleteProfile), profileController.deleteProfile);
|
||||
|
||||
router
|
||||
.route('/admin')
|
||||
.route('/all')
|
||||
.get(auth(), validate(profileValidation.getProfiles), profileController.getProfiles);
|
||||
|
||||
module.exports = router;
|
||||
|
@ -3,7 +3,7 @@ const { Product } = require('../models');
|
||||
|
||||
const data = [
|
||||
{
|
||||
label: 'cappy - multiwitamina',
|
||||
label: 'multiwitamina',
|
||||
brand: 'capy',
|
||||
verified: true,
|
||||
eco: false,
|
||||
@ -17,7 +17,7 @@ const data = [
|
||||
salt: 0,
|
||||
},
|
||||
{
|
||||
label: 'star - chipsy o smaku smietana i cebula',
|
||||
label: 'chipsy o smaku smietana i cebula',
|
||||
brand: 'star',
|
||||
verified: true,
|
||||
eco: false,
|
||||
@ -30,6 +30,62 @@ const data = [
|
||||
protein: 6.2,
|
||||
salt: 1.4,
|
||||
},
|
||||
{
|
||||
label: 'Aloe',
|
||||
brand: 'tropical',
|
||||
verified: true,
|
||||
eco: false,
|
||||
barcode: 8712857004606,
|
||||
unit: 'ml',
|
||||
servingCapacity: 500,
|
||||
calories: 20,
|
||||
fat: 0,
|
||||
carbohydrates: 4.9,
|
||||
protein: 0,
|
||||
salt: 0.042,
|
||||
},
|
||||
{
|
||||
label: `chocolate m&m's`,
|
||||
brand: `m&m's`,
|
||||
verified: true,
|
||||
eco: false,
|
||||
barcode: 5000159471725,
|
||||
unit: 'g',
|
||||
servingCapacity: 150,
|
||||
calories: 481,
|
||||
fat: 19,
|
||||
carbohydrates: 71,
|
||||
protein: 5,
|
||||
salt: 0.13,
|
||||
},
|
||||
{
|
||||
label: `black`,
|
||||
brand: `black`,
|
||||
verified: true,
|
||||
eco: false,
|
||||
barcode: 5900552014713,
|
||||
unit: 'ml',
|
||||
servingCapacity: 250,
|
||||
calories: 42,
|
||||
fat: 0,
|
||||
carbohydrates: 9.9,
|
||||
protein: 0,
|
||||
salt: 0.18,
|
||||
},
|
||||
{
|
||||
label: `fantasia mleczna`,
|
||||
brand: `e.wedel`,
|
||||
verified: true,
|
||||
eco: false,
|
||||
barcode: 59074571,
|
||||
unit: 'g',
|
||||
servingCapacity: 104,
|
||||
calories: 166,
|
||||
fat: 9.3,
|
||||
carbohydrates: 16.9,
|
||||
protein: 0.3,
|
||||
salt: 0.14,
|
||||
},
|
||||
];
|
||||
|
||||
class ProductSeeder extends Seeder {
|
||||
|
@ -13,8 +13,8 @@ const createProduct = async (productBody) => {
|
||||
return product;
|
||||
};
|
||||
|
||||
const queryProducts = async (filter, options) => {
|
||||
const products = await Product.paginate(filter, options);
|
||||
const queryProducts = async (query, options) => {
|
||||
const products = await Product.search(query, options);
|
||||
return products;
|
||||
};
|
||||
|
||||
@ -45,6 +45,11 @@ const deleteProductById = async (productId) => {
|
||||
return product;
|
||||
};
|
||||
|
||||
const getAllProducts = async () => {
|
||||
const products = await Product.find();
|
||||
return products;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
createProduct,
|
||||
queryProducts,
|
||||
@ -52,4 +57,5 @@ module.exports = {
|
||||
getProductByBarcode,
|
||||
updateProductById,
|
||||
deleteProductById,
|
||||
getAllProducts,
|
||||
};
|
||||
|
@ -5,10 +5,18 @@ const calculateWeeksToGetGoalWeight = require('../utils/weeksToGetGoalWeight');
|
||||
const dailyCaloricRequirement = require('../utils/dailyCaloricRequirement');
|
||||
|
||||
const createProfile = async (profileBody) => {
|
||||
if (await Profile.hasUser(profileBody.userId)) {
|
||||
if (await Profile.hasUser(profileBody.user)) {
|
||||
throw new ApiError(httpStatus.BAD_REQUEST, 'User already has a profile');
|
||||
}
|
||||
const profile = await Profile.create(profileBody);
|
||||
|
||||
const dailyCalories = dailyCaloricRequirement(profileBody);
|
||||
const weeksToGoal = calculateWeeksToGetGoalWeight(profileBody);
|
||||
|
||||
const profile = await Profile.create({
|
||||
...profileBody,
|
||||
dailyCalories,
|
||||
weeksToGoal,
|
||||
});
|
||||
return profile;
|
||||
};
|
||||
|
||||
@ -19,13 +27,11 @@ const queryProfiles = async (filter, options) => {
|
||||
|
||||
const getProfileById = async (id) => {
|
||||
const profile = await Profile.findById(id);
|
||||
console.log(dailyCaloricRequirement(profile));
|
||||
console.log(calculateWeeksToGetGoalWeight(profile));
|
||||
return profile
|
||||
return profile;
|
||||
};
|
||||
|
||||
const getProfileByUserId = async (userId) => {
|
||||
return Profile.findOne({ userId });
|
||||
return Profile.findOne({ user: userId });
|
||||
};
|
||||
|
||||
const updateProfileById = async (profileId, updateBody) => {
|
||||
@ -33,7 +39,15 @@ const updateProfileById = async (profileId, updateBody) => {
|
||||
if (!profile) {
|
||||
throw new ApiError(httpStatus.NOT_FOUND, 'Profile not found');
|
||||
}
|
||||
Object.assign(profile, updateBody);
|
||||
|
||||
const dailyCalories = dailyCaloricRequirement(updateBody);
|
||||
const weeksToGoal = calculateWeeksToGetGoalWeight(updateBody);
|
||||
|
||||
Object.assign(profile, {
|
||||
...updateBody,
|
||||
dailyCalories,
|
||||
weeksToGoal,
|
||||
});
|
||||
await profile.save();
|
||||
return profile;
|
||||
};
|
||||
|
@ -6,14 +6,22 @@ const { activityTypes } = require('../config/activities');
|
||||
|
||||
const createProfile = {
|
||||
body: Joi.object().keys({
|
||||
gender: Joi.string().required().valid(Object.values(genderTypes)),
|
||||
goal: Joi.string().required().valid(Object.values(goalTypes)),
|
||||
gender: Joi.string()
|
||||
.valid(...Object.values(genderTypes))
|
||||
.required(),
|
||||
goal: Joi.string()
|
||||
.valid(...Object.values(goalTypes))
|
||||
.required(),
|
||||
dailyCalories: Joi.number(),
|
||||
weeksToGoal: Joi.number(),
|
||||
birthday: Joi.date().required(),
|
||||
height: Joi.number().required(),
|
||||
currentWeight: Joi.number().required(),
|
||||
goalWeight: Joi.number().required(),
|
||||
rateOfChange: Joi.number().required(),
|
||||
activity: Joi.number().required().valid(Object.values(activityTypes)),
|
||||
activity: Joi.number()
|
||||
.valid(...Object.values(activityTypes))
|
||||
.required(),
|
||||
}),
|
||||
};
|
||||
|
||||
@ -35,16 +43,24 @@ const updateProfile = {
|
||||
params: Joi.object().keys({
|
||||
profileId: Joi.required().custom(objectId),
|
||||
}),
|
||||
body: Joi.object().keys({
|
||||
gender: Joi.string().required().valid(Object.values(genderTypes)),
|
||||
goal: Joi.string().required().valid(Object.values(goalTypes)),
|
||||
birthday: Joi.date().required(),
|
||||
height: Joi.number().required(),
|
||||
currentWeight: Joi.number().required(),
|
||||
goalWeight: Joi.number().required(),
|
||||
rateOfChange: Joi.number(),
|
||||
activity: Joi.number().required().valid(Object.values(activityTypes)),
|
||||
}).min(1),
|
||||
body: Joi.object()
|
||||
.keys({
|
||||
gender: Joi.string()
|
||||
.valid(...Object.values(genderTypes))
|
||||
.required(),
|
||||
goal: Joi.string()
|
||||
.valid(...Object.values(goalTypes))
|
||||
.required(),
|
||||
birthday: Joi.date().required(),
|
||||
height: Joi.number().required(),
|
||||
currentWeight: Joi.number().required(),
|
||||
goalWeight: Joi.number().required(),
|
||||
rateOfChange: Joi.number(),
|
||||
activity: Joi.number()
|
||||
.required()
|
||||
.valid(...Object.values(activityTypes)),
|
||||
})
|
||||
.min(1),
|
||||
};
|
||||
|
||||
const deleteProfile = {
|
||||
|
Loading…
Reference in New Issue
Block a user