3

I have a DB with a set of fields f1,f2,f3, where f3 is an array. I would like to do an update operation such as this

db.collection.update ({f1:1},{$push:{f3:{$each:[f2's value],$slice:-2}}})

I searched on the internet and was not abole to find anything. I am not sure if this is even possible. Any help would be much appreciated.

EXAMPLE:

This is my set of documents:

doc1 = { name: "User1", score: "Good",  History of scores: ["Good", "Bad", "Average", "Bad"] }
doc2 = { name: "User2", score: "Bad",  History of scores: ["Good", "Average", "Average", "Bad"] }
doc3 = { name: "User3", score: "Good",  History of scores: ["Good", "Good", "Average", "Good"] }

Now suppose we have to insert the corresponding data:

{name : "User1", score: "Good"}

I would like the document to update user1's history of scores so that doc1 becomes as follows:

doc1 = { name: "User1", score: "Good", History of scores: ["Bad", "Average", "Bad", "Good"] }

another of the same update should change doc1 to:

doc1 = { name: "User1", score: "Good", History of scores: ["Average", "Bad", "Good", "Good"] }

I hope now my question has become clearer. Thanks.

2
  • 1
    it would be nice, if you add some example Commented Jun 18, 2013 at 7:41
  • @artech thanks. I have added an example. Commented Jun 18, 2013 at 21:19

2 Answers 2

2

Try this:

> db.c.find()
{ "_id" : ObjectId("51c156d25a334e9347b576a7"), "name" : "User1", "score" : "Good", "scores" : [ "Good", "Bad", "Average", "Bad" ] }
> db.c.update({}, {$push: {scores:{$each:['111', '222'], '$slice': -4}}})
> db.c.find()
{ "_id" : ObjectId("51c156d25a334e9347b576a7"), "name" : "User1", "score" : "Good", "scores" : [ "Average", "Bad", "111", "222" ] }

btw, I there is a problem with this kind of updates: if new object is grater then previous in size, it cause moving this object to another location on disk(e.g. you pushed "Average" and popped "Bad"). Updates "in-place" is faster, you can preallocate space for objects on first insert, like so:

> db.c.insert({ "_id" : ObjectId("51c156d25a334e9347b576a7"), "name" : "<big_tmp_string>", "score" : "<big_tmp_string>", "scores" : [ "<big_tmp_string>", "<big_tmp_string>", "<big_tmp_string>", "<big_tmp_string>" ] })
> db.c.update({ "_id" : ObjectId("51c156d25a334e9347b576a7")}, {<your_real_obj>}
Sign up to request clarification or add additional context in comments.

1 Comment

@arrtech Thanks. However in this example the values '111' and '222' are known and inserted manually by the user every time the query is requested. The kind of functionality I was hoping for was to take the values '111' and '222' contained in another field, say 'score', so that the values need not be inserted manually into the query. The pointer about the size of the new object was a really new thing and very useful information. Thank you.
1

Now that update commands can contain pipelines as of MongoDB 4.2, something like this is possible.

db.collection.updateOne({ f1: 1 }, [{
  $set: {
    historyOfScores: {
      $concatArrays: [
        "$historyOfScores",
        ["$score"]
      ]
    }
  }
}, {
  $set: {
    score: 'Good'
  }
}]

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.