$elemMatch on array of objects - mongodb

My schema has 6 attributes of type Array, which can each contain objects, like this:
alpha : Array
0 : Object
linkedID : "62495a66fb140b240476d8ff"
verified : false
1 : Object
linkedID : "62494fd789291c4f58bdad86"
verified : true
...
I want to write a query that returns a document where at least one of these Arrays contains an object that has the attribute verified == false. I've tried with $elemMatch:
myModel.findOne(
{$or: [
{ alpha: { $elemMatch: { verified: false } } },
{ beta: { $elemMatch: { verified: false } } },
{ gamma: { $elemMatch: { verified: false } } },
{ delta: { $elemMatch: { verified: false } } },
{ epsilon: { $elemMatch: { verified: false } } },
{ zeta: { $elemMatch: { verified: false } } }
] }
)
But this didn't work. I think this may be because it's a nested object, but I have no idea how to solve this. I'd really appreciate any advice.

Related

Conditional operator in mongoose

I am trying to conditionally execute two different mongoose operators but it just return no error and doesn't work to update the document.
My Schema:
const CartSchema: Schema<Document<ICart>> = new Schema({
clientID: { type: String, required: true },
sourceID: { type: String, required: true },
items: { type: Array },
source: { type: String, required: true },
}, { collection: "carts", timestamps: true });
The way I am trying to implement that:
await CartModel.findOneAndUpdate(
{ sourceID: clientID, 'items.id': Types.ObjectId(itemID) },
{
$cond: {
if: {
$eq: 1,
},
then: { $pull: { 'items.$.id': Types.ObjectId(itemID) }},
else: { $inc: { 'items.$.amount': -1 }},
}
},
{ new: true }
).lean({ virtuals: true })
And I also tried to have this kind of query: { sourceID: clientID } but it didn't help. I thought maybe I could not find the element and it just silently pass through.
The main idea here of what I am gonna do is - have a conditional mongoose request where I'll either remove the object from the array if the current value in the field amount is equal to 1, or decrement the value to -1.
Apparently, the $cond operator works only in the aggregation framework but in my Atlas tier I cannot check if the query works properly, but I suppose it should look something like this:
await CartModel.aggregate([
{ $match: {
sourceID: clientID,
'items.id': Types.ObjectId(itemID)
}
},
{
$cond: {
if: {
$eq: 1,
},
then: { $pull: { 'items.$.id': Types.ObjectId(itemID) }},
else: { $inc: { 'items.$.amount': -1 }},
}
}
])

How can i limit each ID i pass to $in operator inside the $match stage to only 4 elements

I have an aggregate like this :
const files = await File.aggregate([
{
$match: { facilityId: { $in: facilities } }
},
{
$sort: { createdAt: 1 }
},
{
$project: {
file: 0,
}
}
])
And i would like to have each "facility" return only 4 files, i used to do something like facilities.map(id => query(id)) but id like to speed things up in production env.
Using $limit will limit the whole query, that's not what i want, i tried using $slice in the projection stage but got en error :
MongoError: Bad projection specification, cannot include fields or add computed fields during an exclusion projection
how can i achieve that in a single query ?
Schema of the collections is :
const FileStorageSchema = new Schema({
name: { type: String, required: true },
userId: { type: String },
facilityId: { type: String },
patientId: { type: String },
type: { type: String },
accessed: { type: Boolean, default: false, required: true },
file: {
type: String, //
required: true,
set: AES.encrypt,
get: AES.decrypt
},
sent: { type: Boolean, default: false, required: true },
},
{
timestamps: true,
toObject: { getters: true },
toJSON: { getters: true }
})
And i would like to returns all fields except for the file fields that contains the encrypted blob encoded as base64.
Also: i have the feeling that my approach is not correct, what i really would like to get is being able to query all facilityId at once but limited to the 4 latest file created for each facility, i though using an aggregate would help me achieve this but im starting to think it's not how its done.
From the question the schema is not clear. So I have two answers based on two Schemas. Please use what works for you
#1
db.collection.aggregate([
{
$match: {
facilityId: {
$in: [
1,
2
]
}
}
},
{
$group: {
_id: "$facilityId",
files: {
$push: "$file"
}
}
},
{
$project: {
files: {
$slice: [
"$files",
0,
4
],
}
}
}
])
Test Here
#2
db.collection.aggregate([
{
$match: {
facilityId: {
$in: [
1,
2
]
}
}
},
{
$project: {
facilityId: 1,
file: {
$slice: [
"$file",
4
]
}
}
}
])
Test Here

Mongodb double query

I have following MongoDb collection:
{
"id":1,
"isBig": true } {
"id":2,
"isBig": true } {
"id":3,
"isBig": true } {
"id":4,
"isBig": true } {
"id":5,
"isBig": false } {
"id":6,
"isBig": false } {
"id":7,
"isBig": false }
I'd like to choose four random items: 2 should be big and 2 not. My attempt:
db.aggregate([
{ $match: { isBig: true } },
{ $sample: { size: 2 } }
]).toArray()
This choose only two big. I would need also two not big. COndition is I can access database only one time.

MongoDB aggregation to check if an element is contained in an array

I am new to MongoDB, my native language is Spanish. So I'm not sure how to find what I need.
I will try to make myself understandable.
my schema:
let noticiaSchema = new Schema({
titulo: {
type: String,
required: [true, 'El titulo de la noticia es requerido']
},
cuerpo_noticia: {
type: String,
required: [true, 'El cuerpo de la noticia es necesario']
},
publicacion_noticia: {
type: Date,
default: new Date()
},
actualizacion_noticia: {
type: Date,
default: new Date()
},
likes: {
type: Array,
default: []
},
foto: {
type: String
},
dislikes: {
type: Array,
default: []
}
})
Sample Doc :
{
"_id" : ObjectId("5e81868faf9d6e084cc60cc8"),
"titulo" : "fdsfsfs",
"cuerpo_noticia" : "fdsfsfs",
"actualizacion_noticia" : ISODate("2020-03-30T05:41:35.144Z"),
"foto" : "14335143.png",
"publicacion_noticia" : ISODate("2020-03-30T05:41:12.997Z"),
"likes" : [
"5e7ffb641650a326dcc1e1c7"
],
"dislikes" : [],
"__v" : 0
}
I'm basically trying to query for an array of elements called likes, if an element is contained in this array I would want to return true / false on a new field.
Below is what I've tried, but it only returns the documents where the element exists in likes.
//5e7ffb641650a326dcc1e1c7 element to search
Noticia.aggregate([
{
$match: {
"likes": { "$in": ["5e7ffb641650a326dcc1e1c7"] },
//likes is the array field with elements to search
}
},
{
$project: {
titulo: "$titulo"
}
}
], (err, trans) => {
})
I want it to return all my docs but with a field that tells me if this element is contained or not.
Finally, I want to know if there is a way to return the result by creation date, that is .sort {_id: -1}
If you wanted to return all docs, you should not be using $match, try below aggregation :
db.collection.aggregate([
/** Add a field across all docs by checking on specific condition */
{
$addFields: {
elementExists: {
$cond: [{ $in: ["5e7ffb641650a326dcc1e1c7", "$likes"] }, true, false]
}
}
},
{
$sort: {
_id: -1 // Sort by '_id' field in descending order
}
}
]);
Test : mongoDB-Playground

How to update all array value in document at once

Actually am trying to update an array in my document which have read property is set to false
here what i have done so far.
const user = await User.update(
{
_id: req.userData.id
},
{
$set: {
"notifications.$[elem].read": true
},
arrayFilters: [
{
"elem.read": false
}
], multi : true
}
);
but am getting an error
(node:12572) UnhandledPromiseRejectionWarning: MongoError: No array filter found for identifier 'elem' in path 'notifications.$[elem].read'
arrayFilters should be defined in options.
Try below code:
const user = await User.update(
{
_id: req.userData.id
},
{
$set: {
"notifications.$[elem].read": true
}
},
{
arrayFilters: [
{
"elem.read": false
}
],
multi : true
}
);