Map减少MongoDB中的帮助

时间:2013-07-09 16:49:28

标签: mongodb mapreduce

我有一个名为'elements'的集合,用于存储课程和曲目。课程和曲目使用名为cms_content_type的字段进行区分,该字段是课程的“课程”和课程的“跟踪”。有一个cms_id字段,它唯一地标识一个元素,无论是轨道还是课程。轨道由track_lessons字段中的一个或多个课程组成。

课程文件的结构是:

{
    '_id': ObjectId('1234'),
    'name': 'Working with Tags',
    'cms_content_type': 'Lesson',
    'cms_id': 'abcd1234'
}

跟踪文档的结构是:

{
    '_id': ObjectId('4567'),
    'name': 'Tags 101',
    'cms_content_type': 'Track',
    'cms_id': 'pqrs4567'
    'track_lessons':[
        {'lesson_cms_id': 'efgh1234'},
        {'lesson_cms_id': 'jklm1234'}
    ]
}

我想查询此元素集合,以获取任何曲目中 的课程列表。我怎样才能做到这一点?我是MapReduce的新手。我是否必须使用它来获取此列表?

另一个要求是我应该能够对检索到的课程列表进行排序和分页。所以我更喜欢编写一个动态查询,而不是将结果存储在一个单独的集合中,然后对其进行处理。

非常感谢任何帮助。

2 个答案:

答案 0 :(得分:0)

  

我想查询此元素集合以获取不是的课程列表   出现在任何轨道上。我怎样才能做到这一点?

您不能使用M / R,因为它只适用于一个集合。重新设计模式是您当然希望防止的,但是现在您有一个不足以满足您需求的模式。在MongoDB中,您可以根据使用数据的方式设计模式,这与根据规范化规则设计模式的RDBM不同

因此,到目前为止,最简单的方法是将音轨的cms_id存储为带有课程文档的数组:

{
    '_id': ObjectId('1234'),
    'name': 'Working with Tags',
    'cms_content_type': 'Lesson',
    'cms_id': 'abcd1234',
    'tracks': [ 'pqrs4567' ]
}

这样,您不需要进行繁琐的M / R查询,您可以像以下一样轻松查询:

db.col.find( { tracks: { $size: 0 } );

您不应该将它们存储为嵌套数组,就像您在相反方向上所做的那样,而是只存储ID:

{
    '_id': ObjectId('4567'),
    'name': 'Tags 101',
    'cms_content_type': 'Track',
    'cms_id': 'pqrs4567'
    'track_lessons': [ 'efgh1234', 'jklm1234' ]
}

如果在track_lessons(或轨道)上创建索引,这会使事情变得更加优化。但我建议你完全不用track_lessons,而是将课程存储在课程中:

{
    '_id': ObjectId('1234'),
    'name': 'Working with Tags',
    'cms_content_type': 'Lesson',
    'cms_id': 'abcd1234',
    'tracks': [ 'pqrs4567' ]
}
{
    '_id': ObjectId('4567'),
    'name': 'Tags 101',
    'cms_content_type': 'Track',
    'cms_id': 'pqrs4567'
}

即使您没有存储属于曲目的所有课程(例如标签101),您仍然可以使用以下查询轻松查询属于曲目的所有课程:

db.col.find( { tracks: 'pqrs4567' } );

通过进行这些更改,您可以完全忘记M / R,这使您的应用程序更易于维护和更快。

答案 1 :(得分:0)

我怀疑M / R在这里可以提供任何帮助。因为使用您当前的设计,您需要检查另一个集合(即课程和曲目)上的一个集合中的某些ID。在M / R中,您无法从map reduce函数运行db查询。当前的设计打破了数据局部性,并且在外键使用方面往往更像关系型。因此,您需要使用查询的性能(类型的连接)来支付。 Derick的解决方案试图通过在每个文档中包含所需信息来克服这种局部性问题。我完全鼓励您将所需数据放在一个文档中。

如果你必须使用当前的设计,那么直截了当的解决方案是:

var lessons_without_task = [];
db.lessons.find().forEach(
   function(lesson_doc) {
      var task = db.tasks.findOne({ "track_lessons.lesson_cms_id" : lesson_doc.cms_id });
      if (task == null) {
        lessons_without_task.push(lesson_doc.cms_id);
      }
   }
)

我在这里假设您在track_lessons.lesson_cms_id上定义了多键索引。见http://docs.mongodb.org/manual/core/indexes/#index-type-multi-key 您可能需要为大型集合添加以下选项(在find()之后) addOption(DBQuery.Option.awaitData).addOption(DBQuery.Option.noTimeout)