高级mongodb集合排序

时间:2016-07-09 13:00:59

标签: mongodb sorting

我需要帮助mongodb中的一些高级集合排序。假设我们有数据库本地,我们有以下模型:

收集大陆的文件 {“_ id”:1,“name”:“Europe”},{“_ id”:2,“name”:“Asia”},{“_ id”:3,“name”:“North America”}, {“_ id”:4,“name”:“South America”},{“_ id”:5,“name”:“Australia”},{“_ id”:6,“name”:“Africa”}

带文件的收集国家/地区 { “_id”:1, “名”: “法国”, “populationInMillions”:66, “大陆”:DBREF( “大陆”,1, “本地”), “城市”:[{ “Name”:“巴黎“},{” 名称 “:” 马赛 “},{” 名称 “:” 图卢兹“}]},

{ “_编码”:2 “名称”: “西班牙”, “populationInMillions”:47, “非洲大陆”:DBREF( “大陆”,1, “本地”), “城市”:[{ “姓名” : “马德里”},{ “名称”: “塞维利亚”},{ “名称”: “巴伦西亚”}]},

{ “_ ID”:3 “名称为”: “中国”, “populationInMillions”:1360, “非洲大陆”:DBREF( “大陆”,2, “本地”), “城市”:[{ “姓名” : “北京”},{ “名称”: “渝”},{ “名称”: “上海”}]},

{ “_编码”:4 “名称”: “巴西”, “populationInMillions”:200, “非洲大陆”:DBREF( “大陆”,4 “本地”), “城市”:[{ “姓名” :“Sao Paulo”},{“name”:“Rio de Janeiro”},{“name”:“Salvador”}}}

因此,当我们想通过一些简单的标准(如populationInMillions降序)对国家进行排序时,我们将使用查询: db.country.find({})。sort({populationInMillions:-1})

我的问题是如果我们想按照以下某些复杂标准进行排序 (本例中的一些在现实世界中有意义,有些没有意义,但重点在于技术解决方案。 我必须在现实世界的项目中应用类似的解决方案。)

排序国家/地区: 1.以他们大陆的名义(考虑到我们没有子对象而是DBRef)

  1. ,其中peopleInMillions大于1000的国家位于其他国家之前
  2. 按其所有城市名称中的字符总数(例如法国:巴黎(5个字符),马赛(9个字符),图卢兹(8个字符) - 总共22个字符)
  3. 按国家/地区的第二个城市的名称(在本例中为法国的马赛,西班牙的塞维利亚等)
  4. 如果您对所有或部分问题有答案,请提供帮助。 提前谢谢!

2 个答案:

答案 0 :(得分:2)

从目前存储文档的方式来看,我认为解决方案既昂贵又不可能,因为根据这两个集合的关系添加了更多的排序,尤其是涉及分页时。我建议你把大陆信息放入国家馆藏。 MongoDB被设计为非规范化,更好地利用它。

1 - 按大陆名称排序国家/地区

  1. 按所需顺序拉出各大洲的完整列表。
  2. 使用非洲大陆的_id,您可以使用$in运营商提取国家/地区列表。
  3. 使用HashMap
  4. 将两个列表映射在一起

    问题:在这种情况下几乎不可能进行分页。这是低效的,重复的结果是可能的,你不可能自己排序国家,只有大陆和#39;名称可以排序。

    2 - 优先排序首先超过1000的国家

    我真的不明白你用这个想要实现的目标。按人口计数排序似乎很好地解决了这个问题。但是,如果您需要以下内容:

    |----------------|
    |populationCount |
    |----------------|
    |2500            |
    |2030            |
    |2110            |
    |2666            |
    |1999            |
    |800             |
    |600             |
    |700             |
    |----------------|
    

    为此,您可以在国家/地区集合中添加权重列。对于超过一定数量的populationInMillions(在您的情况下为1000)的所有国家/地区,请将其设置为更高的权重,其余的则为更低的权重。这样,您可以根据需要使用db.Countries.sort({weight : -1})db.Countries.sort({weight : -1, populationInMillions : -1})对其进行排序。它将是这样的:

    |----------------|------|
    |populationCount |weight|
    |----------------|------|
    |2500            |2     |
    |2030            |2     |
    |2110            |2     |
    |2666            |2     |
    |1999            |2     |
    |800             |1     |
    |600             |1     |
    |700             |1     |
    |----------------|------|
    

    3 - 按所有城市名称中的总字符数排序。

    我不认为MongoDB有一种方法可以动态执行该查询,但由于城市名称没有改变,您可以在添加或删除城市时存储总字符数。国家。这样你可以用那个列排序。易于执行和排序可以编制索引。表现友好。

    4 - 按字母顺序排列第二个城市的名称。

    我不知道这意味着什么。有什么例子吗?

    p / s:当需要对某些内容进行排序时,请尝试确保标准位于一个集合中以便于查询。

答案 1 :(得分:1)

我绝对同意在mongodb中应该避免规范化,在上面的例子中我们应该将该大陆作为该国的一个子对象,以便我们可以轻松地对非洲大陆的国家进行过滤和排序。

在上一个答案中,通过向数据模型添加新字段来实现任务有一些很好的建议。在进行了一些mongodb研究后,我发现了另一种实现结果的方法,而没有真正改变数据模型。该解决方案使用聚合。让我们看一下示例2(以人口百万大于1000的国家位于其他国家之前的方式对国家进行排序)。这种类型的解决方案通常可以应用于许多其他自定义排序标准:

db.country.aggregate( [
   { $project: 
        { _id: "$_id",  
            name : "$name",
            populationInMillions : "$populationInMillions",
            cities : "$cities",
            populationRank: { $cond: { if: { $gt : [ "$populationInMillions" , 1000 ] }, then: 0 , else: 1 }} 
        }
    },
    { $sort : {'populationRank' : 1 /*, 'anotherField1' : -1, 'anotherField2' : 1*/} },
    { $project : { /*We can skip this projection if we don't want to exclude populationRank from the result*/ 
            _id : "$_id", 
            name : "$name", 
            populationInMillions : "$populationInMillions", 
            cities : "$cities" } 
    }
] );

对于示例数字3(按其所有城市名称中的字符总数),我们很遗憾没有$ strlen函数,但它将在未来的mongodb版本中添加。 https://jira.mongodb.org/browse/SERVER-5319 但是如果我们假设我们已经有了$ strlen函数,那么这里是第3个示例的有趣解决方案,它也可以为不依赖strlen的其他自定义排序条件提供一个想法:

db.country.aggregate(
    [ { $unwind : "$cities" },
        { $group : { 
            _id : "$_id", 
            name : { $max : "$name" }, 
            populationInMillions : { $max : "$populationInMillions" }, 
            cities : { $push : "$cities" }, 
            citiesCharCount : { $sum : { $strlen : "$cities.name" } } } },
        { $sort : { citiesCharCount : 1 } },
        { $project : { /*We can skip this projection if we don't want to exclude citiesCharCount from the result*/  
            _id : "$_id", 
            name : "$name", 
            populationInMillions : "$populationInMillions", 
            cities : "$cities" } 
        }
    ]
);

没有strlen函数,有基于mapResuce和自定义javascript函数的解决方案https://docs.mongodb.com/manual/tutorial/map-reduce-examples/