check an array of string value with array of object in mongodb - node.js

I have array of strings like this
let fromHour = ['2.5','3','3.5']
let toHour = ['2.5','3','3.5']
I have an array of object saved in mongoDB
timeRange = [
{
from:'2.5',
to:'3'
},
{
from:'3',
to:'3.5'
}
]
I want to check if any of my array of string value exist in that object value
I have tried this but it give me this error ( Unrecognized expression '$match' )
checkAppoint = await Appointment.aggregate([
{
$project: {
date: myScheduleFinal[k].date,
status: { $in: ['pending', 'on-going'] },
timeRange: {
'$match': {
'from': { $in: fromHolder },
'to': { $in: toHolder },
},
},
},
},
]);
also I have tried this solution and it work for me but it take to much time so I am trying this with aggregate
checkAppoint = await Appointment.findOne({
date: myScheduleFinal[k].date,
status: { $in: ['pending', 'on-going'] },
timeRange:{$elemMatch:{
from:{$in:fromHolder},
to:{$in:toHolder}
}}
});
So anyone have a solution for that

Just try $elemMatch and $in operators,
using find() method
checkAppoint = await Appointment.find({
timeRange: {
$elemMatch: {
from: { $in: fromHour },
to: { $in: toHour }
}
}
})
Playground
using aggregate() method
checkAppoint = await Appointment.aggregate([
{
$match: {
timeRange: {
$elemMatch: {
from: { $in: fromHour },
to: { $in: toHour }
}
}
}
}
])
Playground

So I have found a way around to solve this problem and I will share the solution I used
First I want to minimize my request to mongodb so I am now making just one request that bring all the appointment with the required date
and I want to make it this way because my fromHour and toHour array will change many time through single request
helperArray => contains all the day I want to check it's range
let checkAppoint = await Appointment.find({
date: { $in: helperArray },
status: { $in: ['pending', 'on-going'] },
});
now inside my for loop I will go through that data
checkAppoint.filter((singleAppoint) => {
if (singleAppoint._doc.date === myScheduleFinal[k].date) {
singleAppoint._doc.timeRange.map((singleTime) => {
if (fromHolder.includes(singleTime.from)) {
busy = true;
}
});
}
});

Related

Update nested object in array MongoDB

I need to find and update documents with category that corresponding to the query. Array could contain mo than one corresponding id.
Query:
{
"ids": ["61f1cda47018c60012b3dd01", "61f1cdb87018c60012b3dd07"],
"userId": "61eab3e57018c60012b3db3f"
}
I got collection with documents like:
`{
"_id":{"$oid":"61f1cdd07018c60012b3dd09"},
"expenses":[
{"category":"61eafc104b88e154caa58616","price":"1111.00"},
{"category":"61f1cdb87018c60012b3dd07","price":"2222.00"},
{"category":"61f1cda47018c60012b3dd01","price":"1241.00"},
{"category":"61f1cdb87018c60012b3dd07","price":"111.00"}
],
"userId":"61eab3e57018c60012b3db3f"
}`
my method:
async myMethod(ids: [string], userId: string) {
try {
const { ok } = await this.ExpensesModel.updateMany(
{"userId": userId, "expenses.category": { $in: ids }},
{$set: {"expenses.$.category": "newCategoryID"}}
);
return ok
} ........
I path array of ids ["61f1cda47018c60012b3dd01","61f1cdb87018c60012b3dd07","61f1cdb87018c60012b3dd07"] and userId, this code update only 1 category by document.
So can i made it with mongo build in methods? or i need to find matching document and update it it by my self and after that update or insert;
Update with arrayFilters
db.collection.update({
"expenses.category": {
$in: [
"61f1cda47018c60012b3dd01",
"61f1cdb87018c60012b3dd07"
]
}
},
{
$set: {
"expenses.$[elem].category": "61eab3e57018c60012b3db3f"
}
},
{
arrayFilters: [
{
"elem.category": {
$in: [
"61f1cda47018c60012b3dd01",
"61f1cdb87018c60012b3dd07"
]
}
}
]
})
mongoplayground

How to pass an optional argument in Mongoose/MongoDb

I have the following query:
Documents.find({
$and: [
{
user_id: {$nin:
myUserId
}
},
{ date: { $gte: dateMax, $lt: dateMin } },
{documentTags: {$all: tags}}
],
})
What I'm trying to do is make the documentTags portion of the query optional. I have tried building the query as follows:
let tags = " ";
if (req.body.tags) {
tags = {videoTags: {$all: req.body.tags}};
}
let query = {
$and: [
{
user_id: {$nin:
myUserId
}
},
{ date: { $gte: dateMax, $lt: dateMin } },
tags
],
}
and then Document.find(query). The problem is no matter how I modify tags (whether undefined, as whitespace, or otherwise) I get various errors like $or/$and/$nor entries need to be full objects and TypeError: Cannot read property 'hasOwnProperty' of undefined.
Is there a way to build an optional requirement into the query?
I tried the option below and the query is just returning everything that matches the other fields. For some reason it isn't filtering by tags. I did a console.log(queryArr) and console.log(query) get the following respectively:
[
{ user_id: { '$nin': [Array] } },
{
date: {
'$gte': 1985-01-01T00:00:00.000Z,
'$lt': 2020-01-01T00:00:00.000Z
}
},
push: { documentTags: { '$all': [Array] } }
]
console.log(query)
{
'$and': [
{ user_id: [Object] },
{ date: [Object] },
push: { documentTags: [Object] }
]
}
You are almost there. Instead you could construct the object outside the query and just put the constructed query in $and when done..
let queryArr = [
{
user_id: {$nin: myUserId}
},
{ date: { $gte: dateMax, $lt: dateMin } }
];
if (req.body.tags) {
queryArr.push({videoTags: {$all: req.body.tags}});
}
let query = {
$and: queryArr
}
Now you can control the query by just pushing object into the query Array itself.
I figured out why it wasn't working. Basically, when you do myVar.push it creates a key-value pair such as [1,2,3,push:value]. This would work if you needed to append a k-v pair in that format, but you'll have difficulty using it in a query like mine. The right way for me turned out to be to use concact which appends the array with just the value that you set, rather than a k-v pair.
if (req.body.tags){
queryArgs = queryArgs.concat({documentTags: {$all: tags}});
}
let query = {
$and: queryArgs
}

Mongoose: cannot infer query fields to set, path 'participants' is matched twice

I'm using mongoose with Node.js to create a document of chat with participants as one of the fields if the chat doesn't exist.
If it does exist then simply increment the status to 1 or any number.
My current Solution:
try {
let query = { participants: { $all: [CURRENT_USER_ID, TARGETED_ID] } };
let update = { $inc: { status: 1 }};
let options = { upsert: true, new: true };
let chat = await Chat.findOneAndUpdate(
query,
update,
options
).exec();
console.log(chat);
} catch (err) {
console.log(err.message);
}
I will receive an error
"cannot infer query fields to set, path 'participants' is matched
twice"
I even use this solution and it doesn't work, it created an empty list of participants instead.
let query = {
participants: {
$all: [
{ $elemMatch: { $eq: CURRENT_USER_ID } },
{ $elemMatch: { $eq: TARGETED_ID } }
]
}
};
Any help would be really helpful. Thanks

Select fields in mongoose query where field value not equal to something

I am basically trying to update a document and then select the fields from the result where the field value is not equal to something. Assume jwt_id to be b816cf00e9f649fbaf613e2ca2d523b5.
Query
const removeDevices = await Identity.findOneAndUpdate(
{
userID: user_id
},
{
$pull: {
activeTokens: {
jti: {
$ne: jwt_id
}
}
}
},
).select(["-_id", "activeTokens.jti"]);
Now, running this query gives the following output:
{ activeTokens:
[ { jti: '5d872359af2c47e5970c1fae531adf0e' },
{ jti: 'd3ac84f520614067b1caad504d7ab27f' },
{ jti: '25c6fa96705c4eec96e1427678c3ff50' },
{ jti: 'b816cf00e9f649fbaf613e2ca2d523b5' }
]
}
How can I get all the jti fields except { jti: b816cf00e9f649fbaf613e2ca2d523b5 } from the select command?
Desired Output
{ activeTokens:
[ { jti: '5d872359af2c47e5970c1fae531adf0e' },
{ jti: 'd3ac84f520614067b1caad504d7ab27f' },
{ jti: '25c6fa96705c4eec96e1427678c3ff50' },
]
}
It's hard to say for certain without testing, but i don't think mongoose returns the document after it was modified, but rather simply returns the matching document. So, i think in the case of findOneAndUpdate, you would have to have your query match to do the pull, and then manually filter the array again in application code to get the desired output.
This might work:
const removeDevices = await Identity.findOneAndUpdate(
{
userID: user_id
},
{
$pull: {
'activeTokens.jti': { $ne: jwt_id }
}
},
).select(["-_id", "activeTokens.jti"]).then(identity=>identity.activeTokens.filter(token=>token.jti!==jwt_id));
If the above doesn't work for some reason, then i would try something more simpler
simple:
const removeDevices = await Identity.findOne({userID: user_id}).select(["-_id", "activeTokens"]).then(identity=>{
const removedTokens = []
identity.activeTokens = identity.activeTokens.filter(token=>{
if(token.jti===jwt_id) {
return true;
}
removedTokens.push(token);
})
identity.save(err=>{
console.log('doc saved')
});
return removedTokens;
});
or (atomic):
const removeDevices = await Identity.findOne({userID: user_id}).select('activeTokens','jti _id').then(identity=>{
const removedTokens = identity.activeTokens.filter(token=>token.jti!==jwt_id);
const result = await Identity.update({userId:user_id},{$pull:{'activeTokens._id': { $in: removedTokens.map(t=>t._id) } }});
console.log(result.nModified);
return removedTokens;
});

mongoDB find, update and pull in One Query

I want to do all the find the data from the collection and then want to update some field as well as depending on want to empty the array.
const addCityFilter = (req, res) => {
if (req.body.aCities === "") {
res.status(409).jsonp({ message: adminMessages.err_fill_val_properly });
return false;
} else {
var Cities = req.body.aCities.split(","); // It will make array of Cities
const filterType = { "geoGraphicalFilter.filterType": "cities", "geoGraphicalFilter.countries": [], "geoGraphicalFilter.aCoordinates": [] };
/** While using $addToset it ensure that to not add Duplicate Value
* $each will add all values in array
*/
huntingModel
.update(
{
_id: req.body.id,
},
{
$addToSet: {
"geoGraphicalFilter.cities": { $each: Cities }
}
},
{$set:{filterType}},
).then(function(data) {
res.status(200).jsonp({
message: adminMessages.succ_cityFilter_added
});
});
}
};
Collection
geoGraphicalFilter: {
filterType: {
type:String,
enum: ["countries", "cities", "polygons"],
default: "countries"
},
countries: { type: Array },
cities: { type: Array },
aCoordinates: [
{
polygons: { type: Array }
}
]
}
But as result, the only city array is getting an update. No changes in filterType.
You appear to be passing the $set of filterType as the options argument, not the update argument.
huntingModel
.update(
{
_id: req.body.id,
},
{
$addToSet: {
"geoGraphicalFilter.cities": { $each: Cities }
},
$set: {
filterType
}
}
).then(function(data) {
res.status(200).jsonp({
message: adminMessages.succ_cityFilter_added
});
});

Resources