Как выбрать, groupBy и присоединиться к Waterline или MongoDB

У меня есть три модели: user, noun и usernoun (user_noun в PHP/Eloquent). Существует много разных отношений между user и noun. Таблица "pivot" имеет дополнительный атрибут score. Я могу использовать этот запрос с Eloquent, чтобы суммировать оценки каждого существительного, которое пользователь должен получить от общего количества пользователей:

$users = User::leftJoin('noun_user', 'user.id', 'noun_user.user_id')
    ->groupBy('user.id')
    ->select('user.*', DB::raw('sum(noun_user.score) as score'))
    ->orderBy('score', 'desc')
    ->get();

Но я не могу понять, как заставить это работать в Waterline. Это работает, но не тогда, когда я раскомментирую строку .populate('user'). Мне нужно user для заполнения.

UserNoun
    .find({})
    //.populate('user')
    .groupBy('user')
    .sum('score')
    .sort({ score: 'desc' })
    .exec((err, usernouns) => {
    return res.json(usernouns)
})

здесь есть запрос .native(), который работает:

UserNoun.native(function(err, collection) {
    collection.aggregate([
        {
            $lookup: {
                from: 'user',
                localField: 'user',
                foreignField: '_id',
                as: 'user'
            }
        },
        {
            $group: { _id: '$user', total: { $sum: '$score' } }
        },
        {
            $sort : { total: -1 }
        }
    ]).toArray(function (err, results) {
        return res.json(results)
    })
})

Может ли этот собственный запрос быть перезаписан в Waterline с помощью groupBy и populate и sum?

Ответ 1

Как и Waterline @0.11.8, единственный способ сделать это - использовать собственные запросы.

Кстати, вы можете экспортировать свой метод внутри модели User:

// User.js
module.exports = {

    // ...

    findTotalScores: function (callback) {
        UserNoun.native(function(err, collection) {
            if (err) {
                return callback(err);
            }

            collection.aggregate([
                {
                    $lookup: {
                        from: 'user',
                        localField: 'user',
                        foreignField: '_id',
                        as: 'user'
                    }
                },
                {
                    $group: { _id: '$user', total: { $sum: '$score' } }
                },
                {
                    $sort : { total: -1 }
                }
            ]).toArray(callback);
        });
    }

};

И вы можете использовать его в своем контроллере, вызывая:

User.findTotalScores(function (err, results) {
    return res.json(results);
});

Ответ 2

Теперь вы не можете присоединяться к ватерлинии, вам придется использовать необработанные запросы для этого. populate будет просто заполнять связанные поля, он не возвращает вам результат, как это делает sql join.

Водяная линия в этот момент поддерживает только комбинацию GroupBy в комбинации с суммой(), count() и т.д.

Для группировки и сортировки вы можете использовать следующее:

Model.find()  
.groupBy('term') 
.sum('count')  
.limit(20)
.sort({count: 'desc'}) 
.exec(function (err, data){
//Your code here..
});