Return Array from Embedded document - node.js

I have a data like in mongodb collection name property.
{
"_id": "593a3d828e2ef100d1496e77",
"feature_type": "house",
"features": [
{
"name": "h1"
},
{
"name": "h2"
}
]
}
I want only
[
{
"name": "h1"
},
{
"name": "h2"
}
]
as result i tried this by
req.db.collection('FeatureSettings').findOne({feature_type: req.params.feature_type}, {features: 1});
this give
{
"_id": "593a3d828e2ef100d1496e77",
"features": [
{
"name": "Hotel"
},
{
"name": "Apartment"
}
]
}
what can i do to get result as given above.

You have to exclude the _id from your projection like here:
req.db.collection('FeatureSettings').findOne({feature_type: req.params.feature_type}, {features: 1, _id:0});

Assuming you're using something like mongoose that returns promises, you could use the result of the query
return req.db.collection('FeatureSettings')
.findOne({feature_type: req.params.feature_type}, {features: 1})
.then((result) => {
return result.features;
});

You can try this to return only features array values.
req.db.collection('FeatureSettings')
.findOne({feature_type: req.params.feature_type}, {features:1, _id:0}).features;
OR
// if you use mongoose can use
return req.db.collection('FeatureSettings')
.findOne({feature_type: req.params.feature_type}, {features:1, _id:0})
.exec()
.then((result) => {
return result.features;
});

Related

find mongoose matching items inside an array of objects

I have a mongoose schema with an array of objects...
Im trying to get only matching userID in the array, however the array return all the users if there is even one match.
I have the following on node js
app.get("/myanswers/:id", async (req, res) => {
try {
const q = await posts.find(
{ "postAnswer.userID": req.params.id },
"postAnswer.answer postAnswer._id postAnswer.user postAnswer.userID"
);
res.json(q);
} catch (error) {
res.send(error);
}
});
and i get this result:
[
{
"_id": "6074f2648e9f41497438b37a",
"postAnswer": [
{
"_id": "6074f3858e9f41497438b37b",
"userID": "60579272980cb93ea8a91140",
"user": "admin",
"answer": "Kakashi 🐱‍👤"
}
]
},
{
"_id": "6074f4768e9f41497438b37c",
"postAnswer": [
{
"_id": "6074f5338e9f41497438b37e",
"userID": "6074f4f98e9f41497438b37d",
"user": "barrym",
"answer": "here's the correct sequence. table, add row table headers close row. add row add table data close row close table."
},
{
"_id": "6074f5808e9f41497438b37f",
"userID": "60579272980cb93ea8a91140",
"user": "admin",
"answer": "with TH and TD!"
}
]
}
]
i only want exact match so user barrym shouldn't appear
Demo - https://mongoplayground.net/p/3BV58F3GiS4
Use $ (projection)
Note:- limits the contents of an to return the first element
The positional $ operator limits the contents of an to return the first element that matches the query condition on the array.
db.collection.find(
{ "postAnswer.userID": "6074f4f98e9f41497438b37d"},
{ "postAnswer.$": 1}
)

How to get count by array of values using MongoDB and Node.js

I need to get sum of count of document using MongoDB and Node.js. I am explaining my code and document below.
var finalOut=[
{
"location": "NEW DELHI",
"nos_of_fos": 15,
"login_id": [
"9619300317",
"9619300343",
"9619300338",
"9619300351",
"9619300322",
"9619300316",
"9619300323",
"9619300328",
"9619300341",
"9619300309",
"9619300310",
"9619300329",
"9619300353",
"9619300356",
"NORTH#oditeksolutions.com"
],
},
{
"location": "North West Delhi",
"nos_of_fos": 6,
"login_id": [
"9619300355"
],
}
]
The above is my input. I am explaining my code below.
finalOut.forEach(function(listItem, index){
Feedback.collection.aggregate([
{
$match: {
login_id: {
$in: listItem['login_id']
}
}
},
])
.toArray((cerr,cdocs)=>{
console.log(cdocs);
})
finalOut[index]['total_remarks']=cdocs;
})
Here I need the total count of all login_id arrays value which is present inside feedback document.
You can not use forEach for asynchronous operations:
Here I will use the async library but you can use any library that you like.
async.forEachOf(finalOut, (listItem, index, callback) => {
Feedback.collection.aggregate([
{
$match: {
login_id: {
$in: listItem['login_id']
}
}
},
{
$count: ‘theCount’
}
])
.toArray((cerr,cdocs)=> {
finalOut[index]['total_remarks']=cdocs.theCount;
callback();
})
}, err => {
// here you can access the filled `finalOut` object
});

Mongoose: Update does not work in nested array object

I have a document with the array of objects and one object contains multiple objects I want to update inner object with $set but didn't get any luck.
can anybody give me any hint so that I can resolve it?.
This is my object:
{
"_id": ObjectId("56fbfafdf86fa6161911d104"),
"site": "xyz",
"adsPerCategory": NumberInt(2),
"sampledAt": ISODate("2016-03-30T16:12:45.138+0000"),
"items": [
{
"id": "4563873",
"content": {
"title": "WATER DISTILLERS",
"body": "Perfect to save money.",
}
},
{
"id": "4563s23232873",
"content": {
"title": "Cola water",
"body": "Perfect for body.",
}
}
]
}
I want to update body.
for now, I have given single object but it can be multiple.
Here what I tried
models.Sample.update(
{
_id: samples._id
},
'$set': {
'items.0.content.body': body.description
},
function(err, numAffected) {
console.log(err);
console.log('Affected....', numAffected);
}
);
It's working fine if I put 0 but I want to make it dynamic.
Like 'items.index.content.body': body.description
Thank You.
I think you can do something like this.
models.Sample.find({ _id: ObjectId(samples._id) })
.forEach(function (doc) {
doc.items.forEach(function (element, index, array) {
items[index].content.body = body.description;
});
models.Sample.save(doc);
});

Mongoose multi update

I want to update multiple docs with different values.
My Database looks something like this.
[
{
"_id": 1,
"value": 50
},
{
"_id": 2,
"value": 100
}
]
This Query return an error because i'm passing an array instead of an object in the $set.
Model.update({_id: {$in: ids}}, {$set: ids.value}, {multi: true};
I want my database to look like this
[
{
"_id": 1,
"value": 4
},
{
"_id": 2,
"value": 27
}
]
Supposing you had an array of objects that you wanted to update in your collection on matching ids like
var soldItems = [
{
"_id": 1,
"value": 4
},
{
"_id": 2,
"value": 27
}
];
then you could use the forEach() method on the array to iterate it and update your collection:
soldItems.forEach(function(item)){
Model.update({"_id": item._id}, {"$set": {"value": item.value }}, callback);
});
or use promises as
var updates = [];
soldItems.forEach(function(item)){
var updatePromise = Model.update({"_id": item._id}, {"$set": {"value": item.value }});
updates.push(updatePromise);
});
Promise.all(updates).then(function(results){
console.log(results);
});
or using map()
var updates = soldItems.map(function(item)){
return Model.update({"_id": item._id}, {"$set": {"value": item.value }});
});
Promise.all(updates).then(function(results){
console.log(results);
});
For larger arrays, you could take advantage of using a bulk write API for better performance. For Mongoose versions >=4.3.0 which support MongoDB Server 3.2.x,
you can use bulkWrite() for updates. The following example shows how you can go about this:
var bulkUpdateCallback = function(err, r){
console.log(r.matchedCount);
console.log(r.modifiedCount);
}
// Initialise the bulk operations array
var bulkOps = soldItems.map(function (item) {
return {
"updateOne": {
"filter": { "_id": item._id } ,
"update": { "$set": { "value": item.value } }
}
}
});
// Get the underlying collection via the native node.js driver collection object
Model.collection.bulkWrite(bulkOps, { "ordered": true, w: 1 }, bulkUpdateCallback);
For Mongoose versions ~3.8.8, ~3.8.22, 4.x which support MongoDB Server >=2.6.x, you could use the Bulk API as follows
var bulk = Model.collection.initializeOrderedBulkOp(),
counter = 0;
soldItems.forEach(function(item) {
bulk.find({ "_id": item._id }).updateOne({
"$set": { "value": item.value }
});
counter++;
if (counter % 500 == 0) {
bulk.execute(function(err, r) {
// do something with the result
bulk = Model.collection.initializeOrderedBulkOp();
counter = 0;
});
}
});
// Catch any docs in the queue under or over the 500's
if (counter > 0) {
bulk.execute(function(err,result) {
// do something with the result here
});
}
First of all your update() query is not ok..
See documentation for this here
Model.update(conditions, update, options, callback);
Suppose your current db model contains docs like this (as you described in question as well):
[
{
"_id": 1,
"value": 50
},
{
"_id": 2,
"value": 100
}
];
and you've below array which contains objects (i.e., docs) to be modified with current db's docs to like this:
idsArray: [
{
"_id": 1,
"value": 4
},
{
"_id": 2,
"value": 27
}
];
From my experience with mongodb & mongoose, i don't think you can update all docs with single line query (that's you're trying to do).. (P.S. I don't know about that so I am not sure to this..)
But to make your code work, you will be doing something like this:
Idea: Loop over each doc in docs i.e, idsArray and call update() over it..
So, Here's code to this:
idsArray.forEach(function(obj) {
Model.update({_id: obj._id}, {$set: {value: obj.value}});
});
In above code, I am supposing you've _id values in db docs as they 're written above (i.e, "_id": 1).. But if they're like this "_id": ObjectId('1')
[
{
"_id": ObjectId('1'),
"value": 50
},
.....
.....
]
then you'll be need to convert _id to ObjectId(obj._id) in update() query..
so for that you'll be doing like this.
var ObjectId = require('mongodb').ObjectID;
idsArray.forEach(function(obj) {
Model.update({_id: ObjectId(obj._id)}, {$set: {value: obj.value}});
});
P.S. Just confirm it (i.e., _id) before go forward..
Hope this helps.
Cheers,
Multi update can be used only for updating multiple documents to the same value(s) or updating with the same update operation for all documents.
If you want to update to different values you have to use several update statements.

Using $in in MongooseJS with nested objects

I've been successfully using $in in my node webservice when my mongo arrays only held ids. Here is sample data.
{
"_id": {
"$oid": "52b1a60ce4b0f819260bc6e5"
},
"title": "Sample",
"team": [
{
"$oid": "52995b263e20c94167000001"
},
{
"$oid": "529bfa36c81735b802000001"
}
],
"tasks": [
{
"task": {
"$oid": "52af197ae4b07526a3ee6017"
},
"status": 0
},
{
"task": {
"$oid": "52af197ae4b07526a3ee6017"
},
"status": 1
}
]
}
Notice that tasks is an array, but the id is nested in "task", while in teams it is on the top level. Here is where my question is.
In my Node route, this is how I typically deal with calling a array of IDs in my project, this works fine in the team example, but obviously not for my task example.
app.get('/api/tasks/project/:id', function (req, res) {
var the_id = req.params.id;
var query = req.params.query;
models.Projects.findById(the_id, null, function (data) {
models.Tasks.findAllByIds({
ids: data._doc.tasks,
query: query
}, function(items) {
console.log(items);
res.send(items);
});
});
});
That communicates with my model which has a method called findAllByIds
module.exports = function (config, mongoose) {
var _TasksSchema = new mongoose.Schema({});
var _Tasks = mongoose.model('tasks', _TasksSchema);
/*****************
* Public API
*****************/
return {
Tasks: _Tasks,
findAllByIds: function(data, callback){
var query = data.query;
_Tasks.find({_id: { $in: data.ids} }, query, function(err, doc){
callback(doc);
});
}
}
}
In this call I have $in: data.ids which works in the simple array like the "teams" example above. Once I nest my object, as with "task" sample, this does not work anymore, and I am not sure how to specify $in to look at data.ids array, but use the "task" value.
I'd like to avoid having to iterate through the data to create an array with only id, and then repopulate the other values once the data is returned, unless that is the only option.
Update
I had a thought of setting up my mongo document like this, although I'd still like to know how to do it the other way, in the event this isn't possible in the future.
"tasks": {
"status0": [
{
"$oid": "52995b263e20c94167000001"
},
{
"$oid": "529bfa36c81735b802000001"
}
],
"status1": [
{
"$oid": "52995b263e20c94167000001"
},
{
"$oid": "529bfa36c81735b802000001"
}
]
}
You can call map on the tasks array to project it into a new array with just the ObjectId values:
models.Tasks.findAllByIds({
ids: data.tasks.map(function(value) { return value.task; }),
query: query
}, function(items) { ...
Have you try the $elemMatch option in find conditions ? http://docs.mongodb.org/manual/reference/operator/query/elemMatch/

Resources