Use MongoDB array as stack

2019-07-16 16:13发布

Has anybody used MongoDB's Array type to implement a stack?

I know that I can append to an Array like so:

db.blogposts.update( {_id:5}, {$push: {comments: {by: "Abe", text:"First"}}})

Here, the end of the array is the top of the stack... I don't see a way to implement this with the top of the stack at the zero'th index, but I'd love to be wrong.

And I know that I can peek at the last value of the the array like so:

db.blogposts.find( {_id:5}, {comments: {$slice:-1}})

With an implementation like this, can I "peek" at the top of the stack in a MongoDB update statement? That would give me the semantic, "push this item on the stack if the top of the stack is X". I need this to be an atomic operation!

Any advice appreciated. Thanks!

标签: mongodb stack
2条回答
太酷不给撩
2楼-- · 2019-07-16 16:34

Looks like as of mongoDB v2.6, this is now supported via the $position operator: http://docs.mongodb.org/manual/reference/operator/update/position/

db.blogposts.update(
  {_id:5}
  , {$push:
      {comments:
          {$each: {by: "Abe", text:"First"}
          , $position:0 }
      }
  }
);
查看更多
孤傲高冷的网名
3楼-- · 2019-07-16 16:53

Unfortunately, there is currently no way to do this exactly as you have described.

As Chris Shain pointed out, https://jira.mongodb.org/browse/SERVER-2191 - "$push() to front of array" and similarly https://jira.mongodb.org/browse/SERVER-1824 - "Support for inserting into specific array index" would help, but these features are currently not slated for a specific release version.

As a possible work-around, you could add a field named "lastElement" (or equivalent) to your document, which contains a copy of the last element pushed to the array. In your update statement, you could then query against the "lastElement" value, and if it matches, simultaneously set it to the new value and push the same value to the array in a single, atomic operation.

For example:

> db.blogposts.save({_id:5, comments:[{by: "Abe", text:"First"}], lastElement:{by: "Abe", text:"First"}})
> db.blogposts.find().pretty()
{
    "_id" : 5,
    "comments" : [
        {
            "by" : "Abe",
            "text" : "First"
        }
    ],
    "lastElement" : {
        "by" : "Abe",
        "text" : "First"
    }
}
> db.blogposts.update({"lastElement.text":"First"}, {$set:{lastElement:{by: "Joe", text:"Second"}}, $push:{comments:{by: "Joe", text:"Second"}}})
> db.blogposts.find().pretty()
{
    "_id" : 5,
    "comments" : [
        {
            "by" : "Abe",
            "text" : "First"
        },
        {
            "by" : "Joe",
            "text" : "Second"
        }
    ],
    "lastElement" : {
        "by" : "Joe",
        "text" : "Second"
    }
}
> 

As an alternative, you may consider the strategy outlined in the "Update if Current" section of the "Atomic Operations" documentation: http://www.mongodb.org/display/DOCS/Atomic+Operations

I realize these are work-arounds and not ideal solutions. Hopefully the above will help you to accomplish your goal, or at least provide some food for thought for you to come up with a different solution. If you do, please share it here so that any members of the Community who may be experiencing similar issues may have the benefit of your experience. Thanks.

查看更多
登录 后发表回答