Fostah
Fostah

Reputation: 2946

Query a collection for the latest unique object

Question: Lets say I have the following objects in a collection:

How would I return one record per "product_id" and only the one with the highest "version" number? And is this possible to do within mongoose?

{
    "_id" : ObjectId("54f765564b10883c1800002a"),
    "total_invoice_fob_case" : 86.70999999999999,
    "status" : "Draft",
    "discount" : "3.40",
    "effective_date" : ISODate("2013-08-01T06:00:00.000Z"),
    "version" : 2,
    "controlstate" : "AB",
    "controlstate_id" : ObjectId("54d510e9e3d793f581b6bb27"),
    "product" : "Product A",
    "product_id" : ObjectId("54f75b5e4b1088801a000627"),
    "size" : "1.75LTR",
    "size_id" : ObjectId("5418a3dd750b4294c2cb3a47"),
    "vendor" : "BEAM SUNTORY",
    "vendor_id" : ObjectId("54ef5aa74b1088781b000169"),
    "product_state_code" : "123",
    "net_fob_cost" : 86.70999999999999,
    "change_reason" : [ 
        "Other"
    ],
    "submitted" : {
        "submitted_date" : ISODate("2014-05-16T06:00:00.000Z")
    }
},
{
    "_id" : ObjectId("54f765564b10883c1800002b"),
    "total_invoice_fob_case" : 86.70999999999999,
    "status" : "Draft",
    "discount" : "4.40",
    "effective_date" : ISODate("2013-08-01T06:00:00.000Z"),
    "version" : 3,
    "controlstate" : "AB",
    "controlstate_id" : ObjectId("54d510e9e3d793f581b6bb27"),
    "product" : "Product A",
    "product_id" : ObjectId("54f75b5e4b1088801a000627"),
    "size" : "1.75LTR",
    "size_id" : ObjectId("5418a3dd750b4294c2cb3a47"),
    "vendor" : "BEAM SUNTORY",
    "vendor_id" : ObjectId("54ef5aa74b1088781b000169"),
    "product_state_code" : "123",
    "net_fob_cost" : 86.70999999999999,
    "change_reason" : [ 
        "Other"
    ],
    "submitted" : {
        "submitted_date" : ISODate("2014-05-16T06:00:00.000Z")
    }
},
{
    "_id" : ObjectId("54f765564b10883c1800002c"),
    "total_invoice_fob_case" : 86.70999999999999,
    "status" : "Draft",
    "discount" : "3.40",
    "effective_date" : ISODate("2013-08-01T06:00:00.000Z"),
    "version" : 2,
    "controlstate" : "AB",
    "controlstate_id" : ObjectId("54d510e9e3d793f581b6bb27"),
    "product" : "Product B",
    "product_id" : ObjectId("54f75b5e4b1088801a000628"),
    "size" : "1.75LTR",
    "size_id" : ObjectId("5418a3dd750b4294c2cb3a47"),
    "vendor" : "BEAM SUNTORY",
    "vendor_id" : ObjectId("54ef5aa74b1088781b000169"),
    "product_state_code" : "123",
    "net_fob_cost" : 86.70999999999999,
    "change_reason" : [ 
        "Other"
    ],
    "submitted" : {
        "submitted_date" : ISODate("2014-05-16T06:00:00.000Z")
    }
}

Upvotes: 2

Views: 55

Answers (2)

Jason Cust
Jason Cust

Reputation: 10909

Sounds like a job for Mongo's aggregation framework. You can extrapolate from this example how to approach the problem.

Update: To retrieve one per product_id with the highest version you would need to also use $first:

db.products.aggregate([
  {$sort: {product_id: 1, version: -1}}, // sort first so that $first pulls the correct record
  {$group: {
    _id: {product_id: '$product_id'}, // group by the product_id
    product: {$first: $$ROOT} // only return the first document per group
  }}
]);

Upvotes: 2

chridam
chridam

Reputation: 103445

You need an aggregation pipeline that first sorts the documents in the collection by version number descending using a $sort pipeline stage, then groups the ordered documents by product_id using the $group operator. Within the grouping use the $first operator on $$ROOT to return the first document in the sorted group:

var pipeline = [
    {
        "$sort": { "version": -1 }
    },
    {
        "$group": {
            "_id": "$product_id",
            "value": {
                "$first": "$$ROOT"
            }
        }
    },
    {
        "$project": {
            "_id": 0,
            "product_id": "$_id",
            "status": "$value.status",
            "version": "$value.version",
            "product" : "$value.product"
        }    
    }
 ];

// Mongoose aggregation
Model.aggregate(pipeline, function (err, res) {
  if (err) return handleError(err);
  console.log(res); // 
});

Console Output:

[ 
    {
        "product_id" : ObjectId("54f75b5e4b1088801a000628"),
        "status" : "Draft",
        "version" : 2,
        "product" : "Product B"
    }, 
    {
        "product_id" : ObjectId("54f75b5e4b1088801a000627"),
        "status" : "Draft",
        "version" : 3,
        "product" : "Product A"
    }
]

-- UPDATE --

To project the full document, replace the $project pipeline with the following:

{
    "$project": {
        "_id": 0,
        "product": "$value"
    }
}

Output:

/* 1 */
{
    "result" : [ 
        {
            "product" : {
                "_id" : ObjectId("54f765564b10883c1800002c"),
                "total_invoice_fob_case" : 86.7099999999999940,
                "status" : "Draft",
                "discount" : "3.40",
                "effective_date" : ISODate("2013-08-01T06:00:00.000Z"),
                "version" : 2,
                "controlstate" : "AB",
                "controlstate_id" : ObjectId("54d510e9e3d793f581b6bb27"),
                "product" : "Product B",
                "product_id" : ObjectId("54f75b5e4b1088801a000628"),
                "size" : "1.75LTR",
                "size_id" : ObjectId("5418a3dd750b4294c2cb3a47"),
                "vendor" : "BEAM SUNTORY",
                "vendor_id" : ObjectId("54ef5aa74b1088781b000169"),
                "product_state_code" : "123",
                "net_fob_cost" : 86.7099999999999940,
                "change_reason" : [ 
                    "Other"
                ],
                "submitted" : {
                    "submitted_date" : ISODate("2014-05-16T06:00:00.000Z")
                }
            }
        }, 
        {
            "product" : {
                "_id" : ObjectId("54f765564b10883c1800002b"),
                "total_invoice_fob_case" : 86.7099999999999940,
                "status" : "Draft",
                "discount" : "4.40",
                "effective_date" : ISODate("2013-08-01T06:00:00.000Z"),
                "version" : 3,
                "controlstate" : "AB",
                "controlstate_id" : ObjectId("54d510e9e3d793f581b6bb27"),
                "product" : "Product A",
                "product_id" : ObjectId("54f75b5e4b1088801a000627"),
                "size" : "1.75LTR",
                "size_id" : ObjectId("5418a3dd750b4294c2cb3a47"),
                "vendor" : "BEAM SUNTORY",
                "vendor_id" : ObjectId("54ef5aa74b1088781b000169"),
                "product_state_code" : "123",
                "net_fob_cost" : 86.7099999999999940,
                "change_reason" : [ 
                    "Other"
                ],
                "submitted" : {
                    "submitted_date" : ISODate("2014-05-16T06:00:00.000Z")
                }
            }
        }
    ],
    "ok" : 1
}

Upvotes: 1

Related Questions