Find value inside a collection in mongoose from nested json - node.js

Suppose I have data stored in collection Teacher as:
{
"name":"john",
"age":45,
"class_access":{
1234:"head",
1235:"head
},
"_id" : ObjectId("12312312"),
}
{
"name":"henry",
"age":55,
"class_access":{
1234:"head",
},
"_id" : ObjectId("sdf9991"),
}
{
"name":"travis",
"age":35,
"class_access":{
2341:"head",
},
"_id" : ObjectId("sas21"),
}
I want to find all the information of all the teachers belonging to class 1234.
For this I tried:
const TeacherDetails = await Teacher.find({ class_access: {1234:"head"} })
But it doesn't return anything.
So how can I access nested json to get all the details?
If any one needs any further information do let me know.
As per solution provided by Mr. Arif
const TeacherDetails = await Teacher.find({ "class_access.1234": "head" });
Support class value is not constant say I'm getting it from variable
const className = 1234
Now if I try, fetching className, it gives syntax error, I tried following syntax error for all of them
const TeacherDetails = await Teacher.find({ class_access.className: "head" });
const TeacherDetails = await Teacher.find({ class_access[className]: "head" });
const TeacherDetails = await Teacher.find({ 'class_access.+'${className}': "head" });
So how can we do it dynamically?

Since class_access represent object which may have multiple keys, you should try like
You can use [variableName] as key like following
method 1: Using string template
const className = 1234
const TeacherDetails = await Teacher.find({ [`class_access.${className }`]: "head" });
method 2: Using string concatenation
const className = 1234
const classAccess = 'class_access.' + className;
const TeacherDetails = await Teacher.find({ [classAccess] : "head" });

Related

MongoDb bulkWrite is not working for me in node.js

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?

How do I add an entry in an array inside MongoDB

Below is my code to add an item to an array inside a mongoDB object. I am trying to add a review for my restaurant
The restaurant object looks like:
_id: ObjectID("61723c7378b6d3a5a02d908e")
name: "Hotel"
reviews: Array
reviews.js:
const mongoCollections = require('../config/mongoCollections');
const restaurants = mongoCollections.restaurants;
module.exports = {
async created (restaurantId, title, reviewer, rating, dateOfReview, review) {
const restaurantsCollection = await restaurants();
let newReview = {
restaurantId : restaurantId,
title : title,
reviewer : reviewer,
rating : rating,
dateOfReview : dateOfReview,
review : review
};
const insertInfo = await restaurantsCollection.updateOne( {_id : restaurantId},{$addToSet: {reviews: newReview}} )
if (insertInfo.insertedCount === 0) throw 'Could not add review';
},
index.js:
const restaurantsData = require('./restaurants');
const reviewsData = require('./reviews')
module.exports = {
restaurants: restaurantsData,
reviews: reviewsData
};
seed.js: // to call the function
const dbConnection = require('../config/mongoConnection');
const data = require('../data/');
const restaurants = data.restaurants;
const reviews = data.reviews;
const main = async () => {
await reviews.created("61723c7378b6d3a5a02d908e", "random", "sam", 4, "25/2/2002",
"amazing");
}
main();
I tried to use update instead of updateOne but it shows a deprecated warning

Search for user by email or by primary key of type uuid

I'm using typeorm with typescript and the postgresql driver
I have this code in my controller:
const userRepository = getCustomRepository(UserRepositories);
const query = { by_email: {where: {email: user_receiver} }, by_uuid: user_receiver }
const isValidReceiver = user_receiver.includes("#") ? await userRepository.findOne(query.by_email) : await userRepository.findOne(query.by_uuid)
Is there any way to simplify this query or the checking if the user receiver is an email or a uuid?
you use the following function, to check if is a valid email, yup.js or some other libraries to check it for you
const isEmail = (email: string) => {
const regex = /^(([^<>()[\]\\.,;:\s#"]+(\.[^<>()[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
if (!regex.test(email)) {
return false
}
return true
}

How to use MongoDB $ne on nested object property

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 }
};

sequelize dynamic query params

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 })

Resources