0

I am trying to write a query to group my records by the value inside an array inside another array inside a collection in MongoDB. Now if that doesn't have your head hurting I think a sample schema might be easier to understand:

{
    "_id": ObjectId("...")
    "attributes": [
        [ "attributeA", "valueA" ],
        [ "attributeB", "valueB" ],
        [ "attributeC", "valueC" ],
        ...
    ]
} 

Now I want to be able to group my records by the attributeB field based on valueB.

So far I can aggregate if I know the actual value of valueB:

collection.aggregate([
    { '$match': { 'attributes': [ "attributeB", "valueB" ] } },
    { '$group': {
        '_id': { 'attributes': [ "attributeB", "valueB" ] } } 
    }  
])

Basically seeing if the attributes array contains the pair: [ "attributeB", "valueB" ]. But now I want to be able to have the query determine what valueB is as it performs the aggregation.

To paraphrase: I can't seem to figure out how to group by the value if I don't know the value of valueB. I just want all records to group by their valueB's, when attributeB is found at the first position inside an array inside the attributes array.

Any help is appreciated. Thanks!

2
  • 1
    Your document structure seems to be incorrect though you might have a definite reason for putting it that way. Why not make attributes array :[{"attributeA":"valueA"},{"attributeB":"valueB"}] or "attributes":{"attributeA":"valueA","attributeB":"valueB"} ? It would make querying a lot easier. Commented Jan 27, 2015 at 18:55
  • I didn't want to restrict the data type of an element in the attributes array. It could be anything, not necessarily a key:value pair. Commented Jan 27, 2015 at 19:36

1 Answer 1

1

After grouping your data you should use the $unwind operator. It pairs up your other fields with every item in the array.

collection.aggregate([
  { '$match': { 'attributes': [ "attributeB", "valueB" ] } },
  { '$group': {
      '_id': { 'attributes': [ "attributeB", "valueB" ] } } 
  },
  { '$unwind':  'attributes'},
    ... // here you can match again and continue aggregation
]) 

Most probably this is not the fastest solution. I will think of a better one.

Also note that the order of elements in the array is not preserved.

UPDATE

This is a similar question. So what I would do is create documents in the attribute array like

'attributes':[
   {'attribute': 'attributeB', 'value': 'valueB'},
   {'attribute': 'attributeC', 'value': 'valueA'},
]

So you can access your valueB after the $match or $unwind through $value.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for your reply, it solves another part of the problem I have but not the main one. My main problem is that I don't want my query to match with "valueB" explicitly. I want every instance of "valueB" to be grouped together implicitly by the query.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.