MongoDB lists - get every Nth item

前端 未结 6 2360
太阳男子
太阳男子 2021-02-13 05:36

I have a Mongodb schema that looks roughly like:

[
  {
    \"name\" : \"name1\",
    \"instances\" : [ 
      {
        \"value\" : 1,
        \"date\" : ISODate         


        
6条回答
  •  别跟我提以往
    2021-02-13 06:06

    It seems that your question clearly asked "get every nth instance" which does seem like a pretty clear question.

    Query operations like .find() can really only return the document "as is" with the exception of general field "selection" in projection and operators such as the positional $ match operator or $elemMatch that allow a singular matched array element.

    Of course there is $slice, but that just allows a "range selection" on the array, so again does not apply.

    The "only" things that can modify a result on the server are .aggregate() and .mapReduce(). The former does not "play very well" with "slicing" arrays in any way, at least not by "n" elements. However since the "function()" arguments of mapReduce are JavaScript based logic, then you have a little more room to play with.

    For analytical processes, and for analytical purposes "only" then just filter the array contents via mapReduce using .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
    )
    

    It's really just a "JavaScript runner" at this point, but if this is just for anaylsis/testing then there is nothing generally wrong with the concept. Of course the output is not "exactly" how your document is structured, but it's as near a facsimile as mapReduce can get.

    The other suggestion I see here requires creating a new collection with all the items "denormalized" and inserting the "index" from the array as part of the unqique _id key. That may produce something you can query directly, bu for the "every nth item" you would still have to do:

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

    So work out and provide the index value of "every nth item" in order to get "every nth item". So that doesn't really seem to solve the problem that was asked.

    If the output form seemed more desirable for your "testing" purposes, then a better subsequent query on those results would be using the aggregation pipeline, with $redact

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

    That at least uses a "logical condition" much the same as what was applied with .filter() before to just select the "nth index" items without listing all possible index values as a query argument.

提交回复
热议问题