Resolving MongoDB DBOef array using Mongo Native Query and working on resolved documents
My MongoDB collection consists of two main collections:
1) Maps
{
"_id" : ObjectId("542489232436657966204394"),
"fileName" : "importFile1.json",
"territories" : [
{
"$ref" : "territories",
"$id" : ObjectId("5424892224366579662042e9")
},
{
"$ref" : "territories",
"$id" : ObjectId("5424892224366579662042ea")
}
]
},
{
"_id" : ObjectId("542489262436657966204398"),
"fileName" : "importFile2.json",
"territories" : [
{
"$ref" : "territories",
"$id" : ObjectId("542489232436657966204395")
}
],
"uploadDate" : ISODate("2012-08-22T09:06:40.000Z")
}
2) Territories referenced by Map objects:
{
"_id" : ObjectId("5424892224366579662042e9"),
"name" : "Afghanistan",
"area" : 653958
},
{
"_id" : ObjectId("5424892224366579662042ea"),
"name" : "Angola",
"area" : 1252651
},
{
"_id" : ObjectId("542489232436657966204395"),
"name" : "Unknown",
"area" : 0
}
My goal is to list each map with their cumulative area and number of territories. I am trying to execute the following query:
db.maps.aggregate(
{'$unwind':'$territories'},
{'$group':{
'_id':'$fileName',
'numberOf': {'$sum': '$territories.name'},
'locatedArea':{'$sum':'$territories.area'}
}
})
However, the results show 0 for each of these values:
{
"result" : [
{
"_id" : "importFile2.json",
"numberOf" : 0,
"locatedArea" : 0
},
{
"_id" : "importFile1.json",
"numberOf" : 0,
"locatedArea" : 0
}
],
"ok" : 1
}
I probably did something wrong when trying to access Territory member variables (name and scope), but I couldn't find an example of such a case in the Mongo doc. the area is stored as an integer and the name as a string.
source to share
I probably did something wrong when I was trying to access the Territory member variables (name and scope), but I couldn't find an example of such a case in the Mongolian dock. the region is stored as an integer and the name as a string.
Yes, the field "territories"
has an array of database links and not the actual documents
. DBRefs
- objects that contain information with which we can locate the actual documents
.
In the above example, you can clearly see this, fire below mongo request:
db.maps.find({"_id":ObjectId("542489232436657966204394")}).forEach(function(do
c){print(doc.territories[0]);})
it will print the DBRef object, not the document itself:
o/p: DBRef("territories", ObjectId("5424892224366579662042e9"))
therefore '$sum': '$territories.name'
, '$sum': '$territories.area'
will show you "0" since there are no fields like name
or area
.
So, you need to resolve this doc link before doing something like $territories.name
To achieve what you want, you can use a function map()
as the aggregation and ancillary queries support Map-less and you already have a standalone document map
with links to it territories
.
Steps to Achieve:
a) get each map
b) resolve the `DBRef`.
c) calculate the total area, and the number of territories.
d) make and return the desired structure.
Mongolian shell script:
db.maps.find().map(function(doc) {
var territory_refs = doc.territories.map(function(terr_ref) {
refName = terr_ref.$ref;
return terr_ref.$id;
});
var areaSum = 0;
db.refName.find({
"_id" : {
$in : territory_refs
}
}).forEach(function(i) {
areaSum += i.area;
});
return {
"id" : doc.fileName,
"noOfTerritories" : territory_refs.length,
"areaSum" : areaSum
};
})
o / p:
[
{
"id" : "importFile1.json",
"noOfTerritories" : 2,
"areaSum" : 1906609
},
{
"id" : "importFile2.json",
"noOfTerritories" : 1,
"areaSum" : 0
}
]
Map-Reduce
functions should not and cannot be used for DBRefs
server side resolution . See what the documentation has to say:
The map function shouldn't access the database for any reason.
The map function must be pure or have no effect outside the function (i.e. side effects).
The shrink function does not have to access the database, even to perform a read operation. The reduction function should not affect the external system.
Also, the function reduce
, even if used (which may never work anyway), will never be called for your problem, as the wrt "fileName"
or group "ObjectId"
will always only have one document in your dataset.
MongoDB will not call a decrement function on a key that only has a single value
source to share