1

This is my sample documents in DB.

{
    "_id":1,
    "object":{
        "featureOne":true,
        "featureTwo":false,
        "featureThree":false,
        "featureFour":false
    }
}, {
    "_id":2,
    "object":{
        "featureOne":false,
        "featureTwo":false,
        "featureThree":false,
        "featureFour":false
    }
}, {
    "_id":3,
    "object":{
        "featureOne":true,
        "featureTwo":false,
        "featureThree":false,
        "featureFour":false
    }
}

I want to get one object with "featureOne": true and another with "featureOne": false in a single query. I have knowledge on finding data using a particular key and value. But how can I get the data using same key of true and false values in a single query. Present I am using this code

db.collection(collectionName).find({"featureOne":true, {limit:1}).toArray(function(err, results) {
    db.collection(collectionName).find("featureOne":,false{limit:2}).toArray(function(err,res {
        console.log(results,res);
    });
});

Is there any better way to achieve this?

1
  • Please see my answer using aggregation pipeline. That goes thru the collection only once. Commented Dec 4, 2017 at 8:41

3 Answers 3

2

You can achieve it with aggregation framework.

You can use $group and $project stages (as mentioned in the Clement Amarnath answer), but if you need a more control over the data collection stage then you can use:

  • $facet - collects data by sub aggregation pipelines.
  • $match - sets the criterias for the documents filter.
  • $limit - defines the maximum amount of documents which can be taken (use $sample if you want to randomize a result).
  • $addFields - merge results into one element (array).
  • $unwind - splits the results array into seperated documents.
  • $replaceRoot - changes the document root element.

Example code:

db.collection.aggregate([
    {
        $facet: {
            "result1": [
                {$match: {"object.featureOne": true}},
                {$limit: 1}
            ],
            "result2": [
                {$match: {"object.featureOne": false}},
                {$limit: 1}
            ]
        }
    },
    {$addFields: {"results": [{$arrayElemAt: ["$result1", 0]}, {$arrayElemAt: ["$result2", 0]}]}},
    {$unwind: "$results"},
    {$replaceRoot: {newRoot: "$results"}}
])
Sign up to request clarification or add additional context in comments.

Comments

2

Using $aggregate, we can do this

Query to achieve the result

db.collection.aggregate([
    {$group:{"_id": {"featureOne":"$object.featureOne"}, 
             object:{$first:"$object"}, 
             origId:{$first:"$_id"}}
    }, 
    {$project:{_id:"$origId", object:"$object"}}
])

$group and $project were used to get this result

Sample data

{ "_id" : 1, "object" : { "featureOne" : true, "featureTwo" : false, "featureThree" : false, "featureFour" : false } }
{ "_id" : 2, "object" : { "featureOne" : false, "featureTwo" : false, "featureThree" : false, "featureFour" : false } }
{ "_id" : 3, "object" : { "featureOne" : true, "featureTwo" : false, "featureThree" : false, "featureFour" : false } }
{ "_id" : 4, "object" : { "featureOne" : true, "featureTwo" : true, "featureThree" : false, "featureFour" : true } }
{ "_id" : 5, "object" : { "featureOne" : false, "featureTwo" : false, "featureThree" : false, "featureFour" : true } }
{ "_id" : 6, "object" : { "featureOne" : true, "featureTwo" : true, "featureThree" : false, "featureFour" : false } }

After executing $group on this data we will be getting\

{ "_id" : { "featureOne" : false }, "object" : { "featureOne" : false, "featureTwo" : false, "featureThree" : false, "featureFour" : false }, "origId" : 2 }
{ "_id" : { "featureOne" : true }, "object" : { "featureOne" : true, "featureTwo" : false, "featureThree" : false, "featureFour" : false }, "origId" : 1 }

to make it presentable as it looks like the original collection we have to use $project, after executing the $project pipeline on our result we will be getting

{ "_id" : 2, "object" : { "featureOne" : false, "featureTwo" : false, "featureThree" : false, "featureFour" : false } }
{ "_id" : 1, "object" : { "featureOne" : true, "featureTwo" : false, "featureThree" : false, "featureFour" : false } }

2 Comments

I am able to get result. But how can I get multiple objects instead of getting a single result for both cases?
@SuryaTeja - The reason why you are getting only two results is, the $group query _id is taking the value object.featureOne which has only two values either true or false. You can play with _id of $group to get multiple/different results. An alternate easier approach is you can use the approach mentioned in Neodan's answer, by increasing the limit(increase it to the number of records you want) and replace $addFields pipeline with $concatArrays together with a $project - {$project: {results: {$concatArrays:["$result1", "$result2"]}}}
1

I think your approach is good. But in both cases your limit can be 1 In Mongoose, for example, you could call findOne, but with the official driver I think your approach is correct.

If you want to avoid both calls, you can extract all the elements of the collection and loop through them in code until you get one of each value. Probably more efficient than the other approach if you don't have many documents in your collection.

2 Comments

Hi @David. Your answer is good for me but I have thousands of documents in my collection. So I am trying to reduce no.of calls to DB.Thank you
The aggregate that @Clement Amarnath suggest is best option

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.