Как в Meteor опубликовать обработанные результаты поиска в виде курсора?

Я создаю простое приложение для обмена сообщениями с Meteor. Раздел, с которым я борюсь в непрочитанных сообщениях. Я хотел бы вернуть список, показывающий имя пользователя (меня это не волнует, пожалуйста, не сосредотачивайтесь на этом аспекте, вокруг реактивных объединений/композитов и т. д.) и последнее сообщение от этого пользователя Следовательно, в функции публикации ниже мне нужно вернуть самые новые непрочитанные сообщения, НО очевидно, только одно от каждого уникального идентификатора пользователя.

чтобы сделать это, я пытаюсь манипулировать результатами запроса поиска в моем методе публикации, но я не понимаю, как манипулировать набором документов, не нарушая реактивность, как я показал в настоящее время в коде ниже, это то, что у меня пока:

Meteor.publish('unreadmessages', function() {

    if (!this.userId) {
        throw new Meteor.Error('denied', 'not-authorized');
    }

    var messageQuery, messages, userGroupQuery, userGroups;

    var self        = this;
    var user        = Meteor.users.findOne(self.userId);
    var userIdArr   = [self.userId]; // for use where queries require an array
    var contacts    = user.contacts;

    // get groups
    userGroupQuery  = Groups.find({
        $or : [
            { owner   : self.userId },
            { members : self.userId }
        ]
    }, { // Projection to only return the _id field
            fields : { _id:1 }
        }
    );

    userGroups = _.pluck(userGroupQuery.fetch(), '_id'); // create an array of id's

    messages = Messages.find({
        $or : [
            {
                $and : [
                    { participant : self.userId },
                    { userId : { $in : contacts } },
                    { readBy : { $nin : userIdArr } }
                ]
            },
            {
                $and : [
                    { groupId : { $in : userGroups } },
                    { readBy  : { $nin : userIdArr } }
                ]
            },
        ]
    });

     // TODO : also handle groups here
    uniqueMessages = _.uniq(messages.fetch(), function(msg) {
        return msg.userId;
    });

    return uniqueMessages; // obviously an array and not a cursor - meteor errors out.

});

я понимаю, что моя функция подчеркивания, конечно же, работает и действительно возвращает массив, а не реактивный курсор, который мне нужен. я знаю, что одним из решений было бы просто вытащить идентификаторы сообщений, а затем запустить другой .find для сообщений, но есть ли другой / лучший / более эффективный / более естественный способ вернуть курсор с набором результатов, который я ищу?


person bigmadwolf    schedule 04.01.2015    source источник
comment
То, что вы написали, не является реактивным, поэтому, возможно, вам действительно нужен метод Meteor.method. Метод может возвращать массив клиенту.   -  person user728291    schedule 05.01.2015
comment
Кроме того, Messages.find() уже возвращает набор уникальных сообщений. Чтобы понять, почему вы вызываете _.uniq, было бы полезно показать несколько примеров документов из Messages.   -  person user728291    schedule 05.01.2015
comment
Я понимаю, что то, что у меня есть сейчас, не является реактивным, но я бы хотел, чтобы он возвращал курсор, который есть, так что определенно после функции публикации, я думаю, и, во-вторых, чтобы объяснить, что я хочу вернуть последнее непрочитанное сообщение для каждого из контактов пользователей или групп. без _uniq (или чего-то подобного) я получаю все непрочитанные сообщения.   -  person bigmadwolf    schedule 05.01.2015
comment
Примечание. Я добавил рабочее решение ниже, но мне кажется, что это не идеальный подход.   -  person bigmadwolf    schedule 05.01.2015


Ответы (3)


Вы можете использовать observeChanges и сделать это реактивным. На added вы можете добавлять поля. Я использую этот замечательный пакет: meteor-publish-composite. Он экономит ваше время.

Используйте пагинацию, иначе вы не получите удовольствия от исполнения.

person Mário    schedule 05.01.2015
comment
Большое спасибо, я специально стараюсь избегать нескольких удобных пакетов в моем первом приложении (или, по крайней мере, его первой итерации), чтобы по-настоящему покопаться в Meteor и узнать как можно больше. Не могли бы вы добавить пример того, как вы изменили бы мою функцию публикации, чтобы использовать observeChanges и added ? - person bigmadwolf; 05.01.2015
comment
посмотрев составной файлObserverChanges и публикацию метеора, я не уверен, что вы понимаете вопрос, который я задаю, или, может быть, я не понимаю вашего ответа, я не собираюсь публиковать составной элемент, я добавляю описание ожидаемого результата, установленного выше. - person bigmadwolf; 05.01.2015

Следующий код действительно публикует реактивный курсор и является жизнеспособным решением, но я полагаю, что суть моего вопроса заключается в том, есть ли лучший способ манипулировать набором результатов предпоследнего Messages.find, чтобы по-прежнему публиковать реактивный курсор (я думая о курсоре. forEach или .map, но я не уверен, как к этому подойти).

В основном - есть ли лучший способ сделать это:

Meteor.publish('unreads', function() {
    if (!this.userId) {
        throw new Meteor.Error('denied', 'not-authorized');
    }

    // setup some vars
    var messageQuery, messages, userGroupQuery, userGroups, uniqeMsgIds;
    var self        = this;
    var user        = Meteor.users.findOne(self.userId);
    var userIdArr   = [self.userId]; // for use where queries require an array
    var contacts    = user.contacts;

    // get groups
    userGroupQuery  = Groups.find({
        $or : [
            { owner   : self.userId },
            { members : self.userId }
        ]
    }, { // Projection to only return the _id field
            fields : { _id:1 }
        }
    );

    // create an array of group id's that belong to the user.
    userGroups = _.pluck(userGroupQuery.fetch(), '_id');

    messages = Messages.find({
        $or : [
            {  // unread direct messages
                $and : [
                    { participant : self.userId },
                    { userId      : { $in : contacts } },
                    { readBy      : { $nin : userIdArr } }
                ]
            },
            {  // unread group messages
                $and : [
                    { groupId : { $in : userGroups } },
                    { readBy  : { $nin : userIdArr } }
                ]
            },
        ]
    }, { sort : { // put newest messages first
            time : -1
        }
    });

     // returns an array of unique documents based on userId or groupId
    uniqueMessages = _.uniq(messages.fetch(), function(msg) {
        if (msg.groupId) {
            return msg.groupId;
        }
        return msg.userId;
    });

    // Get the id's of all the latest unread messages (one per user or group)
    uniqeMsgIds = _.pluck(uniqueMessages, '_id');

    // finally publish a reactive cursor containing one unread message(latest) for each user/group
    return Messages.find({
        _id : { $in : uniqeMsgIds };
    });

});
person bigmadwolf    schedule 05.01.2015
comment
найдя это обсуждение meta.telesc.pe/posts/y4piwYfDhzJMPpHdb, я чувствую, что это правильно способ сделать это. - person bigmadwolf; 07.01.2015

Вот окончательный рабочий код. Это действительно возвращает курсор, поскольку в основном то, что я делаю, - это получение результатов от нескольких запросов/модификаций и закачка набора идентификаторов в окончательный запрос .find.

Meteor.publish('unreads', function() {
    if (!this.userId) {
        throw new Meteor.Error('denied', 'not-authorized');
    }

    // setup some vars
    var messageQuery,
        messages,
        userGroupQuery,
        userGroups,
        uniqeMsgIds;
    var self        = this;
    var user        = Meteor.users.findOne(self.userId);
    var userIdArr   = [self.userId]; // for use where queries require an array
    var contacts    = user.contacts;

    // get groups
    userGroupQuery  = Groups.find({
        $or : [
            { owner : self.userId },
            { members : self.userId }
        ]
    }, { // Projection to only return the _id field
            fields : { _id:1 }
        }
    );

    // create an array of group id's that belong to the user.
    userGroups = _.pluck(userGroupQuery.fetch(), '_id');

    messages = Messages.find({
        $or : [
            {  // unread direct messages
                $and : [
                    { participant : self.userId },
                    { userId : { $in : contacts } },
                    { readBy : { $nin : userIdArr } }
                ]
            },
            {  // unread group messages
                $and : [
                    { groupId : { $in : userGroups } },
                    { readBy : { $nin : userIdArr } }
                ]
            },
        ]
    }, { sort : { // put newest messages first
            time : -1
        }
    });

     // returns an array of unique documents based on userId or groupId
    uniqueMessages = _.uniq(messages.fetch(), function(msg) {
        if (msg.groupId) {
            return msg.groupId;
        }
        return msg.userId;
    });

    /*  Get the id's of all the latest unread messages
    (one per user or group) */
    uniqeMsgIds = _.pluck(uniqueMessages, '_id');

    /*  finally publish a reactive cursor containing
    one unread message(latest) for each user/group */
    return Messages.find({
        _id : { $in : uniqeMsgIds }
    });

});
person bigmadwolf    schedule 07.01.2015