I am not sure whether the title explains what I try to say.
But here assume collection's structure is like;
[
{email: "alex#sda.com"},
{email: "elizabeth#sds.com"},
{email: "hannah#xx.com"},
]
I want to get emails but before the # character.
What have I done to achieve this?
db.collection.find({}).toArray(function(err, result){
var emails = [];
for(var i =0; i< result.length; i++){
emails.push(result[i].split("#")[0]);
}
})
But I don't find this efficient because after the query, I loop through the result and store them in new array.
Is there a better way to get only the alex, elizabeth, hannah parth with a query?
This is just an example. I want to ask your suggestions because I have lots of situations like this. (I am looking for a most efficient way to trim, update some values in the collections)
You can use the distinct method first to get the list of email addresses then use regex to get the names:
db.collection.distinct("email", function(err, result){
var emails = result.map(function(email){
return email.match(/^([^#]*)#/)[1];
});
console.log(emails);
});
With the aggregation framework, MongoDB 3.4 has a $split operator that you can use in conjunction with $arrayElemAt:
db.collection.aggregate([
{
"$group": {
"_id": null,
"emails": {
"$push": {
"$arrayElemAt": [
{ "$split": ["$email", "#"] },
0
]
}
}
}
}
], function(err, result){
if (err) throw err;
console.log(result);
/*
{
"_id" : null,
"emails" : [
"alex",
"elizabeth",
"hannah"
]
}
*/
});
Related
I am trying to find specific fields for more than one value. For example, I have a database with different countries and I am trying to retrieve their name, year, and nominalGDP (renamed to y in the result for some other important reason). It works perfect for this example, where I am only retrieving from USA, but how would I add another country like China or whatever?
Country.aggregate([
{
$match: {
name: "USA"
}
},
{
$project: {
_id: 0,
name: 1,
year: 1,
'y' : '$nominalGDP'
}
}
], function(err, recs){
if(err){
console.log(err);
} else {
console.log(recs);
}
});
This is probably really simple but I have not been able to find out how.
Use $in operator to specify more than one matching option. For example:
{
$match: {
name: { $in: [ "USA", "China" ] }
}
}
`const results = await SchemaName.find([{$match:{name:{$in:["USA","China"]}}}])
res.status(200).json(results);`
but if you are getting the country names from frontend through the body or something then:
` const allCountries = req.body.allCountries;
var result;
for(let i=0; i < allCountries.length; i++){
result = await SchemaName.find([{$match:{name:{$in:allCountries[i]}}}])
}
`
assuming you are building asynchronous functions...
I have to mongodb collections, like this:
UserGroup collection
fields:
name: String
group_id: Number
User collection
fields:
user_name: String
group_id: Number
I want generate a report like this:
ADMINISTRATORS
--------------------------
jlopez
rdiaz
OPERATORS
--------------------------
amiralles
dcamponits
But I get the following report:
ADMINISTRATORS
--------------------------
OPERATORS
--------------------------
jlopez
rdiaz
amiralles
dcamponits
Following is the code to generate the report:
UserGroup.find({}, (err, groups) => {
for(var i in groups){
console.log(groups[i].name)
console.log("--------------------")
User.find( {group_id : groups[i].group_id}, (err, users) =>{
for(var j in users){
console.log(users[j].user_name)
}
})
}
})
Clearly, this is a problem of the NodeJs/Mongoose asynchronicity.
QUESTION: How do I make the first For cycle wait until the internal cycle ends for each UserGrop?
Thanks in advance,
David.
You can run an aggregation pipeline that uses $lookup to do a "left-join" to another collection in the same database to filter in documents from the "joined" collection for processing. With this you won't need any async library:
UserGroup.aggregate([
{
"$lookup": {
"from": "users",
"localField": "group_id",
"foreignField": "group_id",
"as": "users"
}
},
{
"$project": {
"_id": 0,
"name": 1,
"users": {
"$map": {
"input": "$users",
"as": "user",
"in": "$$user.user_name"
}
}
}
}
], (err, groups) => {
if (err) throw err;
console.log(JSON.stringify(groups, null, 4));
})
Sample Output
[
{
"name": "ADMINISTRATORS",
"users": ["jlopez", "rdiaz"]
},
{
"name": "OPERATORS",
"users": ["amiralles", "dcamponits"]
}
]
Add support for promises to mongoose. I use q, but you can use bluebird too.
mongoose.Promise = require('q').Promise;
Then you can use q.all to resolve once all of the user queries have completed.
var promises = [];
var groups = [];
UserGroup.find({}, (err, groups) => {
for(var i in groups){
groups.push(groups[i]);
promises.push(User.find( {group_id : groups[i].group_id}));
}
});
q.all(promises).then( function(usersByGroup){
var indx = 0;
usersByGroup.forEach(function(users){
var grp = groups[indx];
console.log(groups[i].name);
console.log("--------------------");
for(var j in users){
console.log(users[j].user_name)
}
indx++;
});
});
This is a good use case for asyc, you can get a get basic idea from following code. it is based on async each & waterfall. [ Please add proper error handling for the following code yourself.]
UserGroup.find({}, (err, groups) => {
async.each(groups, (group, callback) =>{
async.waterfall([
(wCallback) => {
User.find({group_id : group.group_id}, wCallback)
},
(users, wCallback) => {
console.log(group.name)
console.log("--------------------")
for(var j in users){
console.log(users[j].user_name)
}
wCallback()
}
], callback)
})
})
I'm having a tough time coming up with a solution to update multiple documents with different values.
I have an app that makes sales for every sold item I want to reduce the quantity in my database by one.
var soldItems =
[
{
"_id": "1"
"name": "Foo",
"price": 1.09
},
{
"_id": "2",
"name": "Bar",
"price": 2.00
}
]
var ids = [];
soldItems.forEach(function(item){
ids.push(item._id);
});
I'm collecting all the ids in my soldItems array of objects.
Now I want to know how many items quantity I have in the database and then reduce the number quantity by one.
ModelItem.find({_id: {$in: ids}}, function(err, docs){
if(err) throw err;
docs.forEach(function(doc){
soldItems.forEach(function(item){
if(doc._id === item._id){
doc.quantity += -1;
}
});
});
Item.update({_id: {$in: ids}}, {$set: docs }, function(err){
if(err) throw err;
});
});
Obviously this is wrong because $set is passing in array instead of an object.
I want to know how can I reduce the quantity by one for each item in my database, but I the same time I don't want to go below 0 items in the database.
I'm sure im looking at this from the wrong angle.
Thanks.
Use the $inc operator instead, and the multi options:
Item.update({_id: {$in: ids}, quantity: {$gt: 0}}, // selection
{$inc: {quantity: -1}}, // modifications
{multi: true}); //options
Hello all i have an array of items now i want to know which of them are present in mongoDB now for that i read that i should use aggregations now i am using aggregations but it is not returning anything can anyone please point out what am i doing wrong below is my query given
userfavouritemodel.aggregate([
{ "$match": { "offers": {"$in": my_array}, "mobile_no":"1234567890" } },
{ $unwind: "$offers" },
{ "$group" : { "_id":"$_id","matched_elements":{ "$addToSet": "$offers" }}}
], function (err, result) {
if (err) {
console.log(err);
return;
}
console.log('user favourites');
console.log(result);
});
UPDATE- Example of documents in my database
{ "_id" : "566d6add9384223014ebcf43" , "mobile_no" : "1234567890" , "total_offers" : 4 , "offers" : [ "565ae5d8fff110dc18718b7c" , "565ae479fff110dc18718b7a" , "565ae479fff110dc18718b7a" , "5644a9339bf660501f15254e"]}
Here you go, this will return the offers that matches the mobile number and the offers within the array:
db.collection.aggregate([
{$match:{mobile_no:"1234567890"}},
{$project:{_id:0, offers:1}},
{$unwind:"$offers"},
{$match:{offers:{$in:["565ae479fff110dc18718b7a", "565ae5d8fff110dc18718b7c"]}}}]);
I'm looking to write a find clause that will search for objects where a certain attribute exactly matches a condition or if the object doesn't have that attribute at all.
Currently I am using:
Card.find( { $or: [ { "playerClass": req.params.className }, { "playerClass": {'$eq': null } } ] }, function(err, docs) {
res.json(docs);
});
But this yields no results.
I tried $equals before as well to no avail -- as a newcomer to Mongo, what am I doing wrong?
Thanks
null counts for missing, so you could use $in:
Card.find({ "playerClass" : { "$in" : [null, req.params.className] } }, callback)