mongodbscalacasbah

Relate children to parent using Mongo aggregate query


I have a single collection named assets that contains documents in 2+ formats, ParentObject and ChildObject. I am currently associating ParentObject to ChildObject with two queries. Can this be done with an aggregate query?

ParentObject

{
    "_id" : {
        "oid" : "ParentFooABC",
        "brand" : "acme"
    },
    "type": "com.ParentClass",
    "title": "Title1234",

    "availableDate": Date,
    "expirationDate": Date
}

ChildObject

{
    "_id" : {
        "oid" : "ChildFoo",
        "brand" : "acme"
    },
    "type": "com.ChildClass",
    "parentObject": "ParentFooABC",
    "title": "Title1234",
    "modelNumber": "8HE56",
    "modelLine": "Metro",
    "availableDate": Date,
    "expirationDate": Date,
    "IDRequired": true
}

Currently I filter data like this

val parent = db.asset.find(MongoDBObject("_id.brand": MongoDBObject($eq: "acme")),MongoDBObject("type":"com.ParentClass"))
val children = db.asset.find(MongoDBObject("_id.brand": MongoDBObject($eq: "acme")),MongoDBObject("type":"com.ChildClass"), MongoDBObject("parentObject": "${parent._id.oid}"))
if(childs.nonEmpty) {
  //I have confirmed this parent has a child associated and should be returned
  val childModelNumbers = childs.map(child -> child.modelNumber)
  val response = ResponseObject(parent, childModelNumbers)
}

Can I do this in an aggregate query?

Updated:

Mongo Version: db version v2.6.11

Language: Scala

Driver: Casbah 2.8.1


Solution

  • Technically yes, however what you're doing now is standard practice with mongodb. If you need to join collections of data frequently you probably should use an RDBMS. However if you occasionally need to aggregate data from 2 separate collection then there is $lookup. In general you'll find yourself populating data into your document from another document pretty frequently, but that's typically how a document database works. Using $lookup when you're really looking for an equivalent to a SQL JOIN isn't something I would recommend. It's not really the intended purpose, and you'd be better off doing exactly what you're doing now or moving to an RDBMS.

    Addition:

    Since the data is in the same collection you can use an aggregation to $group that data together. You just have to group them based on _id.brand.

    A quick example(in mongodb shell):

    db.asset.aggregate([
    {  //start with a match to narrow down
       $match: {"_id.brand": "acme"}
    },
    { //group by `_id.brand`
       $group: {
          _id: "$_id.brand",
          "parentObject": {$first: "$parentObject"},
          title: {$first: "$title"},
          modelNumer: {$addToSet: "$modelNumer"}, //accumulate child model # as array
          modelLine: {$addToSet: "$modelLine"}, //accumulate child model line as array
          availableDate: {$first: "$availableDate"},
          expirationDate: {$first: "$expirationDate"}
       }
    }
    ]);
    

    This should return documents where all the children's properties have been grouped into the parent document as an array(modelNumber,modelLine). It's probably better to do what you're doing above, it might even be better yet if you put the children into their own collection instead of keeping track of their type with a type field in the document. This way you know that the documents in each collection represent a given data structure. However, if you were to do that you would not be able to perform the aggregation in the example.