0

I have a document of the sorts:

{ _id:ObjectID, list:Array }

And the list contains elements of the form (which I will refer to as listElement):

{ _id:ObjectID, time:Number }

I want to update the time subfield of 2 specific listElements each with its own distinct value. I have access to both _id of the listElements.

A related thing about this issue: would it be better to transform list from Array to an Object who's keys are the _id values? so I would do db.update( ( _id:"Document id" }, { "list.423rfasf2q3.time":200, "list.fjsdhfksjdh2432.time":100 } ) ?

I am unsure how one could use an ObjectID as a key, but I guess I can just string it and have both a _id value and the key containing that listElement be the same string.

Discipol
  • 3,137
  • 4
  • 22
  • 41

1 Answers1

2

You can't update two docs with two different values, you'll need to run it as two separate updates.

You do not want to put data into your key values, it's better to leave them as they are. There's no good way to match key values outside of $exists and even that does not support wildcards. So you'd have to get the ObjectId, convert it to a string, concatenate it into the key, then query for documents where that key exists. It's much easier to just put the value in a document in an array. Additionally, there's no way to index key values in MongoDB, so if you want to index your data you'll need to have it in the array as a data element.

Edit to include positional match example

First, insert two documents

> db.test.insert( {list: [ {_id: new ObjectId(), time: 1234}, {_id: new ObjectId(), time: 4556} ] } )
>

Demonstrate that they are both there

> db.test.find()
{ "_id" : ObjectId("5215036749177daf439a2ffe"), "list" : [{ "_id" : ObjectId("5215036749177daf439a2ffc"), "time" : 1234 }, { "_id" :
ObjectId("5215036749177daf439a2ffd"), "time" : 4556 } ] }
>

Show that if we do a normal array filter we get all elements of the array

> db.test.find({"list._id":ObjectId("5215036749177daf439a2ffc")})
{ "_id" : ObjectId("5215036749177daf439a2ffe"), "list" : [ { "_id" : ObjectId("5215036749177daf439a2ffc"), "time" : 1234 }, { "_id" :
ObjectId("5215036749177daf439a2ffd"), "time" : 4556 } ] }
>

Demonstrate that we can use the $ operator to only return the first array element that was matched

> db.test.find({"list._id":ObjectId("5215036749177daf439a2ffc")}, {_id: 0, "list.$": 1})
{ "list" : [ { "_id" : ObjectId("5215036749177daf439a2ffc"), "time" : 1234 } ] }
Mason
  • 8,767
  • 10
  • 33
  • 34
  • I really don't want to make separate queries, it would overload my transaction system way too much. Then again the actions are independent from one another, but I have to return 1 update to the client for all operations, that is why I wanted to stack the calls. Why couldn't I index subfields of subfields of documents? – Discipol Aug 21 '13 at 14:30
  • The problem is that there's no syntax to tell MongoDB to do a conditional update (if _id = x then set value to y, if _id = a then set value to b). Updating a document is relatively light weight, there will be very little difference between updating two docs in one call or two docs in two calls - you might consider sharding if your system can't handle the volume. Reason indexing won't work if you put the `ObjectId` in the key is because you must set your index on a specific key name. If you're embedding `ObjectId`s into the key then there's no key name to set your index on. – Mason Aug 21 '13 at 14:43
  • what if I just index the "list" subfield? would that help only when getting the "list" subfield itself? all of it? – Discipol Aug 21 '13 at 16:25
  • Indexing the `list` subfield will only help when accessing `list` directly. Is there a reason you're so set on adding the `ObjectId` to the key name? It still won't help you update two keys with two different values in one operation. – Mason Aug 21 '13 at 17:13
  • I need to identify the listElements somehow :| and I will try to access them individually more often than getting the entire "list". I am keen on using ObjectID because its a "random" way of identifying them. I don't want to use _0, _1.._n as keys for the buildings, nor for their _id. – Discipol Aug 21 '13 at 17:25
  • You can use the positional match operator in your projection to return only the array element that matched your filter. I've updated my answer with an example above. – Mason Aug 21 '13 at 18:19
  • I decided to switch to storing the listElements as properties of list, with key being an objectid made string to ensure uniqueness. While I use my elements individually, I still get them through the document's _id and I just update the document's sub-sub-sub-subfields. I wouldn't need index for this particular case. – Discipol Aug 21 '13 at 22:55