So I'm using NodeJS to query MongoDB (4.4). I'm trying to figure how to search for a field inside an object inside a document and retrieve the object (or at least the _id). The field I'm querying by is the created field within the transactions document. How the table looks like is bellow.
I tried:
const result = await Model.findOne({}, { created: createdDate });
Didn't work. Never worked with these kinds of DB and am a bit lost. Any help is appreciated.
Maybe something like this:
Option 1: ( Find )
db.collection.find({
"transactions.created": "2022-12-21"
},
{
transactions: {
$elemMatch: {
created: "2022-12-21"
}
}
})
Explained:
Find the document based on "transaction.created".
Project only the matching "transaction.created" object.
playground1
Option 2: (Aggregation)
db.collection.aggregate([
{
$match: {
"transactions.created": "2022-12-21"
}
},
{
$addFields: {
transactions: {
"$filter": {
"input": "$transactions",
"as": "t",
"cond": {
$eq: [
"$$t.created",
"2022-12-21"
]
}
}
}
}
}
])
Explained:
Find the document based on transaction.created
Filter only the matched transaction created object.
playground2
For best performance index need to be created on the "transaction.created" field.
I have this aggregate in Python working without 0 issues
mycol.aggregate([
{
'$match': {
'Extention': '100',
'FinalUnicode': {
'$ne': ''
}
}
}
I am now working with NodeJS and Express. I have this so far but can't seem to give it the FinalUnicode filter. Currently it works by returning the Extentions that are equal to 100.
query = { "Extention": { $eq: filters["ext"] } }
You can use find() like this:
mycol.find({ Extention: '100', FinalUnicode: { $ne: '' }});
Or you can literally copy/paste aggregation code that you have in Python.
For example, if we have a Product variant scenario in which one product can have many variants. If we want to update the product and its variants, the request which comes from the client application is something like the below, and we don't know which one of the variants in the variants array has been changed by the client so we must update all the variants array in every request to update how to handle this kind of situation with "mongoose, MongoDB" update function or bulk write update operations?
Note: the product and variants are different documents and I want to update the variants in a single operation
{
"_id": "5ef4f34dd43f52cff62459aa",
"brand":"5cc9aeeaa3918f2e43e046af",
"isActive":true,
"name":"Product V",
"supplier":"5ccb24fc3760713f4d9dbd1b",
"supplierCode":"12343212234r2",
"tags":[
"5cd90de9f374d90ed569ab35"
],
"type":"5ccad8a1efe8713bc1cac566",
"variants":[
{
"_id":"5ef4fad0e0e5dfd29e188811",
"retailMarkup":0,
"retailPrice":0,
"sku":"T001-W-M",
"inventory":[
{
"storeId":"5ef4fad0e0e5dfd29e188823",
"quantity":0
}
]
},
{
"_id":"5ef4fad0e0e5dfd29e188811",
"retailMarkup":0,
"retailPrice":0,
"sku":"T001-W-M",
"inventory":[
{
"storeId":"5ef4fad0e0e5dfd29e188823",
"quantity":0
}
]
}
]
}
As discussed in the chat, this will suit your requirement
var bulk = db.variants.initializeUnorderedBulkOp();
bulk.find( { _id: <variant_id_1> } ).update( { $set: <variant_doc_1>});
bulk.find( {_id: variant_id_2 } ).update( { $set: <variant_doc_2> } );
.....
bulk.execute();
I'm trying to find a better performing solution maybe where you don't take the whole string array from collection and instead just return the count. Basically the countDocuments equivalent for a string array in one user collection.
I have a solution that works, but if that list gets really long then it won't be as good performance-wise.
User.findById(
{ _id: req.params.id }, { _id: 0 })
.select("blacklistGroup").then((result) => {
let output = JSON.parse(JSON.stringify(result.blacklistGroup));
console.log(output.length + " + people");
});
I was hoping to get an example of a more efficient solution more aligned with countDocuments, but for string array. I appreciate the help!
Here is the model definition of blacklistGroup
blacklistGroup: [String]
collection example
"blacklistGroup": [
"5e98e8e785e69146f841d239",
"5e9867ff5e72550988820dd3",
"5e98e90d85e69146f841d23a",
"5e98c950f4fb3f63b4634e30",
"5e99fcf3a506cf570056898b",
"5e99fd15a506cf570056898c",
"5e99fd3aa506cf570056898d",
"5e99fd64a506cf570056898e",
"5e99fda5a506cf570056898f"
]
Try This One:
User.aggregate([
{
$match: { _id: req.params.id }
},
{
$project: { blacklistGroupLength: { $size: '$blacklistGroup' } }
}
])
I've found something from MongoDB docs that might help you. Here is the link
If I have this schema...
person = {
name : String,
favoriteFoods : Array
}
... where the favoriteFoods array is populated with strings. How can I find all persons that have "sushi" as their favorite food using mongoose?
I was hoping for something along the lines of:
PersonModel.find({ favoriteFoods : { $contains : "sushi" }, function(...) {...});
(I know that there is no $contains in mongodb, just explaining what I was expecting to find before knowing the solution)
As favouriteFoods is a simple array of strings, you can just query that field directly:
PersonModel.find({ favouriteFoods: "sushi" }, ...); // favouriteFoods contains "sushi"
But I'd also recommend making the string array explicit in your schema:
person = {
name : String,
favouriteFoods : [String]
}
The relevant documentation can be found here: https://docs.mongodb.com/manual/tutorial/query-arrays/
There is no $contains operator in mongodb.
You can use the answer from JohnnyHK as that works. The closest analogy to contains that mongo has is $in, using this your query would look like:
PersonModel.find({ favouriteFoods: { "$in" : ["sushi"]} }, ...);
I feel like $all would be more appropriate in this situation. If you are looking for person that is into sushi you do :
PersonModel.find({ favoriteFood : { $all : ["sushi"] }, ...})
As you might want to filter more your search, like so :
PersonModel.find({ favoriteFood : { $all : ["sushi", "bananas"] }, ...})
$in is like OR and $all like AND. Check this : https://docs.mongodb.com/manual/reference/operator/query/all/
In case that the array contains objects for example if favouriteFoods is an array of objects of the following:
{
name: 'Sushi',
type: 'Japanese'
}
you can use the following query:
PersonModel.find({"favouriteFoods.name": "Sushi"});
In case you need to find documents which contain NULL elements inside an array of sub-documents, I've found this query which works pretty well:
db.collection.find({"keyWithArray":{$elemMatch:{"$in":[null], "$exists":true}}})
This query is taken from this post: MongoDb query array with null values
It was a great find and it works much better than my own initial and wrong version (which turned out to work fine only for arrays with one element):
.find({
'MyArrayOfSubDocuments': { $not: { $size: 0 } },
'MyArrayOfSubDocuments._id': { $exists: false }
})
Incase of lookup_food_array is array.
match_stage["favoriteFoods"] = {'$elemMatch': {'$in': lookup_food_array}}
Incase of lookup_food_array is string.
match_stage["favoriteFoods"] = {'$elemMatch': lookup_food_string}
Though agree with find() is most effective in your usecase. Still there is $match of aggregation framework, to ease the query of a big number of entries and generate a low number of results that hold value to you especially for grouping and creating new files.
PersonModel.aggregate([
{
"$match": {
$and : [{ 'favouriteFoods' : { $exists: true, $in: [ 'sushi']}}, ........ ] }
},
{ $project : {"_id": 0, "name" : 1} }
]);
There are some ways to achieve this. First one is by $elemMatch operator:
const docs = await Documents.find({category: { $elemMatch: {$eq: 'yourCategory'} }});
// you may need to convert 'yourCategory' to ObjectId
Second one is by $in or $all operators:
const docs = await Documents.find({category: { $in: [yourCategory] }});
or
const docs = await Documents.find({category: { $all: [yourCategory] }});
// you can give more categories with these two approaches
//and again you may need to convert yourCategory to ObjectId
$in is like OR and $all like AND. For further details check this link : https://docs.mongodb.com/manual/reference/operator/query/all/
Third one is by aggregate() function:
const docs = await Documents.aggregate([
{ $unwind: '$category' },
{ $match: { 'category': mongoose.Types.ObjectId(yourCategory) } }
]};
with aggregate() you get only one category id in your category array.
I get this code snippets from my projects where I had to find docs with specific category/categories, so you can easily customize it according to your needs.
For Loopback3 all the examples given did not work for me, or as fast as using REST API anyway. But it helped me to figure out the exact answer I needed.
{"where":{"arrayAttribute":{ "all" :[String]}}}
In case You are searching in an Array of objects, you can use $elemMatch. For example:
PersonModel.find({ favoriteFoods : { $elemMatch: { name: "sushiOrAnytthing" }}});
With populate & $in this code will be useful.
ServiceCategory.find().populate({
path: "services",
match: { zipCodes: {$in: "10400"}},
populate: [
{
path: "offers",
},
],
});
If you'd want to use something like a "contains" operator through javascript, you can always use a Regular expression for that...
eg.
Say you want to retrieve a customer having "Bartolomew" as name
async function getBartolomew() {
const custStartWith_Bart = await Customers.find({name: /^Bart/ }); // Starts with Bart
const custEndWith_lomew = await Customers.find({name: /lomew$/ }); // Ends with lomew
const custContains_rtol = await Customers.find({name: /.*rtol.*/ }); // Contains rtol
console.log(custStartWith_Bart);
console.log(custEndWith_lomew);
console.log(custContains_rtol);
}
I know this topic is old, but for future people who could wonder the same question, another incredibly inefficient solution could be to do:
PersonModel.find({$where : 'this.favouriteFoods.indexOf("sushi") != -1'});
This avoids all optimisations by MongoDB so do not use in production code.