Java 1.4 API $addToSet replacing instead of appending

1,274 views
Skip to first unread message

Wes

unread,
Apr 12, 2010, 1:18:26 AM4/12/10
to mongodb-user
I am having issues with the $addToSet operator through the 1.4 Java
API. It adds a missing array just fine, but following updates with
$addToSet overwrite the existing entry instead of appending to it.

For example, I am modeling a family below with kids which have toys.
I can never add more than one kid or toy

(I HAVE OMITTED ID ATTRIBUTES TO KEEP SHORT, THEY ARE ALL UNIQUE IN MY
ACTUAL SCENARIO)

{
"name" : "John Doe",
"age" : "40"
}

I want to add values to an array that may not exist.

This works as expected from the shell:

db.users.update({'users.name': 'John Doe'},{$addToSet : {'kids' :
{ 'name' : 'Bob', 'age': '4'}}})
db.users.update({'users.name': 'John Doe'},{$addToSet : {'kids' :
{ 'name' : 'Dan', 'age': '2'}}})

Results in:

{
"name" : "John Doe",
"age" : "40",
"kids" : [
{
"name" : "Bob",
"age" : "4"
},
{
"name" : "Dan",
"age" : "2"
}
]
}

The java api is overriding the previous entry when I attempt to
express the same thing.
I am sure I have something wrong, but I am not sure what. Please see
below.

EXECUTION #1:

BasicDBObject kidBob = new BasicDBObject();
kids.put("name","Bob");
kids.put("age","4");

BasicDBObject userMod = new BasicDBObject();
userMod.put("$addToSet", new BasicDBObject("kids", kidBob));

BasicDBObject userMatch = new BasicDBObject();
userMatch.put("name","John Doe");

usersCollection.update(userMatch, userMod);

This results in:

{
"name" : "John Doe",
"age" : "40",
"kids" : [
{
"name" : "Bob",
"age" : "4"
},
]
}

EXECUTION #2:

BasicDBObject kidDan = new BasicDBObject();
kids.put("name","Dan");
kids.put("age","2");

BasicDBObject userMod = new BasicDBObject();
userMod.put("$addToSet", new BasicDBObject("kids", kidDan));

BasicDBObject userMatch = new BasicDBObject();
userMatch.put("name","John Doe");

usersCollection.update(userMatch, userMod);

This results in:

{
"name" : "John Doe",
"age" : "40",
"kids" : [
{
"name" : "Dan",
"age" : "2"
}
]
}

Any ideas why it is not appending to the array as expected????

I have the same issue for a nested array:

db.users.update({'users.name': 'John Doe', 'users.kids.name' : 'Bob'},
{$addToSet : {'users.$.toys' : { 'type' : 'Truck', 'color' :
'blue'}}})

BasicDBObject toyDan = new BasicDBObject();
kids.put("type","Truck");
kids.put("color","blue");

BasicDBObject userMod = new BasicDBObject();
userMod.put("$addToSet", new BasicDBObject("kids.$.toys", toyDan));

BasicDBObject userMatch = new BasicDBObject();
userMatch.put("name","John Doe");
userMatch.put("kids.name","Dan");

usersCollection.update(userMatch, userMod);

{
"name" : "John Doe",
"age" : "40",
"kids" : [
{
"name" : "Dan",
"age" : "2"
"toys" : [
{
"type" : "Truck",
"color" : "blue"
}
]
}
]
}

Adding another toy just replaces the previous toy.

How do I get the behavior I want???

Eliot Horowitz

unread,
Apr 12, 2010, 9:50:15 AM4/12/10
to mongod...@googlegroups.com
Looks like you are misusing a variable.

> BasicDBObject kidBob = new BasicDBObject();
> kids.put("name","Bob");
> kids.put("age","4");

kids should be kidBob...

> --
> You received this message because you are subscribed to the Google Groups "mongodb-user" group.
> To post to this group, send email to mongod...@googlegroups.com.
> To unsubscribe from this group, send email to mongodb-user...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/mongodb-user?hl=en.
>
>

Wes

unread,
Apr 12, 2010, 10:49:18 AM4/12/10
to mongodb-user
You are right. I made a mistake when making the example. Fixes are
below.
This is not my real code or data. This is just a simplification of the
problem.

It should have been:

BasicDBObject kidBob = new BasicDBObject();

kidsBob.put("name","Bob"); // fixed this
kidsBob.put("age","4"); // fixed this

BasicDBObject userMod = new BasicDBObject();
userMod.put("$addToSet", new BasicDBObject("kids", kidBob));

BasicDBObject userMatch = new BasicDBObject();
userMatch.put("name","John Doe");

usersCollection.update(userMatch, userMod);


AND

BasicDBObject kidBob = new BasicDBObject();

kidBob.put("name","Bob"); // fixed this
kidBob.put("age","4"); // fixed this

BasicDBObject userMod = new BasicDBObject();
userMod.put("$addToSet", new BasicDBObject("kids", kidBob));

BasicDBObject userMatch = new BasicDBObject();
userMatch.put("name","John Doe");
usersCollection.update(userMatch, userMod)

AND

BasicDBObject toyDan = new BasicDBObject();

toyDan.put("type","Truck"); // fixed this
toyDan.put("color","blue"); // fixed this

BasicDBObject userMod = new BasicDBObject();
userMod.put("$addToSet", new BasicDBObject("kids.$.toys", toyDan));

BasicDBObject userMatch = new BasicDBObject();
userMatch.put("name","John Doe");
userMatch.put("kids.name","Dan");

usersCollection.update(userMatch, userMod);

Is the way this intended to be used? I thought I may have been doing
something wrong.

Eliot Horowitz

unread,
Apr 12, 2010, 10:51:41 AM4/12/10
to mongod...@googlegroups.com
This is how it should be used and is working fine for me.

If you paste a full .java that you run that reproduces the error, can
you send the entire file.

Wes

unread,
Apr 12, 2010, 5:19:05 PM4/12/10
to mongodb-user
Ok. I found the problem probably has nothing to do with $addToSet, but
it is by calling save on the same
object twice. Doing so causes the behavior I described before. The
following program shows the scenario
I am experiencing. Commenting out the line I note as the issue solves
the problem. So, how do I
update the existing object?


OUTPUT:

WITH THE SECOND SAVE:

> db.users.find()
{ "_id" : "John Doe", "age" : "40", "kids" : [ { "_id" : "Bob",
"name" : "Bob", "age" : "4" } ], "name" : "John Doe" }


WITHOUT THE SECOND SAVE:

> db.users.find()
{ "_id" : "John Doe", "age" : "40", "kids" : [
{
"_id" : "Dan",
"age" : "2",
"name" : "Dan",
"toys" : [
{
"_id" : "Truck",


"type" : "Truck",
"color" : "blue"

},
{
"_id" : "Car",
"type" : "Car",
"color" : "Red"
}
]
},
{
"_id" : "Bob",


"name" : "Bob",
"age" : "4"
}

], "name" : "John Doe" }


CODE:

import com.mongodb.Mongo;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.BasicDBObject;

public class Test
{

public static void main(String[] args) throws Exception
{

Mongo mongo = new Mongo("localhost", 27017);
DB db = mongo.getDB("test");

DBCollection usersCollection = db.getCollection("users");

BasicDBObject user = new BasicDBObject();
user.put("_id", "John Doe");
user.put("name", "John Doe");
user.put("age", "40");

usersCollection.save(user);

// START TEST 1
System.out.println("Start Test 1");

BasicDBObject kidDan = new BasicDBObject();

kidDan.put("_id", "Dan");
kidDan.put("name", "Dan");
kidDan.put("age", "2");

BasicDBObject userMod = new BasicDBObject();
userMod.put("$addToSet", new BasicDBObject("kids", kidDan));

BasicDBObject userMatch = new BasicDBObject();
userMatch.put("name", "John Doe");

usersCollection.update(userMatch, userMod);

// START TEST 2
System.out.println("Start Test 2");

BasicDBObject toyDan = new BasicDBObject();

toyDan.put("_id", "Truck");


toyDan.put("type", "Truck");

toyDan.put("color", "blue");

BasicDBObject userMod2 = new BasicDBObject();
userMod2.put("$addToSet", new BasicDBObject("kids.$.toys", toyDan));

BasicDBObject userMatch2 = new BasicDBObject();
userMatch2.put("name", "John Doe");
userMatch2.put("kids.name", "Dan");

usersCollection.update(userMatch2, userMod2);

/************************************
* THIS FOLLOWING LINE IS CAUSING THE PROBLEM
* I AM EXECUTING THIS IN A LOOP WHICH RESULTS
* IN THE SAME USER EXISTING MULTIPLE TIMES
* I WANT TO UPDATE THE USER IF IT IS DIFFERENT
* I UNDERSTOOD THAT SAVE MADE THE DISTINCTION
* BETWEEN INSERT AND UPDATE
***********************************/
usersCollection.save(user);

// START TEST 3
System.out.println("Start Test 3");

BasicDBObject kidBob = new BasicDBObject();

kidBob.put("_id", "Bob");


kidBob.put("name", "Bob");

kidBob.put("age", "4");

BasicDBObject userMod3 = new BasicDBObject();
userMod3.put("$addToSet", new BasicDBObject("kids", kidBob));

BasicDBObject userMatch3 = new BasicDBObject();
userMatch3.put("name", "John Doe");

usersCollection.update(userMatch3, userMod3);


// START TEST 4
System.out.println("Start Test 4");

BasicDBObject toy2Dan = new BasicDBObject();
toy2Dan.put("_id", "Car");
toy2Dan.put("type", "Car");
toy2Dan.put("color", "Red");

BasicDBObject userMod4 = new BasicDBObject();
userMod4.put("$addToSet", new BasicDBObject("kids.$.toys",
toy2Dan));

BasicDBObject userMatch4 = new BasicDBObject();
userMatch4.put("name", "John Doe");
userMatch4.put("kids.name", "Dan");

usersCollection.update(userMatch4, userMod4);

Eliot Horowitz

unread,
Apr 12, 2010, 5:21:23 PM4/12/10
to mongod...@googlegroups.com
Why are you calling save() at all?

Wes

unread,
Apr 12, 2010, 5:31:40 PM4/12/10
to mongodb-user
I want to update the object if it exist or create it if it does not.

It makes no sense in the scenario, but imagine I am loading from
a file with duplicate users. Test 1 & 2 occur at the same time but
3 & 4 occur in a different iteration of the loop. I need to create or
update the user as appropriate.

I am starting to think save does not act how I expect.
How do I update the existing user?

> ...
>
> read more »

Eliot Horowitz

unread,
Apr 12, 2010, 5:33:48 PM4/12/10
to mongod...@googlegroups.com
You should use upsert.
See the boolean upsert option on the update() method

Wes

unread,
Apr 12, 2010, 5:49:59 PM4/12/10
to mongodb-user
OK. Same result though. What else am I missing?


import com.mongodb.Mongo;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.BasicDBObject;

public class Test
{

public static void main(String[] args) throws Exception
{

Mongo mongo = new Mongo("localhost", 27017);
DB db = mongo.getDB("test");

DBCollection usersCollection = db.getCollection("users");

BasicDBObject user = new BasicDBObject();
user.put("_id", "John Doe");
user.put("name", "John Doe");
user.put("age", "40");

BasicDBObject userSearch = new BasicDBObject();
userSearch.put("name", "John Doe");
usersCollection.update(userSearch,user,true,false);

usersCollection.update(userMatch, userMod);

usersCollection.update(userMatch2, userMod2);

***********************************/
//usersCollection.save(user);
usersCollection.update(userSearch,user,true,false);

usersCollection.update(userMatch3, userMod3);

usersCollection.update(userMatch4, userMod4);

}
}

> ...
>
> read more »

Eliot Horowitz

unread,
Apr 12, 2010, 8:17:09 PM4/12/10
to mongod...@googlegroups.com
You're doing a replace...

You should walk through line by line seeing what each does. I don't
quite think you get what does what

>>>>> $.toys", toyDan));
>>
>>>>> BasicDBObject userMatch2 = new BasicDBObject();
>>>>> userMatch2.put("name", "John Doe");
>>>>> userMatch2.put("kids.name", "Dan");
>>
>>>>> usersCollection.update(userMatch2, userMod2);
>>
>>>>> /************************************
>>>>> * THIS FOLLOWING LINE IS CAUSING THE PROBLEM
>>>>> * I AM EXECUTING THIS IN A LOOP WHICH RESULTS
>>>>> * IN THE SAME USER EXISTING MULTIPLE TIMES
>>>>> * I WANT TO UPDATE THE USER IF IT IS DIFFERENT
>>>>> * I UNDERSTOOD THAT SAVE MADE THE DISTINCTION
>>>>> * BETWEEN INSERT AND UPDATE
>>>>> ***********************************/
>>>>> usersCollection.save(user);
>>
>>>>> // START TEST 3
>>>>> System.out.println("Start Test 3");
>>
>>>>> BasicDBObject kidBob = new BasicDBObject();
>>>>> kidBob.put("_id", "Bob");
>>>>> kidBob.put("name", "Bob");
>>>>> kidBob.put("age", "4");
>>
>>>>> BasicDBObject userMod3 = new BasicDBObject();
>>>>> userMod3.put("$addToSet", new BasicDBObject

>>>>> ("kids", kidBob));
>>
>>>>> BasicDBObject userMatch3 = new BasicDBObject();
>>>>> userMatch3.put("name", "John Doe");
>>
>>>>> usersCollection.update(userMatch3, userMod3);
>>
>>>>> // START TEST 4
>>>>> System.out.println("Start Test 4");
>>
>>>>> BasicDBObject toy2Dan = new BasicDBObject();
>>>>> toy2Dan.put("_id", "Car");
>>>>> toy2Dan.put("type", "Car");
>>>>> toy2Dan.put("color", "Red");
>>
>>>>> BasicDBObject userMod4 = new BasicDBObject();
>>>>> userMod4.put("$addToSet", new BasicDBObject("kids.

Wes

unread,
Apr 12, 2010, 9:06:39 PM4/12/10
to mongodb-user
I agree. I am confused. I specified the _id when using save, so I
thought it would upsert the object reference by the id. When using
update, I thought it would search for the first object and upsert with
the second. That seems to be what the documentation indicates. I
definitely need to play around some more from the shell.

Thanks for your time and help. Nice product!


On Apr 12, 6:17 pm, Eliot Horowitz <eliothorow...@gmail.com> wrote:
> You're doing a replace...
>
> You should walk through line by line seeing what each does. I don't  
> quite think you get what does what
>

> ...
>
> read more »

Kristina Chodorow

unread,
Apr 13, 2010, 8:17:54 AM4/13/10
to mongod...@googlegroups.com

save replaces the object whereas update with $-ops modifies individual fields.

On Apr 12, 2010 6:06 PM, "Wes" <wwwe...@gmail.com> wrote:

I agree. I am confused. I specified the _id when using save, so I
thought it would upsert the object reference by the id. When using
update, I thought it would search for the first object and upsert with
the second. That seems to be what the documentation indicates. I
definitely need to play around some more from the shell.

Thanks for your time and help. Nice product!

On Apr 12, 6:17 pm, Eliot Horowitz <eliothorow...@gmail.com> wrote: > You're doing a replace... > ...

> On Apr 12, 2010, at 5:49 PM, Wes <wwwest...@gmail.com> wrote: > > > > > OK. Same result though. Wh...

> ... > > read more » -- You received this message because you are subscribed to the Google Groups...

Reply all
Reply to author
Forward
0 new messages