简体   繁体   中英

How to Sort by Weighted Values

I have this problem that I want to sort the result of a query based on the field values from another collection,

Problem: I want to first get the user 123 friends and then get their posts and then sort the post with the friends strength value,

I have this :

POST COLLECTON:
{
    user_id: 8976,
    post_text: 'example working',
}
{
    user_id: 673,
    post_text: 'something',
}

USER COLLECTON:
{
    user_id: 123,
    friends: {
        {user_id: 673,strength:4}
        {user_id: 8976,strength:1}
    }
}

Based on the information you have retrieved from your user you essentially want to come out to an aggregation framework query that looks like this:

db.posts.aggregate([
    { "$match": { "user_id": { "$in": [ 673, 8976 ] } } },
    { "$project": {
        "user_id": 1,
        "post_text": 1,
        "weight": {
            "$cond": [
                { "$eq": [ "$user_id", 8976 ] },
                1,
                { "$cond": [ 
                    { "$eq": [ "$user_id", 673 ] },
                    4,
                    0
                ]}
            ]
        }
    }},
    { "$sort": { "weight": -1 } }
])

So why aggregation when this does not aggregate? As you can see, the aggregation framework does more than just aggregate. Here it is being used to "project" a new field into the document an populate it with a "weight" to sort on. This allows you to get the results back ordered by the value you want them to be sorted on.

Of course, you need to get from your initial data to this form in a "generated" way that you do do for any data. This takes a few steps, but here I'll present the JavaScript way to do it, which should be easy to convert to most languages

Also presuming your actual "user" looks more like this, which would be valid:

{
    "user_id": 123,
    "friends": [
        { "user_id": 673,  "strength": 4 },
        { "user_id": 8976, "strength": 1 }
    ]
}

From an object like this you then construct the aggregation pipeline:

// user is the structure shown above

var stack = [];
args = [];

user.friends.forEach(function(friend) {

    args.push( friend.user_id );

    var rec = {
        "$cond": [
            { "$eq": [ "user_id", friend.user_id ] },
            friend.strength
        ]
    };

    if ( stack.length == 0 ) {
        rec["$cond"].push(0);
    } else {
        var last = stack.pop();
        rec["$cond"].push( last );
    }

    stack.push( rec );

});


var pipeline = [
    { "$match": { "user_id": { "$in": args } } },
    { "$project": {
        "user_id": 1,
        "post_text": 1,
        "weight": stack[0]
    }},
    { "$sort": { "weight": -1 } }
];

db.posts.aggregate(pipeline);

And that is all there is to it. Now you have some code to go through the list of "friends" for a user and construct another query to get all posts from those friends weighted by the "strength" value for each.

Of course you could do much the same things with a query for all posts by just removing or changing the $match , but keeping the "weight" projection you can "float" all of the "friends" posts to the top.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM