1

I need to get documents from collection in MongoDB. A single document looks like this:

{
    name    : "John Doe",
    address : "Some cool address",
    value1  : 2,
    value2  : 3,
    value3  : 5
}

I need to retrieve all fields + sort the documents descending by number equal to value1 + value2 + value3.

Only solution I found (incomplete because it doesn't retrieve all fields) looks like this:

db.someCollection.aggregate(
    {
        $project: {
            sum: { $add: [ "$value1", "$value2", "$value3" ] }
        },
        $sort: {
            sum: -1
        }
    }
)

Like I said, this returns only sum field which is not what I want.

EDIT: Also the document is a bit larger in reality so I don't want to use pseudo-solution like

$project: {
    name:    1,
    address: 1,
    ...
}

i.e. I'd like some universal reusable solution, not the case-specific one

4 Answers 4

7

You can use $$ROOT to references the root document. Keep all fields of this document in a field and try to get it after that (depending on your client system: Java, C++, ...)

db.someCollection.aggregate(
    {
        $project: {
            sum: { $add: [ "$value1", "$value2", "$value3" ] },
            document: "$$ROOT"
        },
        $sort: {
            sum: -1
        }
    }
)
Sign up to request clarification or add additional context in comments.

2 Comments

I think it is answer is a better answer for the question
Although document creates a document field. Is there a way not to have this field?
4

Try the following query :

db.myCollection.aggregate(
{$project: {name : "$name", 
            address : "$address", 
            value1 : "$value1", 
            value2 : "$value2", 
            value3 : "$value3",
            sum : {$add : ["$value1", "$value2", "$value3"]}}},
{$sort: {sum: -1}}
)

As far as I know, there is no option like sort by condition in MongoDB. Also there is no option to add all the fields into the the project by default. There was an issue for this but it is not resolved yet.

For now, another option would be to create new field (ex : totalValues) and store the sum in that field and when value fields changed. Then you can sort by totalValues.

4 Comments

well yeah, that would work but in case the document has 20 fields, I don't want to enumerate 20 fields explicitly... there must be some universal solution. I just need to add the sum field on top without changing the rest
Added explanation to my answer.
I was afraid there's no simple solution... weird, seems legit to have this functionality I need. Thanx anyway
Since Mongo3.4, there's better solution using $addFields, check the answer by Franz Kirkman
3

where $project: needs to specify which fields to pass through, $addFields will return all fields and add or replace specified fields.

db.someCollection.aggregate(
    {
        $addFields: {
            sum: { $add: [ "$value1", "$value2", "$value3" ] }
        },
        $sort: {
            sum: -1
        }
    }
)

will achieve exactly what you want.

Please note, this feature was added in Mongo version 3.4.

2 Comments

This solution is good but it seems like it's not properly formatted. Like $sort should be in a nested object I think
Thanks @KeitelDOG, it was a while ago, but this works in my project.
1

To get back the documents as they are in the DB you can use the $replaceRoot operator:

db.someCollection.aggregate(
    [
        {
            $project: {
                sum: { $add: [ "$value1", "$value2", "$value3" ] },
                document: "$$ROOT"
            }
        },
        {
            $sort: { sum: -1 }
        },
        {
            $replaceRoot : {newRoot : "$document"}
        }
    ]
)

Result is:

{
    "_id" : ObjectId("58f0d4777bde783e55e3410b"),
    "name" : "John Doe",
    "address" : "Some cool address",
    "value1" : 2,
    "value2" : 3,
    "value3" : 5
}

{
    "_id" : ObjectId("58f0d4c77bde783e55e3411e"),
    "name" : "Jane Doe",
    "address" : "Some cool address",
    "value1" : 1,
    "value2" : 3,
    "value3" : 2
}

3 Comments

This is clever, but since $replaceRoot is available in Mongo 3.4 or newer, there's also $addFields which does exactly what the op was asking for. So there's no need for this workaround.
@ŁukaszMazan That's right. This operator is more appropriate in other scenarios.
I like your solution. The Cleaner one as it put the fields back to normal with no additional field name.

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.