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
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 }},
}
}
])
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
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.
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
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
}
);