2014-01-10 7 views
3

У меня есть несколько «продукта» объекты:MongoDB: совпадение и сортировка по элементу массива?

{ name: 'ProductA', 
    social_stats: [ 
    { name: 'facebook', 
     shares: 60 
    }, 
    { name: 'twitter', 
     shares: 0 
    } 
    ] 
} 

{ name: 'ProductB', 
    social_stats: [ 
    { name: 'facebook', 
     shares: 0 
    }, 
    { name: 'twitter', 
     shares: 30 
    } 
    ] 
} 

Я хотел бы запросить «наиболее общие продукты на Facebook» и «наиболее общих продукты на Twitter», всегда отсортированные от наиболее до наименее акций.

Так что мой первый запрос для Facebook выглядит как:

db.videos.find({ 
    _id: { 
    social_stats: { 
     $elemMatch: { 
     name: 'facebook' 
     } 
    } 
    } 
).sort({ 
    social_stats: { 
    shares: -1 
    } 
}) 

Урожайность:

{ name: 'ProductA' } 
{ name: 'ProductB' } 

Это «правильный», но когда я запускаю тот же запрос для «твиттере» вместо этого, я ожидаю, B-> A, но получить тот же результат, что и выше. Похоже, что мы не применяем логику сортировки &, так как я предполагаю, что «упорядочивается элементом social_stat, который соответствует« twitter ».

Что я ищу

  • Как изменением мой запрос, чтобы отразить применение порядка(), чтобы соответствовать social_stat элементов?
  • Если это невозможно с этими обычными запросами MongoDB, это то, что я могу сделать с помощью структуры агрегации? И как это будет выглядеть?
  • Тогда как бонус, как бы закодировать эквивалентный запрос с Mongoid?

Некоторые родственные связи, я посмотрел на:

ответ

4

Вы не можете получить sort() свои результаты по массиву, так что это не приведет к тому, что вы после.

Ваш лучший подход (как в MongoDB 2.4) будет использовать Aggregation Framework:

db.videos.aggregate(
    // Optional: potentially take advantage of an index to only find videos with 
    //   facebook stats; could also limit to those with shares $gt 0 
    { $match: { 
     'social_stats.name' : 'facebook' 
    }}, 

    // Convert the social_stats array into a document stream 
    { $unwind: '$social_stats' }, 

    // Only match the social stats for facebook 
    { $match: { 
     'social_stats.name' : 'facebook' 
    }}, 

    // Sort in descending order 
    { $sort: { 
     'social_stats.shares' : -1 
    }}, 

    // Only include the product names & score 
    { $project: { 
     _id: 0, 
     name: "$name", 
     shares: "$social_stats.shares" 
    }} 
) 

Результаты для 'твиттере':

{ 
    "result" : [ 
     { 
      "name" : "ProductB", 
      "shares" : 30 
     }, 
     { 
      "name" : "ProductA", 
      "shares" : 0 
     } 
    ], 
    "ok" : 1 
} 

Результаты для 'Facebook':

{ 
    "result" : [ 
     { 
      "name" : "ProductA", 
      "shares" : 60 
     }, 
     { 
      "name" : "ProductB", 
      "shares" : 0 
     } 
    ], 
    "ok" : 1 
} 
+0

Это должно быть легко перевести на Mongoid, но [Mongoid и MongoDB Aggregation Framework] (http://blog.joshsoftware.com/2013/09/05/mongoid-and-the-mongodb-aggregation-framework/) может быть полезным началом. – Stennie

+0

Это именно то, что я закончил делать, и структура агрегации отлично справилась с этим. У Mongoid есть функция Model.collection.aggregate() ', которую вы можете передать массиву команд для выполнения (например,' [match, unwind, match, sort, project] '), поэтому он был довольно тривиальным, переводя запрос MongoDB с Монгоидом. –

1

С social_stats является массив, я думаю, вам понадобится структура агрегации или MapReduce. Но если изменить структуру

{ name: 'ProductB', 
    social_stats : { 
    'facebook' : {shares: 0, name : "Facebook"}, 
    'twitter' : {shares: 30, name : "Twitter"}, 
    } 
} 

... вы могли бы сделать:

.sort({"social_stats.facebook.shares" : -1}) 

Примечание: Это потребует изменения вашего запроса, как хорошо!

+0

Нет кубиков, одинаковый результат. –

+0

Правильно, я не думал о том, что 'social_stats' был встроенным массивом. См. Мое обновление. – luttkens

+0

Я думаю, что это прекрасное решение, но я не уверен, насколько хорошо он играет с монгольскими и рубиновыми классами. В частности, похоже, что мне нужно изменить отношение 'embands_many' в поле типа: Hash, чтобы сделать его совместимым. Конечно, практичный, но, возможно, не предпочтительный для моего случая использования. Все равно достойный голосования. –

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