2015-02-12 3 views
0

Я пытаюсь реализовать функцию, которая собирает непрочитанные сообщения из коллекции статей. В каждой статье в сборнике есть запись «обсуждение» с субдокументами комментариев к обсуждению. Примером такого поддокументе является:Mongo Aggregate: как сравнить с полем из другой коллекции?

{ 
    "id": NumberLong(7534), 
    "user": DBRef("users", ObjectId("...")), 
    "dt_create": ISODate("2015-01-26T00:10:44Z"), 
    "content": "The discussion comment content" 
} 

Родительский документ имеет следующую (частичную) структуру:

{ 
    model: { 
     id: 17676, 
     title: "Article title", 
     author: DBRef("users", ObjectId(...)), 
     // a bunch of other fields here 
    }, 
    statistics: { 
     // Statistics will be stored here (pageviews, etc) 
    }, 
    discussions: [ 
     // Array of discussion subdocuments, like the one above 
    ] 
} 

Каждый пользователь также имеет last_viewed запись, которая является документом, примером может служить следующее :

{ 
    "17676" : "2015-01-10T00:00:00.000Z", 
    "18038" : "2015-01-10T00:00:00.000Z", 
    "18242" : "2015-01-20T00:00:00.000Z", 
    "18325" : "2015-01-20T00:00:00.000Z" 
} 

Это означает, что пользователь смотрел на обсуждение комментариев в последний раз 10 января 2015 года для статей с идентификаторами 17676 и 18038, а 20 января го 2015 года для статей с идентификаторами 18242 и 18325.

Поэтому я хочу собрать записи обсуждения из документов статьи, а для статьи с ID 17676 я хочу собрать записи обсуждения, созданные после 2015-01-10, и для статьи с ID 18242 я хочу показать записи обсуждения, созданные после 2015-01-20.

ОБНОВЛЕНО

Основываясь на Neil Lunn's reply, функция я создал до сих пор:

function getUnreadDiscussions(userid) { 
    user = db.users.findOne({ 'model.id': userid }); 
    last_viewed = []; 

    for(var i in user.last_viewed) { 
     last_viewed.push({ 
      'id': parseInt(i), 
      'dt': user.last_viewed[i] 
     }); 
    } 

    result = db.articles.aggregate([ 
     // For now, collect just articles the user has written 
     { $match: { 'model.author': DBRef('users', user._id) } }, 
     { $unwind: '$discussions' }, 
     { $project: { 
       'model': '$model', 
       'discussions': '$discussions', 
       'last_viewed': { 
        '$let': { 
         'vars': { 'last_viewed': last_viewed }, 
         'in': { 
          '$setDifference': [ 
           { '$map': { 
            'input': '$$last_viewed', 
            'as': 'last_viewed', 
            'in': { 
             '$cond': [ 
              { '$eq': [ '$$last_viewed.id', '$model.id' ] }, 
              '$$last_viewed.dt', 
              false 
             ] 
            } 
           } }, 
           [ false ] 
          ] 
         } 
        } 
       } 
      } 
     }, 
     // To get a scalar instead of a 1-element array: 
     { $unwind: '$last_viewed' }, 
     // Match only those that were created after last_viewed 
     { $match: { 'discussions.dt_create': { $gt: '$last_viewed' } } }, 
     { $project: { 
      'model.id': 1, 
      'model.title': 1, 
      'discussions': 1, 
      'last_viewed': 1 
     } } 
    ]); 

    return result.toArray(); 
} 

Вся $let вещь, и $unwind после того, преобразует данные в следующую частичную проекцию (с последними $match не заполнено):

{ 
    "_id" : ObjectId("54d9af1dca71d8054c8d0ee3"), 
    "model" : { 
     "id" : NumberLong(18325), 
     "title" : "Article title" 
    }, 
    "discussions" : { 
     "id" : NumberLong(7543), 
     "user" : DBRef("users", ObjectId("54d9ae24ca71d8054c8b4567")), 
     "dt_create" : ISODate("2015-01-26T00:10:44Z"), 
     "content" : "Some comment here" 
    }, 
    "last_viewed" : ISODate("2015-01-20T00:00:00Z") 
}, 
{ 
    "_id" : ObjectId("54d9af1dca71d8054c8d0ee3"), 
    "model" : { 
     "id" : NumberLong(18325), 
     "title" : "Article title" 
    }, 
    "discussions" : { 
     "id" : NumberLong(7554), 
     "user" : DBRef("users", ObjectId("54d9ae24ca71d8054c8b4567")), 
     "dt_create" : ISODate("2015-01-26T02:03:22Z"), 
     "content" : "Another comment here" 
    }, 
    "last_viewed" : ISODate("2015-01-20T00:00:00Z") 
} 

Пока все хорошо. Но проблема в том, что $match для выбора только обсуждений, созданных после даты last_viewed, не работает. Я получаю пустой ответ массива. Однако, если я скорректирую дату и поставлю $match: { 'discussions.dt_create': { $gt: ISODate("2015-01-20 00:00:00") } }, она работает. Но я хочу, чтобы он взял его с last_viewed.

+1

Поток выглядит следующим образом: сначала выполняется сценарий java в коде, затем код агрегации передается на сервер. Таким образом, оценка 'last_viewed ['$ model.id']' происходит до того, как выполняется код агрегации. Переменная 'last_viewed' не имеет свойства' $ model.id', что приводит к неопределенному значению. Следовательно, это неверный путь. – BatScream

+0

Спасибо @BatScream. Да, я нашел это: http://stackoverflow.com/questions/24335983/how-to-use-a-javascript-object-inside-mongodb-aggregation-pipeline, и он упоминает, что внешняя переменная действительно не будет доступна в пределах процесс агрегации.Я смотрю на ответ Нейла там, задаваясь вопросом, могу ли я переформатировать свой объект last_viewed таким образом и посмотреть, могу ли я заставить его работать, используя '$ let'. Но я не уверен, как работает блок '$ let'. –

+1

Вы не сможете динамически ссылаться на значение поля, которое вы пытаетесь сделать. Было бы неплохо, если бы вы могли опубликовать полный пример родительского документа записей обсуждения, потому что тогда я мог бы более конкретно рассказать о том, как следует структурировать вещи, но я думаю, что лучший путь вперед - это иметь записи обсуждения отдельные документы. Вероятно, вам нужно будет использовать один запрос для каждой статьи, чтобы получить непрочитанные обсуждения. – wdberkeley

ответ

0

Я нашел другую нить SO, где эта проблема была разрешена using the $cmp operator.

Заключительная часть агрегации будет:

[ 
    { /* $match, $unwind, $project, $unwind as before */ }, 
    { $project: { 
     'model': 1, 
     'discussions': 1, 
     'last_viewed': 1, 
     'compare': { 
      $cmp: [ '$discussions.dt_create', '$last_viewed' ] 
     } 
    } }, 
    { $match: { 'compare': { $gt: 0 } } } 
] 

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

Я сохраню вопрос без ответа на тот случай, если у кого-нибудь будет лучший ответ/метод. Если бы этот ответ был достаточно популярен, я соглашусь с этим.

Смежные вопросы