MongoDB列表 - 获取每个第N项

Yar*_*mer 15 mongodb mongodb-query aggregation-framework

我有一个看起来大致如下的Mongodb架构:

[
  {
    "name" : "name1",
    "instances" : [ 
      {
        "value" : 1,
        "date" : ISODate("2015-03-04T00:00:00.000Z")            
      }, 
      {
        "value" : 2,
        "date" : ISODate("2015-04-01T00:00:00.000Z")
      }, 
      {
        "value" : 2.5,
        "date" : ISODate("2015-03-05T00:00:00.000Z")
      },
      ...
    ]
  },
  {
    "name" : "name2",
    "instances" : [ 
      ...
    ]
  }
]
Run Code Online (Sandbox Code Playgroud)

其中每个元素的实例数量可能非常大.

我有时想只得到一个数据样本,也就是说,获取每个第三个实例,或每第10个实例......你得到了图片.

我可以通过获取所有实例并在我的服务器代码中过滤它们来实现这一目标,但我想知道是否有办法通过使用一些聚合查询来实现.

有任何想法吗?


更新

假设数据结构是扁平的,如下面的@SylvainLeroux所示,那就是:

[
  {"name": "name1", "value": 1, "date": ISODate("2015-03-04T00:00:00.000Z")},
  {"name": "name2", "value": 5, "date": ISODate("2015-04-04T00:00:00.000Z")},
  {"name": "name1", "value": 2, "date": ISODate("2015-04-01T00:00:00.000Z")},
  {"name": "name1", "value": 2.5, "date": ISODate("2015-03-05T00:00:00.000Z")},
  ...
]
Run Code Online (Sandbox Code Playgroud)

获得每个第N项(具体name)的任务会更容易吗?

Bla*_*ven 8

似乎你的问题清楚地问到"得到每个第n个实例",这似乎是一个非常明确的问题.

查询操作.find()实际上只能"按原样"返回文档,但投影和运算符(如位置$匹配运算符)或$elemMatch允许奇异匹配数组元素的运算符除外.

当然有$slice,但这只是允许阵列上的"范围选择",所以再次不适用.

可以修改服务器上的结果的"唯一"事物是.aggregate().mapReduce().前者不会以任何方式"切片"数组"发挥得非常好",至少不是"n"元素.但是由于mapReduce的"function()"参数是基于JavaScript的逻辑,因此您可以有更多的空间来玩.

对于分析过程和"仅"的分析目的,只需使用mapReduce过滤数组内容.filter():

db.collection.mapReduce(
    function() {
        var id = this._id;
        delete this._id;

        // filter the content of "instances" to every 3rd item only
        this.instances = this.instances.filter(function(el,idx) {
            return ((idx+1) % 3) == 0;
        });
        emit(id,this);
    },
    function() {},
    { "out": { "inline": 1 } } // or output to collection as required
)
Run Code Online (Sandbox Code Playgroud)

在这一点上它实际上只是一个"JavaScript跑步者",但如果这只是用于分析/测试,那么这个概念通常没有任何错误.当然,输出并不是"完全"文档的结构,但它与mapReduce的传真接近.

我在这里看到的另一个建议是创建一个包含所有项目"非规范化"的新集合,并从数组中插入"index"作为unqique _id键的一部分.这可能会产生一些你可以直接查询的东西,对于你仍然需要做的"每个第n项"来说:

db.resultCollection.find({
     "_id.index": { "$in": [2,5,8,11,14] } // and so on ....
})
Run Code Online (Sandbox Code Playgroud)

因此,计算并提供"每第n项"的索引值以获得"每第n项".所以这似乎并没有真正解决所提出的问题.

如果输出形式似乎更适合您的"测试"目的,那么对这些结果的更好的后续查询将使用聚合管道, $redact

db.newCollection([
    { "$redact": {
        "$cond": {
            "if": {
                "$eq": [ 
                    { "$mod": [ { "$add": [ "$_id.index", 1] }, 3 ] },
                0 ]
            },
            "then": "$$KEEP",
            "else": "$$PRUNE"
        }
    }}
])
Run Code Online (Sandbox Code Playgroud)

至少使用"逻辑条件"与.filter()之前应用的"逻辑条件"非常相同,只选择"第n个索引"项而不将所有可能的索引值列为查询参数.


use*_*814 5

没有$unwind这里需要。您可以使用$pushwith $arrayElemAt将数组值投影到$group聚合内的请求索引处。

就像是

db.colname.aggregate(
[
  {"$group":{
    "_id":null,
    "valuesatNthindex":{"$push":{"$arrayElemAt":["$instances",N]}
    }}
  },
  {"$project":{"valuesatNthindex":1}}
])
Run Code Online (Sandbox Code Playgroud)

  • 此命令是否需要将整个集合加载到内存中? (2认同)
  • 我认为他是在问是否需要将完整集合加载到服务器内存中。 (2认同)

Ash*_*shh 4

您可能喜欢这种使用$lookup聚合的方法。这可能是最方便、最快的方法,无需任何聚合技巧。

Names使用以下架构创建集合

[
  { "_id": 1, "name": "name1" },
  { "_id": 2, "name": "name2" }
]
Run Code Online (Sandbox Code Playgroud)

然后Instances集合的父 id 为"nameId"

[
  { "nameId": 1, "value" : 1, "date" : ISODate("2015-03-04T00:00:00.000Z") },
  { "nameId": 1, "value" : 2, "date" : ISODate("2015-04-01T00:00:00.000Z") },
  { "nameId": 1, "value" : 3, "date" : ISODate("2015-03-05T00:00:00.000Z") },
  { "nameId": 2, "value" : 7, "date" : ISODate("2015-03-04T00:00:00.000Z") }, 
  { "nameId": 2, "value" : 8, "date" : ISODate("2015-04-01T00:00:00.000Z") }, 
  { "nameId": 2, "value" : 4, "date" : ISODate("2015-03-05T00:00:00.000Z") }
]
Run Code Online (Sandbox Code Playgroud)

现在,使用$lookup聚合3.6语法,您可以$sample在 内部使用来随机获取每个第 N个元素。$lookup pipeline

db.Names.aggregate([
  { "$lookup": {
    "from": Instances.collection.name,
    "let": { "nameId": "$_id" },
    "pipeline": [
      { "$match": { "$expr": { "$eq": ["$nameId", "$$nameId"] }}},
      { "$sample": { "size": N }}
    ],
    "as": "instances"
  }}
])
Run Code Online (Sandbox Code Playgroud)

你可以在这里测试一下