Heems
Heems

Reputation: 158

Mongo embedded document field query

I have an existing collection with documents similar to below

{
    "_id" : UUID("ef2f3778-c74a-4a40-9169-a7798bc6c776"),
    "feedId" : UUID("677d0a5a-0ceb-43d9-954d-bfefddc22ca2"),
    "events" : {
        "1500" : {
            "t" : ISODate("2020-08-20T02:25:00.000Z"),
            "a" : {
                "Manager" : "Joe",
                "Amount" : 20.3
            }
        },
        "1800" : {
            "t" : ISODate("2020-08-20T02:30:00.000Z"),
            "a" : {
                "Manager" : "Joe",
                "Amount" : 21.3
            }
        },
        "2100" : {
            "t" : ISODate("2020-08-20T02:35:00.000Z"),
            "a" : {
                "Manager" : "Joe",
                "Amount" : 22.3
            }
        },
        "2400" : {
            "t" : ISODate("2020-08-20T02:40:00.000Z"),
            "a" : {
                "Manager" : "Joe",
                "Amount" : 23.3
            }
        }
    }
}

I am interested in a query that returns all the Amount field values ordered in time (the "t" field). Now, I recognize the "events" embedded document is not structured properly most likely. It should be an array. But this is the hand dealt I am afraid, i.e. don't have an option to change the documents. How do I get a query that returns something akin to [20.3, 21.3, 22.3, 23.3, etc.] from all the objects in the collection with the same feedId?

Edit: I'll add that the event objects/documents don't have a uniform name across each collection object. I.e. here you see 1500, 1800, 2100, etc. this document but the next document may have 1500, and 3000 only.

Reference to another post who had the same question but hit a wall it seems (without changing the document schema to arrays):

MongoDB Query Embedded Document Field

Upvotes: 0

Views: 496

Answers (2)

Heems
Heems

Reputation: 158

@J.F's answer got me close. Here is the query that worked for me:

db.collection.aggregate(
    [
        { $match: { feedId: 2 } },
        { $project: { events: {$objectToArray: "$events"} } },
        {
            "$unwind": "$events"
        },
        {
            "$sort": {
              "events.v.t": 1
            }
        },
        { $group: {  "_id": null, Amounts: { $push: "$events.v.a.Amount" } } },
        {
          "$project": {
            "Amounts": 1,
            "_id": 0
        }
      }
    ]
)

It's producing the expected result for me. I hope it's correct.

Result:

/* 1 */
{
    "Amounts" : [ 
        20.3, 
        21.3, 
        22.3, 
        23.3, 
        24.3, 
        25.3, 
        26.3, 
        27.3, 
        28.3, 
        29.3, 
        30.3, 
        31.3, 
        32.3, 
        33.3, 
        34.3, 
        35.3, 
        36.3, 
...

Example

Upvotes: 1

J.F.
J.F.

Reputation: 15177

Try this query and check if it is the behaviour you expect (example here)

Note that I've change dates for number to read easier.

db.collection.aggregate([
  {
    "$match": {
      "feedId": 2
    }
  },
  {
    "$project": {
      "events": {
        "$objectToArray": "$events"
      }
    }
  },
  {
    "$unwind": "$events"
  },
  {
    "$sort": {
      "events.v.t": 1
    }
  },
  {
    "$group": {
      "_id": "$_id",
      "events": {
        "$push": "$events"
      }
    }
  },
  {
    "$addFields": {
      "count": "$events.v.a.Amount"
    }
  },
  {
    "$project": {
      "count": 1,
      "_id": 0
    }
  }
])

First $match the feedId field, then is necessary use $objectToArray since your schema is compound by key-values objects.
Then $unwind to can do the $sort and regroup again.
After that I've added a new field count that contains the total amount and I've used $project to shor only this field.

Upvotes: 1

Related Questions