I have a node API which connects to a mongoDB through mongoose. I am creating an advanced results middleware that enabled selecting, filtering, sorting, pagination etc. based on a Brad Traversy course Node.js API Masterclass With Express & MongoDB. This is all good.
I am adapting the code from the course to be able to use the $ne (not equal) operator and I want to be able to get a model that is not equal to a nested property (user id) of the model. I am using this for an explore feature to see a list of things, but I don't want to show the user their own things. I am having trouble figuring out how to access the id property.
********************* UPDATE *********************
It seems all the documentation I've read recommends writing const injected like this:
const injected = {
'user._id': { "$ne": req.user.id }
};
but for some reason it is not working. I can query top level properties that are just a plain string value like this:
const injected = {
access: { "$ne": "public" }
};
but not a property on an object. Does anyone know why? Is it because the property I want to query is an id? I've also tried:
const injected = {
'user._id': { "$ne": mongoose.Types.ObjectId(req.user.id) }
};
which also does not work...
So the model looks like this:
{
name: 'Awesome post',
access: 'public',
user: {
_id: '2425635463456241345', // property I want to access
}
}
then the actual advanced results middleware looks like this and it's the 'injected' object where I am trying to access id. In the course brad uses this syntax to use lte (/?averageCost[lte]=10000) but I do not get any results with my ne. Can anyone help me here?
const advancedResults = (model, populate) => async (req, res, next) => {
let query;
const injected = {
access: 'public',
'user._id[ne]': req.user.id, // I don't think user._id[ne] is correct
};
}
// Copy req.query
const reqQuery = { ...req.query, ...injected };
console.log('injected: ', injected);
// Fields to exclude
const removeFields = ['select', 'sort', 'page', 'limit'];
// Loop over removeFields and delete them from reqQuery
removeFields.forEach(param => delete reqQuery[param]);
// Create query string
let queryStr = JSON.stringify(reqQuery);
// Create operators ($gt, $gte, etc)
queryStr = queryStr.replace(/\b(gt|gte|lt|lte|in|ne)\b/g, match => `$${match}`);
// Finding resource and remove version
query = model.find(JSON.parse(queryStr)).select('-__v');
// Select Fields
if (req.query.select) {
const fields = req.query.select.split(',').join(' ');
query = query.select(fields);
}
// Sort
if (req.query.sort) {
const sortBy = req.query.sort.split(',').join(' ');
query = query.sort(sortBy);
} else {
query = query.sort('-createdAt');
}
// Pagination
const page = parseInt(req.query.page, 10) || 1;
const limit = parseInt(req.query.limit, 10) || 25;
const startIndex = (page - 1) * limit;
const endIndex = page * limit;
const total = await model.countDocuments(JSON.parse(queryStr));
query = query.skip(startIndex).limit(limit);
if (populate) {
query = query.populate(populate);
}
// Executing query
const results = await query;
// Pagination result
const pagination = {};
if (endIndex < total) {
pagination.next = {
page: page + 1,
limit,
};
}
if (startIndex > 0) {
pagination.prev = {
page: page - 1,
limit,
};
}
res.advancedResults = {
success: true,
count: results.length,
pagination,
data: results,
};
next();
};
module.exports = advancedResults;
Answering your question about how to use $ne:
The use of $ne is as follows:
"field":{
"$ne": yourValue
}
Into your query should be like:
"user._id": {
"$ne": req.user.id
}
Example here
$ne operator will return all document where the field value don't match with the given value.
As you have done, to acces the nested field is necessary use the dot notation.
Also, to ensure it works, if your schema defines _id as ObjectId maybe is necessary parse req.user.id to ObjectId.
But if in your schema is a string then should works.
So try (not tested at all):
const injected = {
'user._id': { "$ne": req.user.id }
};
Related
I want to compare to ids one in relation and the other given by me in a query and get all information, for example:
async getAllPhoto(id: string) {
var photo = await this._photoRepository.find({
relations: {
catalogue: true,
},
where: { catalogue: { id: Not(id) } },
});
return photo;
}
I tried this but got an empty array.
const ids = 2; // get your id which you request from font-end
const photo = this.repository.find({
relations:['catalogue'],
where:{
catalogue:{
id: Not(ids)
}
}
})
when you develop project in nestjs,
you'd better enable "logging":true in your database config!
you will find all raw sql from ORM.
This is my first time of using bulkWrite to carry out updates via mongoose. I am building a blog application and I am using it to learn MERN stack. I have a Post model. The Post model has object value which is an array. This is an example of it:
const PostSchema = new mongoose.Schema(
{
postLikes:{
type: Array,
default: []
}
}
)
The postLikes contain mongodb object ids of users who liked a post.
I have a logic for deleting selected users or all users by an admin. The like system does not come with a Like Model of it own. I simply used an array system inside the post model. After deleting a user, I would like to update all post models with likes of the selected users. Some users may have multiple likes across different posts.
In my node, I created a variable like this:
const {selectedIds} = req.body;
The selectedIds came from reactjs like this:
const [selectedUsers, setSelectedUsers] = useState([]);
const arrayOfSelectedUserId = (userId) =>{
setSelectedUsers(prevArray => [...prevArray, userId]);
);
}
For the request, I did it like this:
const response = await axiosPrivate.post(`/v1/users/deleteSelected`, selectedIds, { withCredentials: true,
headers:{authorization: `Bearer ${auth.token}`}})
In nodejs, the selectedUsers ids was passed to this variable:
const {selectedIds} = req.body;
I created the logic this way:
const findIntersection = (array1, array2) => {
return array1.filter((elem) => {
return array2.indexOf(elem) !== -1;
});
}
const filteredPost = posts.filter((singleFilter) => {
const intersection = findIntersection(selectedIds, singleFilter.postLikes);
return singleFilter.postLikes.length !== 0 && intersection.length !== 0;
});
const updatedPosts = filteredPost.map((obj)=>{
const intersection = findIntersection(selectedIds, obj.postLikes);
console.log(intersection )
return {
updateOne: {
filter: { _id: obj._id },
update: { $pull: { postLikes: { $in: intersection } } },
},
};
});
Post.bulkWrite(updatedPosts).then((res) => {
console.log("Documents Updated", res.modifiedCount)
})
The console.log shows the text Document updated and showed number of documents updated. However, if I check my database, the update won't reflect. This means that the selected users' ID is still in the array.
Is there a better method? What Am I doing wrong?
I have this query to display in a table on frontend so I used paginate which is working fine
tableSchema.statics.getn = (query, options) => {
return mongoose.model(MODEL_NAME).paginate(query, options);
};
But when I am trying to perform search query then I am unable to perform paginate on that. Is there any way to send response as paginated form to all the searched queries
I tried following code
tableSchema.statics.search = query => {
const Id = Number(query);
const isNumeric = value => /^\d+$/.test(value);
if (!isNumeric(query)) {
if (query.includes("#")) {
const regex = new RegExp(query, "i");
return mongoose.model(MODEL_NAME).find({ "recipies.to": regex }).paginate(query);
}
return mongoose.model(MODEL_NAME).find({ "temp.name": query });
}
return mongoose.model(MODEL_NAME).find({ recipies: { Id } });
};
It is throwing me error that paginate is not a function. I tried storing find query result in object then performed paginate still it was not working.
I am using "mongoose-paginate-v2" for pagination
Hi I think you missed to add pagination pluging in model section.
const mongoose = require('mongoose');
const mongoosePaginate = require('mongoose-paginate-v2');
const mySchema = new mongoose.Schema({
/* your schema definition */
});
mySchema.plugin(mongoosePaginate);
const myModel = mongoose.model('SampleModel', mySchema);
myModel.paginate().then({}); // Usage
You need to add mongoosePaginate in model as plugin.
let options = {
sort: { createdOn: 1 },
page: 1,
limit: 10
};
ModelName.paginate({ 'recipies.to': 'value' }, options, function (err, result) {
if (err) {
console.log(err);
} else {
// Here you will get paginate array please console and check
console.log(result);
}
I'm searching the web and can't find the right way to make a query in my MongoDB using Mongoose.
I want to perform a query if I receive the parameter:
async list(req, res) {
const { page = 1, limit = 10, status = "" } = req.query;
const orders = await Order.paginate(
{ status },
{
page: parseInt(page),
limit: parseInt(limit),
populate: { path: "user", select: "firstName lastName" }
}
);
return res.send(orders);
}
So for example, if status is not provided in the URL, I want to return all documents, and if I provide the status, return only the ones that match.
I can achieve what I'm looking for if I pass a status, but I can't return all documents if I don't pass a status. I tried with "", undefined and null, but none of them worked.
I also think the way I'm approaching it is not the best, because I'll have to declare in const { page = 1, limit = 10, status = "" } = req.query; all possible query parameters.
How should I handle it?
PS: I'm using mongoose-paginate, but they redirect the query documentation to the MongoDB documentation, so I assume it's the same.
Thanks in advance!
Simple solution. I could accomplish it using the spread operation of ES6:
async list(req, res) {
const { page = 1, limit = 10 } = req.query;
let filters = req.query;
filters = { ...filters, page: undefined, limit: undefined };
const orders = await Order.paginate(
{ ...filters },
{
page: parseInt(page),
limit: parseInt(limit),
populate: { path: "user", select: "firstName lastName" }
}
);
return res.send(orders);
}
This ensures that undefined objects won't make part of the query
I'm sending query params as JSON format in req.query.p from my front-end MVC framework , the point is that this could be a dynamic key and value, for example:
req.query.p = {nombre : 'juan'}
or
req.query.p = {pais : 'chile'}
So I need the key, and the value to put them in the where statement, something like this
exports.select = function(req, res){
console.log('=> GET | Obtener peliculas'.bold.get);
db.Pelicula
.findAndCountAll({
limit : req.query.limit,
offset : req.query.offset,
where : req.query.p ? [req.query.p.KEY + " = ?", req.query.p.VAL] : null
})
.success(function(resp){
console.log(JSON.stringify(resp.rows, null, 4).bold.get);
res.json({peliculas : resp.rows, meta : { total : resp.count}});
});
}
The where parameter can be an object, so you can just pass where: req.query.p
Usually I put the entire object, so if it comes empty, it will work normally as if there is no conditional WHERE.
You don't need to add {} in the where, because the object that comes from req.query already has it.
const filter = req.query;
example= await ModelExample.findAndCountAll({
where:
filter
})
With ES6 and with usage of the dynamic properties I'll do it like this
const { Op } = require("sequelize");
const from = new Date()
// const to = new Date().setMinutes(40)
const to = null
let where = {
timestamp: {
[Op.or]: {}
}
}
if (from) {
where.timestamp[Op.or][Op.gte] = new Date(from)
}
if (to) {
where.timestamp[Op.or][Op.lte] = new Date(to)
}
console.log(where);
Model.find({ where })