i have a variable that can either be an array of string or be an empty array.
const userHospitalQuery = [ req.query.hospital || [] ].flat();
when i filter with is as an array of strings, it works fine. Im running into a problem when i try to run my mongo search with the variable not having any values or empty (no Query pass in the url), how do I make mongo skip or ignore it as part of the filters? so only my other 2 filters are applied to the search. i know i cant use if statements inside my query object so im not sure what to do
const userHospitalQuery = [req.query.hospital || []].flat();
const filter = {
"staff.hospital": { $in: userHospitalQuery }, // How do i ignore/skip this if no query is passed in the URL
organisation: { $eq: "Test" },
isDeleted: { $eq: false },
};
const staff = await User.find(filter).sort({ dateAdded: -1 });
Just modify the filter object with a regular if.
const userHospitalQuery = [req.query.hospital || []].flat();
const filter = {
organisation: { $eq: "Test" },
isDeleted: { $eq: false },
};
if (userHospitalQuery.length) {
filter["staff.hospital"] = { $in: userHospitalQuery };
}
const staff = await User.find(filter).sort({ dateAdded: -1 });
Related
I'm trying to use the following find query to find a list of objects which are related to a "work" object with a specific ID, then return all "form" elements which aren't set to inactive (active = false).
This works perfectly using the mongo shell locally, or testing on mongoplayground. But I can't figure out how to get mongoose to play nice and return the results I expect, I always get an empty array.
I don't understand why it would work perfectly find natively, but not via mongoose. There must be some kind of issue or peculiarity I'm not finding in the docs. My mongodb version is 5.0.5 and my mongoose version is 6.1.5.
Mongo find query I'm trying to use:
db.objects.find({
"work": ObjectId("61d50a22eed3f421dc33a220")
},
{
"form": {
"$filter": {
"input": "$form",
"as": "form",
"cond": {
"$ne": [
"$$form.active",
false
]
}
}
}
})
Mongoose code with Projection (not working, returning []):
export const getWIObjects = async (workID: string) => {
const objs = await ObjectModel.find({
work: new mongoose.Types.ObjectId(workID),
}).select({
form: {
$filter: {
input: "$form",
as: "form",
cond: {
$ne: ["$$form.active", false],
},
},
},
});
return objs;
};
Mongoose code WITHOUT projection (returning data, but will include inactive fields):
export const getWIObjects = async (workID: string) => {
const objs = await ObjectModel.find({
work: new mongoose.Types.ObjectId(workID),
});
return objs;
};
I tried the following with MongoDB v5.0.6 and Mongoose v6.2.4, and the query returns results with the expected projection.
const projectn =
{
form: {
$filter: {
input: "$form",
as: "f",
cond: {
$ne: [ "$$f.active", false ]
}
}
}
};
const filter = { }; // substitute your filter
const result = await MyModel.find(filter, projectn);
The following query (with different syntax) also returned the same result.
const result = await MyModel.find(filter).select(projectn);
NOTE: This projection syntax using aggregation operators requires at least MongoDB v4.4.
What you have written in the Mongoose code with Projection is actually returning documents.
I think why you are getting an empty array [], because you are probably querying an invalid workID that is not in the collection.
Please check the workID you are querying in the find method.
try using aggregation for this
const objs = await ObjectModel.aggregate([
{
'$match': {
work: mongoose.Types.ObjectId(workID),
"form.active": false
}
}, {
'$project': {
form: {
$filter: {
input: "$form",
as: "f",
cond: {
$ne: ["$$f.active", false],
},
},
},
}
}
])
include form.active key in match also as it will lessen the number of documents after match query only
I have a model:
const schema = new Schema({
// ....
conditions: {},
// ....
});
Conditions - nested document and I can save anything into it with any key. And let's say we have such conditions:
{
"conditions": {
"age": 10,
"name": "John"
}
}
This is located on the base. Now, I want to find this document, but since I don't know what fields are there, I am facing problems...
const conditions = {
'conditions.age': 10,
'conditions.name': 'John',
'conditions.surname': 'White' // surname doesn't exists
}
const result = await Model.find(conditions);
console.log(result) // [];
And the question is, is it possible to exclude from the filter the fields that are missing in the document? So that find() simply skipped them, did not take them into account...
Use Logical Query Operators $and and $or as below-
const conditions = {
$and: [
{ 'conditions.age': 10, },
{ 'conditions.name': 'John', },
{ $or: [{ 'conditions.surname': { $exists: false } }, { 'conditions.surname': 'White' }] }
]
}
const result = await Model.find(conditions);
i have the following schema type:
Paitnet:
var PaitentSchema = new mongoose.Schema({
username: String,
password: String,
name: String,
protocol: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Protocol'
},
treatmentTypes: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'TreatmentType'
}],
accesses: [AccessSchema],
reports: [ReportSchema],
}, { collection: ' Paitents' });
and the AccessSchema:
var AccessSchema = new mongoose.Schema({
stageBool: Boolean,
exerciseBool: [{ type: Boolean }]
});
and what I'm trying to do is to update the exerciseBool array for example change one of the values in the array from 'false' to 'true'.
I have tried this code and its work for me but the Problem is that I get the index from the client so I need to embed the indexes in dynamically way (not always 0 and 1)
here is what I did(not dynamically ):
const paitent = await Paitent.updateOne({ username: req.params.username },
{ $set: { "accesses.0.exerciseBool.1": true } });
I want to do something like this but in dynamically indexes way.
please someone can help me?
thanks.
As you said, indexes are known but values may change.
you can use the following to create your query.
const accessesIndex = 0;
const exerciseBoolIndex = 1;
const update = { $set: { [`accesses.${accessesIndex}.exerciseBool.${exerciseBoolIndex}`]: true } };
console.log(update);
//const paitent = await Paitent.updateOne({ username: req.params.username }, update); // run your update query like this
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
Update
Check the index exists or not then only update the record.
add into your query "accesses.0.exerciseBool.1": { $exists: true } to make sure the accesses.0.exerciseBool.1 exists in the record.
const accessesIndex = 0;
const exerciseBoolIndex = 1;
const username = 'abc';
const key = `accesses.${accessesIndex}.exerciseBool.${exerciseBoolIndex}`;
const query = { username, [key]: { "$exists": true } };
console.log('query:', query);
const update = { $set: { [key]: true } };
console.log('update:', update);
Update Working demo - https://mongoplayground.net/p/GNOuZr3wqqw
No update demo - https://mongoplayground.net/p/nsTC8s-ruyo
If you are using MongoDB version >= 4.4. you can use $function along with update-with-aggregation-pipeline to update array dynamically. Try this:
let index1 = 0;
let index2 = 1;
db.patients.updateOne(
{ username: "dhee" },
[
{
$set: {
accesses: {
$function: {
body: function(accesses, index1, index2) {
if (accesses[index1] != null
&& accesses[index1].exerciseBool[index2] != null) {
accesses[index1].exerciseBool[index2] = true;
}
return accesses;
},
args: ["$accesses", index1, index2],
lang: "js"
}
}
}
}
]
);
For the last 9 hours I`m trying to fix one bug so I have code
let query = User.find();
if(req.query.from){
query = query.find({date: {$gte: req.query.from}});
}
if(req.query.to){
query = query.find({date: {$lte: req.query.to}});
}
const User = await query;
res.status(200).json({status:'Success', User})
And the problem is when I do for example
req.query.from = 1
req.query.to = 9
Everything works but when I do
req.query.from = 1
req.query.to = 11
It doesn't return anything.
Single digit numbers can be query but If I want to mix single digit with multi digit then doesn't work.
Can anyone help me solve this problem
Edit:
My user Schema
const userSchema= new mongoose.Schema({
date: {
type:String
},
title: {
type:String
},
description:{
type:String
},
location:{
type:String
}
});
The comparison query operators $gt, $gte, $lt and $lte do not generally work well with string digits as types in MongoDB are strict and do not auto-convert like they would do
in other systems. To compare against a number with $gt/$gte or $lt/$lte, the values in your document also need to be a number. Thus you have an option of changing
the schema design of the Date field to be an int or double.
For your case, since the date field is already of String type, you can utilise the $expr operator to convert the field within the query as it works well with aggregate operators such as $toInt or $toDouble. You would also need to parse the parameter values to integer for correct comparison with the query.
You would thus in the end require a query like this should you decide to stick with the existing schema:
const users = await User.find({
'$expr': {
'$and': [
{ '$gte': [{ '$toInt': '$date' }, 1 ] },
{ '$lte': [{ '$toInt': '$date' }, 11 ] }
]
}
})
The following query derivation returns the desired results with the existing conditions:
const toIntExpression = { '$toInt': '$date' }
const query = {
'$expr': {
'$and': []
}
}
if (req.query.from) {
const from = parseInt(req.query.from, 10)
query['$expr']['$and'].push({
'$gte': [toIntExpression, from]
})
}
if (req.query.to) {
const to = parseInt(req.query.to, 10)
query['$expr']['$and'].push({
'$lte': [toIntExpression, to]
})
}
const users = await User.find(query)
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;
});