Meteor: ускорение объединения MongoDB для больших данных?

У меня есть две коллекции: Data и Users. В коллекции данных есть массив идентификаторов пользователей, состоящий примерно из 300–800 пользователей.

Мне нужно объединить страны всех пользователей для каждой строки в коллекции данных, что зависает в моем веб-браузере из-за того, что одновременно запрашивается слишком много данных.

Я запрашиваю около 16 строк коллекции данных одновременно, а также в коллекции пользователей на данный момент 18833 пользователя.

До сих пор я пытался сделать как метод Meteor, так и transform() JOIN для коллекции Meteor, которая зависает в моем приложении.

Коллекция Монго:

UserInfo = new Mongo.Collection("userInfo")
GlyphInfo = new Mongo.Collection("GlyphAllinOne", {
    transform: function(doc) {
        doc.peopleInfo = doc.peopleInfo.forEach(function(person) {  
            person.code3 = UserInfo.findOne({userId: person.name}).code3;
            return person;
        })
        return doc;
    }
});

code3 обозначает страну пользователя.

Публикация:

Meteor.publish("glyphInfo", function (courseId) {
    this.unblock();
    var query = {};
    if (courseId) query.courseId = courseId;
    return [GlyphInfo.find(query), UserInfo.find({})];
})

Метод проверенного сервера:

Meteor.methods({
    'glyph.countryDistribution': function(courseId) {
        var query = {};
        if (courseId) query.courseId = courseId;
        var glyphs = _.map(_.pluck(GlyphInfo.find(query).fetch(), 'peopleInfo'), function(glyph) {
            _.map(glyph, function(user) {
                var data = Users.findOne({userId: user.name});
                if (data) {
                    user.country = data ? data.code3 : null;
                    console.log(user.country)
                    return user;
                }
            });
            return glyph;
        });
        return glyphs;
    }
});

Данные коллекции:

Коллекция GlyphAllInOne Сбор информации о пользователе

Существует возможность предварительной обработки моей коллекции, чтобы страны уже были включены, однако мне не разрешено изменять эти коллекции. Я предполагаю, что если это JOIN будет выполнено при запуске сервера, а затем выставлено через массив как метод Meteor, это может слишком долго задерживать время запуска сервера; хотя я не уверен.

У кого-нибудь есть идеи, как ускорить этот запрос?

EDIT: Пробовал также команды агрегации MongoDB, и они оказались очень медленными на минимонго Meteor. На запрос ушло 4 минуты по сравнению с 1 секундой на собственном клиенте MongoDB.

var codes = GlyphInfo.aggregate([

        {$unwind: "$peopleInfo"},
        {$lookup: {
            from: "users",
            localField: "peopleInfo.name",
            foreignField: "userId",
            as: "details"
        }
        },
        {$unwind: "$details"},
        {$project: {"peopleInfo.Count": 1, "details.code3": 1}}
    ])

person Dranithix    schedule 12.07.2016    source источник
comment
вываливай свои документы в текст.....   -  person profesor79    schedule 12.07.2016
comment
Мне не разрешено работать с текстовыми документами проекта, над которым я работаю.   -  person Dranithix    schedule 12.07.2016
comment
Агрегация не работает с minimongo, она работает только на стороне сервера.   -  person Michel Floyd    schedule 13.07.2016


Ответы (2)


Я бы подошел к этой проблеме несколько иначе, используя reywood:publish-composite.

  1. На сервере я бы опубликовал глифинформацию, используя publish-composite, а затем включил в публикацию соответствующего пользователя и поле его страны.
  2. Я бы присоединял страну к клиенту везде, где мне нужно было отображать название страны вместе с объектом glyphinfo

Публикация:

Meteor.publishComposite('glyphInfo', function(courseId) {
    this.unblock();
    return {
        find: function() {
        var query = {};
        if (courseId) query.courseId = courseId;
        return GlyphInfo.find(query);
        },
        children: [
            {
                find: function(glyph) {
                    var nameArray = [];
                    glyph.person.forEach(function(person){
                      nameArray.push(person.name);
                    };
                    return UserInfo.find({ userId: {$in: nameArray }});
            }
        ]
    }
});
person Michel Floyd    schedule 12.07.2016
comment
Мне удалось успешно создать публикацию для № 1, однако № 2 потребовал слишком много обработки для одновременного вызова слишком большого количества UserInfo.findOne(), что повесило мой браузер. Я пытаюсь получить распределение peopleInfo по странам, чтобы знать, сколько Count для каждой страны, в виде массива на основе peopleInfo. - person Dranithix; 12.07.2016
comment
№ 2, вероятно, лучше оставить для монго-агрегации на сервере. - person Michel Floyd; 13.07.2016

Решил проблему, создав огромный вызов агрегации MongoDB, при этом самым большим фактором в решении задержки была индексация уникальных столбцов в вашей базе данных.

После тщательного внедрения индексов в мою базу данных с более чем 4,6 миллионами записей это заняло 0,3 секунды на Robomongo и 1,4 секунды с отправкой данных клиенту на Meteor.

Вот код агрегации для тех, кто хотел бы его увидеть:

Meteor.methods({
    'course.countryDistribution': function (courseId, videoId) {
        var query = {};
        if (courseId) query.courseId = courseId;

        var data = GlyphInfo.aggregate([

            {$unwind: "$peopleInfo"},
            {$lookup: {
                from: "users",
                localField: "peopleInfo.name",
                foreignField: "userId",
                as: "details"
            }
            },
            {$unwind: "$details"},
            {$project: {"peopleInfo.Count": 1, "details.code3": 1}},
            {$group: {_id: "$details.code3", count: {$sum: "$peopleInfo.Count"}}}
        ])

        return data;
    }
});

Если кто-то еще занимается подобными проблемами, не стесняйтесь обращаться ко мне. Спасибо всем за вашу поддержку!

person Dranithix    schedule 13.07.2016