Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find after aggregate in MongoDB

{
    "_id" : ObjectId("5852725660632d916c8b9a38"),
    "response_log" : [ 
    {
        "campaignId" : "AA",
        "created_at" : ISODate("2016-12-20T11:53:55.727Z")
    }, 
    {
        "campaignId" : "AB",
        "created_at" : ISODate("2016-12-20T11:55:55.727Z")

    }]
}

I have a document which contains an array. I want to select all those documents that do not have response_log.created_at in last 2 hours from current time and count of response_log.created_at in last 24 is less than 3.

I am unable to figure out how to go about it. Please help

like image 308
Pulkit Aggarwal Avatar asked Apr 25 '17 08:04

Pulkit Aggarwal


People also ask

Is aggregate faster than find in MongoDB?

Because of this, if you have a simple aggregation pipeline or one which does not cut down the data volume much it can often be quicker to use a find() and perform the aggregation client side.

What are lookup in aggregation for MongoDB?

js. The $lookup operator is an aggregation operator or an aggregation stage, which is used to join a document from one collection to a document of another collection of the same database based on some queries. Both the collections should belong to the same databases.

What does find () do in MongoDB?

Find() Method. In MongoDB, find() method is used to select documents in a collection and return a cursor to the selected documents.

What does $match do in MongoDB?

The MongoDB $match operator filters the documents to pass only those documents that match the specified condition(s) to the next pipeline stage.


1 Answers

You can use the aggregation framework to filter the documents. A pipeline with $match and $redact steps will do the filtering.

Consider running the following aggregate operation where $redact allows you to proccess the logical condition with the $cond operator and uses the system variables $$KEEP to "keep" the document where the logical condition is true or $$PRUNE to "remove" the document where the condition was false.

This operation is similar to having a $project pipeline that selects the fields in the collection and creates a new field that holds the result from the logical condition query and then a subsequent $match, except that $redact uses a single pipeline stage which is more efficient:

var moment = require('moment'),
    last2hours = moment().subtract(2, 'hours').toDate(),
    last24hours = moment().subtract(24, 'hours').toDate();

MongoClient.connect(config.database)
    .then(function(db) {
        return db.collection('MyCollection')
    })
    .then(function (collection) {
        return collection.aggregate([
            { '$match': { 'response_log.created_at': { '$gt': last2hours } } },
            { 
                '$redact': {
                    '$cond': [
                        { 
                            '$lt': [
                                {
                                    '$size': {
                                        '$filter': {
                                            'input': '$response_log',
                                            'as': 'res',
                                            'cond': { 
                                                '$lt': [
                                                    '$$res.created_at', 
                                                    last24hours
                                                ] 
                                            }
                                        }
                                    }
                                },
                                3
                            ]
                        },
                        '$$KEEP',
                        '$$PRUNE'
                    ]
                }
            }
        ]).toArray();  
    })
    .then(function(docs) {
        console.log(docs)
    })
    .catch(function(err) {
        throw err;
    });

Explanations

In the above aggregate operation, if you execute the first $match pipeline step

collection.aggregate([
    { '$match': { 'response_log.created_at': { '$gt': last2hours } } }
])

The documents returned will be the ones that do not have "response_log.created_at" in last 2 hours from current time where the variable last2hours is created with the momentjs library using the subtract API.


The preceding pipeline with $redact will then further filter the documents from the above by using the $cond ternary operator that evaluates this logical expression that uses $size to get the count and $filter to return a filtered array with elements that match other logical condition

{ 
    '$lt': [
        {
            '$size': {
                '$filter': {
                    'input': '$response_log',
                    'as': 'res',
                    'cond': { '$lt': ['$$res.created_at', last24hours] }
                }
            }
        },
        3
    ]
}

to $$KEEP the document if this condition is true or $$PRUNE to "remove" the document where the evaluated condition is false.

like image 199
chridam Avatar answered Sep 29 '22 07:09

chridam