0

I have already gone through this popular question. None of the described solutions really solve my problem. I have been going through other posts too, in vain.

I want to find specific projected fields (The projected fields could be part of arrays) of documents with query which consists of conditions on objects at various depths of nested arrays. The nesting of arrays in my documents could go up to 5 levels. I am using $match, $and for combining all my conditions and $elemMatch for nested arrays. The querying part by itself works fine and it finds the right document. The problem however is while projecting the required fields (Some of these fields belong to deeply nested arrays)

Solutions that do not work for my situation and why :

  1. $filter : Available from version 3.2. I am using version 3.0. $filter is not an option.
  2. $unwind : My nested arrays (Array3 and Array6 in my example below) has 8000 array elements. By definition of $unwind, it will create a stream of separate documents with data duplicated. This is not something we would want to do as the documents are pretty big and duplication, thousands of times does not seem like a good idea. If I am missing something with $unwind please let me know.
  3. $elemMatch and $ operator : Both return only one matched object in the array. I want multiple objects in the array that matched certain conditions to be projected.
  4. $redact : I tried the approach described in this answer. Not sure if it is the right approach to take in nested arrays. For simple examples with no nested arrays, it worked (to some extent)

Below is an example document, expected results and some things which came close to solving the issue but not quite.

Example document (Actual document in my collection. Names changed and array elements reduced. I am not making this up. The data I am working with is really this nested. Also the innermost arrays could stretch to have 8000 elements) :

{
"Name" : "fa191a58-3c8b-4533-9545-3379e996c0b7", 
"Type" : "67448a7f-c514-4dc4-9836-4be6f54572a7", 
"DataObj" : { 
    "Community" : [
        {
            "Name" : "5e6ac308-5e14-4c83-8111-55f87f3bab17", 
            "DataPoints" : {
                "Person" : {
                    "ItemCount" : 777, 
                    "NameCount" : NumberInt(999), 
                    "LevelCount" : NumberInt(999)
                }, 
                "Houses" : [                        
                    {
                        "DoorNumber" : NumberInt(4), 
                        "MatNumber" : 777,
                        "Person" : {
                            "ItemCount" : NumberInt(999), 
                            "NameCount" : NumberInt(999), 
                            "OneTwoDetails" : {
                                "WholeObj" : {
                                    "OneTwo123" : {
                                        "One1X" : 999.9, 
                                        "One2X" : 999.9, 
                                        "One3X" : 999.9
                                    }
                                }, 
                                "Houses" : [
                                    {
                                        "DoorNumber" : NumberInt(999), 
                                        "MatNumber" : NumberInt(999), 
                                        "OneTwo123" : {
                                            "One1X" : 999.9, 
                                            "One2X" : 999.9, 
                                            "One3X" : 999.9
                                        }, 
                                        "Rooms" : [
                                            {
                                                "LengthX" : NumberInt(999), 
                                                "LengthY" : NumberInt(999), 
                                                "OneTwo123" : {
                                                    "One1X" : 777, 
                                                    "One2X" : 999.9, 
                                                    "One3X" : 999.9
                                                }
                                            }, 
                                            {
                                                "LengthX" : NumberInt(999), 
                                                "LengthY" : NumberInt(999), 
                                                "OneTwo123" : {
                                                    "One1X" : 999.9, 
                                                    "One2X" : 999.9, 
                                                    "One3X" : 999.9
                                                }
                                            }
                                        ]
                                    }
                                ]
                            }, 
                            "ApplianceCount" : NumberInt(999), 
                            "UtilityPowerCount" : NumberInt(999), 
                            "UtilityPowerDetails" : [
                                {
                                    "Name" : "d0f587f8-0a5a-4cd8-aa40-b8ca6e725f27", 
                                    "Range" : 999.9
                                }, 
                                { 
                                    "Name" : "76626240-2f58-44d0-8a53-0c161cd6ce58", 
                                    "Range" : 999.9
                                }
                            ]
                        }
                    }
                ]
            }, 
            "DataSearching" : {
                "SearchSequence" : NumberInt(999), 
                "SearchedDetails" : {
                    "TagX" : 999.9, 
                    "TagY" : 999.9
                }, 
                "Houses" : [
                    {
                        "Sequence" : NumberInt(4), 
                        "SearchedDetails" : {
                            "TagX" : 999.9, 
                            "TagY" : 999.9
                        }, 
                        "Rooms" : [
                            {
                                "Sequence" : NumberInt(999), 
                                "Clauses" : [
                                    {
                                        "Value" : 777, 
                                        "IsLimited" : true
                                    }, 
                                    {
                                        "Value" : 999.9, 
                                        "IsLimited" : true
                                    }
                                ]
                            }
                        ]
                    }
                ]
            }
        }
    ]
}
}

Query : AND of all the below

  • "DataObj.Community.DataPoints.Person.ItemCount" Equals 777
  • "DataObj.Community.DataPoints.Houses.MatNumber" Equals 777
  • "DataObj.Community.DataPoints.Houses.Person.OneTwoDetails.Houses.Rooms.OneTwo123.One1X" Equals 777
  • "DataObj.Community.DataSearching.Houses.Rooms.Clauses.Value" Equals 777

Projection :

  • "DataObj.Community.DataPoints.Person.ItemCount"
  • "DataObj.Community.DataPoints.Houses.MatNumber"
  • "DataObj.Community.DataPoints.Houses.Person.OneTwoDetails.Houses.Rooms.OneTwo123.One1X"
  • "DataObj.Community.DataSearching.Houses.Rooms.Clauses.Value"

Expected Result :

{
"DataObj" : { 
    "Community" : [
        {
            "DataPoints" : {
                "Person" : {
                    "ItemCount" : 777
                }, 
                "Houses" : [                        
                    {
                        "MatNumber" : 777,
                        "Person" : {
                            "OneTwoDetails" : { 
                                "Houses" : [
                                    {
                                        "Rooms" : [
                                            {
                                                "OneTwo123" : {
                                                    "One1X" : 777
                                                }
                                            }
                                        ]
                                    }
                                ]
                            }
                        }
                    }
                ]
            }, 
            "DataSearching" : {
                "Houses" : [
                    {
                        "Rooms" : [
                            {
                                "Clauses" : [
                                    {
                                        "Value" : 777
                                    }
                                ]
                            }
                        ]
                    }
                ]
            }
        }
    ]
}
}

I achieved partial success with the below approach, but I don't know how to make this work for nested arrays.

Document without nested array :

{ 
"_id" : ObjectId("57154af04b42aa1fd05fe9fe"), 
"Element1" : "5e6ac308-5e14-4c83-8111-55f87f3bab17", 
"Element2" : NumberInt(1),
"Array1" : [
    {
        "Array1Elem1" : NumberInt(666),  
        "Array1Elem2" : 1999.0,
        "Array1Elem3" : 999,
        "Array1Elem4" : 999
    }, 
    {
        "Array1Elem1" : NumberInt(999), 
        "Array1Elem2" : 999.9,
        "Array1Elem3" : 999,
        "Array1Elem4" : 999         
    }, 
    {
        "Array1Elem1" : NumberInt(999), 
        "Array1Elem2" : 999.9,
        "Array1Elem3" : 999,
        "Array1Elem4" : 999
    }, 
    {
        "Array1Elem1" : NumberInt(999), 
        "Array1Elem2" : 999.9,
        "Array1Elem3" : 999,
        "Array1Elem4" : 999
    }, 
    {
        "Array1Elem1" : NumberInt(444), 
        "Array1Elem2" : 2999,
        "Array1Elem3" : 999,
        "Array1Elem4" : 999
    }
]
}

Approach taken (Found in another StackOverflow Question) :

db.collectionName.aggregate(
{ "$match" : { 
  $and : [
  { Array1 : { 
    $elemMatch: {
      "Array1Elem1": {$lt : 999}, 
      "Array1Elem2" : {$gt : 1200} 
      }
    } 
  },
  {
  // other conditions
  }
  ]
  }
},
{ "$project" : {
  "Array1" : {
    "$setDifference" : [
    { "$map" : {
      "input" : "$Array1", 
      "as" : "element",
      "in" : {
        "$cond" : [
        { "$and" : [
          {"$lt" : ["$$element.Array1Elem1", 999]},
          {"$gt" : ["$$element.Array1Elem2", 1200]}
          ]
        },
        {"$cond" : [1, "$$element", 0]},
        false
        ]
        }
      }},
    [false]
    ]
  }
  }
})

Result (Almost what I need. Does not exactly return the asked projected fields, but at least it returns only the array elements which match the condition. I can work with that) :

{ 
"_id" : ObjectId("57154af04b42aa1fd05fe9fe"), 
"Array1" : [
    {
        "Array1Elem1" : NumberInt(666),  
        "Array1Elem2" : 1999.0,
        "Array1Elem3" : 999,
        "Array1Elem4" : 999
    }, 
    {
        "Array1Elem1" : NumberInt(444), 
        "Array1Elem2" : 2999,
        "Array1Elem3" : 999,
        "Array1Elem4" : 999         
    }
]
}

If I can expand the behaviour to nested arrays, I can use that. If there is any other way to achieve projection with nested arrays, please discuss.

Neil Lunn
  • 148,042
  • 36
  • 346
  • 317
mrbubz
  • 427
  • 2
  • 6
  • 20
  • 1
    8000 array elements are a lot. I'd suggest that you should reconsider your document structure to reduce level of nesting. – Saleem Apr 21 '16 at 00:11
  • Do the names of the keys actually change so wildly inside each array element? Or is this just in your typing of an abstract representation? If the key names are not consistent, then this really causes another problem altogether. If they are consistent then it's possible, but you really do need to be careful in the construction. Mainly wondering since there are errors in the notation here with inconsistent brackets. Probably would be better to show a "real document", with the unnecessary data stripped out instead. – Neil Lunn Apr 21 '16 at 00:37
  • 3
    So in short, sorry but there are just so many structural errors in what you presently have posted in an effort to "clearly obfuscate" the actual data, I'm posting my vote that this is *"unclear in what you are asking"*, until such time that you can at least post something that is structurally sound. In it's present form I don't feel the data in the question "truly" represents your actual structure in a way that any answer could be supplied that solves the problem. If you correct the data to something more representative, then this would become an answerable question. Right now it does not. – Neil Lunn Apr 21 '16 at 01:04
  • @Saleem Even though the array has 8000 elements, each element is really small. But the rest of the document is really big. I understand `$unwind` duplicates the rest of the data and is not suitable for my case. The data is what it is and I cannot restructure or change it. The only option I have is to work with it. Same applies for nesting. That is why I am searching for an approach (If one exists) that might work. @NeilLunn, I have replaced the example with actual data (Names changed and document made much shorter, but still very much actual data like). Names of keys change like shown. – mrbubz Apr 21 '16 at 16:53
  • Well, if you don't have option of restructuring, consider map-reduce. I know it will be less performant than aggregation but will work. – Saleem Apr 21 '16 at 19:43
  • I am going ahead with Client Side filtering of data. Will be filtering in backend (C#), based on the query conditions once the data is returned from DB. Any advantages or disadvantages anyone could think of? – mrbubz Apr 22 '16 at 18:41

0 Answers0