How to find an index array inside the MongoDB document - mongodb

I have one collection with this document format:
{
"_id" : ObjectId("5c51fe3a6abdf0e5cd78f658"),
"0" : {
"id" : 1,
"name" : "carlos"
},
"1" : {
"id" : 2,
"name" : "foo"
},
"2" : {
"id" : 3,
"name" : "Jhon Doe"
},
"3" : {
"id" : 4,
"name" : "Max"
}
}
the only way to access the properties was doing a foreach loop and another for in inside.
db.getCollection('tutorial').find({}).forEach( (users) => {
for(user in users){
print("ID-> " + users[user].id, " Name->" + users[user].name);
}
});
But I can only print the results, there is another way to return a value using find ?
Thanks in advance.

You have to split up your operations. Get the collection first and then run a function to return the value you wish:
let collection = db.getCollection('tutorial').find({});
let name = () => {collection.forEach( (users) => {
for(user in users){
if(users[user].name == "foo"){
return ("ID-> " + users[user].id, " Name->" + users[user].name);
}
}
})};
or simply create a variable and set it when you run the loops
let name;
db.getCollection('tutorial').find({}).forEach( (users) => {
for(user in users){
name = ("ID-> " + users[user].id, " Name->" + users[user].name);
}
});
or something similar
Though all of these scenarios seem like a strange way to handle a mongo collection. I would definitely recommend restructuring your collection if you can't access the data using regular find method.

Related

How to print nested array using mongo query

I'm trying to print the values of an nested array.But getting execute script error.How do I print object BSON and avoid error in for nested array.
Note : I want to do with print and not find().
Customer schema
{
"name" : "Sam",
"phone" : [
{
"home" : "123456",
"work" : "045842"
}]}
query
db.getCollection('customer').find({}).forEach( function(cust)
{
print("Customer Name : " + cust.name); // prints Sam
print("Home Contact : " + cust.phone) // prints [object BSON]
print("Home Contact : " + cust.phone.home) // throws error
});
You could use aggregation if there are multiple items in the array
db.collectionName.aggregate([
{ $unwind: { path: "$phone", preserveNullAndEmptyArrays: true}},
]).forEach(function(doc){
print(doc.name)
if(doc.phone !== undefined) print(doc.phone.home)
if(doc.phone !== undefined) print(doc.phone.work)
})
You just need to convert the object to string and access the array;
print("Home Contact : " + JSON.stringify(cust.phone[0]))
// prints ` Home Contact: { "home" : "123456", "work" : "045842" }
print("Home Contact : " + cust.phone[0].home) // "123456"
An example:
aireclaimRs:PRIMARY> use test
switched to db test
aireclaimRs:PRIMARY> db.createCollection('customer')
{ "ok" : 1 }
aireclaimRs:PRIMARY> db.customer.insert( {
... "name" : "Sam",
... "phone" : [
... {
... "home" : "123456",
... "work" : "045842"
... }]})
WriteResult({ "nInserted" : 1 })
aireclaimRs:PRIMARY> db.getCollection('customer').find().forEach(function(cust){
... print("Customer Name : " + cust.name);
... print("Homes Contact : " + JSON.stringify(cust.phone[0]));
... print("Home Contact : " + cust.phone[0].home)
... })
Customer Name : Sam
Homes Contact : {"home":"123456","work":"045842"}
Home Contact : 123456

access id of a nested document in mongoose

I want to write a put method in express for a nested document in mongoose.
I cannot access the id for the nested document.
{ "_id" : ObjectId("5b8d1ecbb745685c31ad8603"),
"name" : "abc",
"email" : "abc#gmail.com",
"projectDetails" : [
{
"technologies" : [
"abc",
"abc"
],
"_id" : ObjectId("5b8d1ecbb745685c31ad8604"),
"projectName" : "abc",
"projectDescription" : "abc",
"manager" : "abc",
"mentor" : "abc"
}
],
"__v" : 0
}
I am trying to access the id ("5b8d1ecbb745685c31ad8604") so that I can update the projectName.
I cannot think of how to write a put method for the same. Please help! Thanks in advance!!
You can use model.findOne() and then save() th update the document instead of model.findOneAndUpdate().
var projectId = "5b8d1ecbb745685c31ad8604";
var newProjectName = "def";
model.findOne({'projectDetails._id': projectId}, (err, data) => {
if (data) {
data.projectDetails.forEach((project) => {
if (project._id == projectId) {
project.projectName = newProjectName;
}
});
data.save();
} else {
// throw error message
}
})
app.put('/api/project/:id',(request,response)=>{
const projectId = request.params.id;
const projectName = "test";
db.users.update({"projectDetails._id":projectId},{$set:{"projectDetails.$.projectName":projectName}},function(err,data){
if(data){
}else{
}
})})
Instead of forEach try above query

How to query MongoDb documents using the indices of embedded arrays

I am trying to learn how to use mongo queries to reach deep into a data tree. Specifically, I'm trying to remove the object below {"object": 'to remove'}
{
"_id" : ObjectId("7840f22736341b09154f7ebf"),
"username" : "nmay",
"fname" : "Nate",
"lname" : "May",
"data" : [
{
"monthNum" : 1,
"year" : 2016,
"days" : [
{
"date" : "2016-01-01T06:00:00.000Z",
"type1" : [],
"type2" : []
},
{
"date" : "2016-01-02T06:00:00.000Z",
"type1" : [
{"object": 'to remove'}
],
"type2" : []
}
]
}
]
}
so far I know how to query for the user _id, but I'm not sure how to remove the desired object using the indices in each array. In this example I want to remove data[0].days[1].type1[0]
Here is the query that I have so far:
app.delete('/user/:id/data/:monthIndex/days/:dayIndex/type1/:type1Index', function (req, res, next) {
var monthIndex = parseInt(req.params.monthIndex); // these console the value properly
var dayIndex = parseInt(req.params.dayIndex); // -1 is applied to the parameter to translate to array position
var type1Index = parseInt(req.params.type1Index);
db.users.update(
{ _id: mongojs.ObjectId(req.params.id) },
{ $pull: data.monthIndex.days.dayIndex.type1.type1Index }
);
}
It gives me the error
ReferenceError: data is not defined
Can someone demonstrate how I can pass this query my index parameters to remove the desired object?
Unfortunately, there is no way to remove an array element by its numerical index with a single operation in MongoDB. In order to do this, you need to unset desired element(s) first, and remove the resulting null-valued fields afterwards.
Your code should look something like this:
db.users.update(
{ _id : mongojs.ObjectId(req.params.id) },
{ $unset : { 'data.0.days.1.type1.0' : 1 } }
);
db.users.update(
{ _id : mongojs.ObjectId(req.params.id) },
{ $pull : { 'data.0.days.1.type1' : null } }
);
Edit by #bob: to pass in the parameters you have to build the query string, which is ugly:
var unset = {};
unset['$unset'] = {};
unset.$unset['data.' + req.params.monthIndex + '.days.' + req.params.dayIndex + '.foods.' + req.params.foodIndex] = 1;
db.users.update( { _id : mongojs.ObjectId(req.params.id) }, unset );
var pull = {};
pull['$pull'] = {};
pull.$pull['data.' + req.params.monthIndex + '.days.' + req.params.dayIndex + '.foods'] = null;
db.users.update( { _id : mongojs.ObjectId(req.params.id) }, pull );

Mongo nested query with keys

Need help on MongoDB nested query. Below is my mongo collection.
Preference collection
{
"_id" : "user123",
"preferences" : {
"product-1" : {
"frequency" : "Weekly",
"details" : {
"email" : {
"value" : "On"
}
}
},
"product-2" : {
"preferencesFor" : "mpc-other",
"preferencesForType" : "Product",
"details" : {
"email" : {
"value" : "Off"
}
}
},
"product-3" : {
"preferencesFor" : "mpc-other",
"preferencesForType" : "Product",
"details" : {
"email" : {
"value" : "On"
}
}
}
}
}
Product Collection
{
"_id" : "product-1",
"name" : "Geo-Magazine"
}
{
"_id" : "product-2",
"name" : "History-Magazine"
}
{
"_id" : "product-3",
"name" : "Science-Magazine"
}
product-1, product-2... are keys from a Map.
The keys are stored in another collection Product Collection.
Can I create a nested query to cross-reference the product keys from another table?
I need the output in the below table format. Please suggest.
user123 product-1 email On
user123 product-2 email Off
user123 product-3 email On
I tried the below but can't get result. Please suggest.
var cursor = db.productSummary.find();
while(cursor.hasNext()){
var sku = cursor.next()._id;
var skuCol = "preferences."+sku+".details.email";
var skuVal = "preferences."+sku+".details.email.value";
db.marketingPreferences.find( {}, {_id:1, skuCol:1, skuVal:1});
}
> var myCursor = db.productSummary.find();
> while(myCursor.hasNext()){
var sku = myCursor.next()._id;
var skuCol = "preferences."+sku+".details.email";
var skuVal = "$preferences."+sku+".details.email.value";
var result = db.marketingPreferences.aggregate([{"$project":{"_id":1,value:skuVal,preferences:{$literal: sku}}}],{allowDiskUse: true});
while(result.hasNext()){
printjson(result.next());
}
}
Result
{ "_id" : "user123", "preferences" : "product-1", "value" : "On" }
{ "_id" : "user123", "preferences" : "product-2", "value" : "Off" }
{ "_id" : "user123", "preferences" : "product-3", "value" : "On" }
There's a difference between MongoDB and normal SQL DB. Firstly, when you query a MongoDB collection, it doesn't return a row as it will in a SQL db. What you get here is a document similar to JSON.
Also when you use preferences.product-1.details.email : 1 it wont return you the word 'email', rather it will return you the value ie. {"value" : "On" }.
Using this: db.preference.find({},{"_id":1,"preferences.product1.details.email.value":1})
you will be able to get two details which are user123 and On and you can get product-1 from your previous query. You can store these values in a variable and keep printing them to obtain the table necessary. Also you would need another cursor to store the result of the second second query that you would do.
Here's what your query will produce if it was single standalone query:
> db.preference.find({},{"_id":1,"preferences.product1.details.email.value":1})
.pretty()
{
"_id": "user123",
"preferences": {
"product-1": {
"details": {
"email": {
"value": "On"
}
}
}
}
}
public static void test(){
MongoCollection<Document> collection = getDatadase().getCollection("product");
MongoCollection<Document> pref = getDatadase().getCollection("pref");
List<Document> allDocList = collection.find().into(new ArrayList<Document>());
for(Document doc:allDocList){
System.out.println(doc.get("_id"));
String preferences = doc.get("_id")+"";
String sku = "$preferences."+preferences+".details.email.value";
Document aggregation = new Document().append("$project", new Document().append("_id", 1).append("value", sku));
List<Document> pipeline = new ArrayList<Document>();
pipeline.add(aggregation);
List<Document> aggList = pref.aggregate(pipeline).into(new ArrayList<Document>());
for(Document doc1:aggList){
System.out.println(doc1.append("preferences", preferences));
}
}
}
This Will return
product-1
Document{{_id=user123, value=On, preferences=product-1}}
product-2
Document{{_id=user123, value=Off, preferences=product-2}}
product-3
Document{{_id=user123, value=On, preferences=product-3}}

mongoDB mapReduce: reduce function returning plain object instead of map

Is it possible to have a reduce function that returns a flat object instead of map-like thing?
More details:
db.getCollection('calls').mapReduce(function () {
emit(this.reportDate + '-' + this.reportTime, {
from: this.caller,
to: this.called,
callEnds: this.callEnds,
callBegins: this.callBegins,
location: this.location
});
}, function (k, v) {
var result = {};
v.forEach(function (value) {
result.from = value.from;
result.to = value.to;
result.callBegins = value.callBegins;
result.callEnds = value.callEnds;
if (value.location) {
result.location = value.location;
}
});
return result;
}, {
out: 'mapReducedCalls'
})
Using this, output collection's documents are all
{ "_id" : "k",
"value" :
{ "from" : "b5c06aafa4be00db3d6acadb67b6ceef",
"to" : "0afba72b041e3ccb5a62f0b0b44cceea",
"callEnds" : "01/03/2013 10:45:44",
"callBegins" : "01/03/2013 10:45:40",
"location" : 44763
}
}
While I'd rather have it in a flat object form like
{ "_id" : "k",
"from" : "b5c06aafa4be00db3d6acadb67b6ceef",
"to" : "0afba72b041e3ccb5a62f0b0b44cceea",
"callEnds" : "01/03/2013 10:45:44",
"callBegins" : "01/03/2013 10:45:40",
"location" : 44763
}
No, the 'value' field is currently required (assuming that's what you mean by map-like thing).