I have a little bug with my script:
BasicDBObject change = new BasicDBObject();
BasicDBObject account =
new BasicDBObject().append("$set", new BasicDBObject().append("status", 0));
account.append("pos.X", getX());
account.append("pos.Y", getY());
account.append("pos.Z", getZ());
change.append("pseudo", gPlayer);
coll.update(change, account);
And the structure of mongoDB is :
{
pseudo: "pseudo"
email: "email"
password: "password"
status: "1"
pos: [
{X: "90.45}
{Y: "90.45}
{Z: "90.45}
]
}
But this is not working! No value is modified.
Thanks for your help.
Java is a very verbose language, and sometimes it is easiest to first write your query using the JS shell, make sure it performs as desired, and then translate it into Java.
By adding System.out.println(account.toString()) to your Java code, I can see that your Update document looks like the following, which is not valid:
{ "$set" : { "status" : 0} , "pos.X" : "90.45" , "pos.Y" : "90.45" , "pos.Z" : "90.45"}
From your question, it is not entirely clear what you would like your updated document to look like, but I am guessing that you would like to modify the values of "status" and "pos.0.X", "pos.1.Y", and "pos.2.Z". Because X, Y, and Z are all stored as individual embedded documents inside an array, they will have to be referenced by their positions in order to be updated at the same time. If possible, you might find it preferable to rearrange your document structure such that X, Y, and Z are all stored inside the same document, like so:
"pos" : {
"X" : "0",
"Y" : "0",
"Z" : "0"
}
This way, you will be able to access each variable more easily using dot notation, pos.X, pos.Y, pos.Z, which from your post looks like what you were intending. More information on embedded documents may be found in the "Dot Notation (Reaching into Objects)" documentation:
http://www.mongodb.org/display/DOCS/Dot+Notation+%28Reaching+into+Objects%29
Here is an Update statement that will modify the above values:
> db.pseudo.find({ "pseudo" : "gPlayer"}).pretty()
{
"_id" : ObjectId("4f904ebb5bebd4375b759c90"),
"email" : "email",
"password" : "password",
"pos" : [
{
"X" : "90.45"
},
{
"Y" : "90.45"
},
{
"Z" : "90.45"
}
],
"pseudo" : "gPlayer",
"status" : "1"
}
> db.pseudo.update({"pseudo" : "gPlayer"}, { "$set" : { "status" : 0 , "pos.0.X" : "0" , "pos.1.Y" : "0" , "pos.2.Z" : "0"}})
> db.pseudo.find({ "pseudo" : "gPlayer"}).pretty()
{
"_id" : ObjectId("4f904ebb5bebd4375b759c90"),
"email" : "email",
"password" : "password",
"pos" : [
{
"X" : "0"
},
{
"Y" : "0"
},
{
"Z" : "0"
}
],
"pseudo" : "gPlayer",
"status" : 0
}
>
Translated into Java this is:
BasicDBObject change = new BasicDBObject("pseudo", "gPlayer");
BasicDBObject setDoc = new BasicDBObject();
setDoc.append("status", "0");
setDoc.append("pos.0.X", "0");
setDoc.append("pos.1.Y", "0");
setDoc.append("pos.2.Z", "0");
BasicDBObject account = new BasicDBObject("$set", setDoc);
coll.update(change, account);
I realize that I guessed a little bit about exactly the update that you would like to do, but hopefully the above will get you pointed in the right direction!
Related
I've been working with mongo for a few months and I'm struggling now.
Here is a document example of my database:
"_id" : ObjectId("5732d96fed40761e640a3f3e"),
"_familyId" : "12345",
"_applications" : [
{
"_applicationRID" : "123456",
"_applicationDate" : "01012000",
"_isRepresentative" : false,
"_applicationId" : {
"CC" : "AB",
"SN" : "123456789",
"KC" : "A"
},
"_publications" : [
{
"_publicationRID" : "123456789",
"_publicationDate" : "01012000",
"_flaId" : "AB123456789A",
"_publicationId" : {
"CC" : "AB",
"SN" : "1234567",
"KC" : "B"
},
[...]
Now, I'm trying to do a collection.find() in Java on an array.
I know all the fields contained in _publicationId and I need to search on _publicationId because it has an index but not the fields inside it.
In shell it would be:
db.collection.find({
"_applications._publications._publicationId": {
"CC": "AB",
"SN": "1234567",
"KC": "B"
}
})
and this works fine.
Using java, I can't find the proper syntax:
collection.find("_applications._publications._publicationId", ??? )
You should try with a query of the next type that is actually the exact equivalent of your query in java:
DBObject query = new BasicDBObject(
"_applications._publications._publicationId",
new BasicDBObject("CC", "AB").append("SN", "1234567").append("KC", "B")
);
DBCursor cursor = collection.find(query);
I've this document:
{
"_id" : ObjectId("54140782b6d2ca6018585093"),
"user_id" : ObjectId("53f4ae1ae750619418a20467"),
"date" : ISODate("2014-09-13T08:59:46.709Z"),
"type" : 0,
"tot" : 2,
"additional_info" : {
"item_id" : ObjectId("540986159ef9ebafd3dcb5d0"),
"shop_id" : ObjectId("53f4cc5a6e09f788a103d0a4"),
"ap_id" : ObjectId("53f4cc5a6e09f788a103d0a5")
},
"transactions" : [
{
"_id" : ObjectId("54140782b6d2ca6018585091"),
"date_creation" : ISODate("2014-09-13T08:59:46.711Z"),
"type" : -1
},
{
"_id" : ObjectId("54140782b6d2ca6018585092"),
"date_creation" : ISODate("2014-09-13T08:59:46.788Z"),
"type" : 1
}
]
}
and I need to add 2 more field to the first transaction opbject:
- date_execution: date
- result: this bson document
{ "server_used" : "xxx.xxx.xxx.xxx:27017" , "ok" : 1 , "n" : 1 , "updated_executed" : true} (m_OR.getDocument() in the following code example)
to obtaing that document
{
"_id" : ObjectId("54140811b6d25137753c1a1a"),
"user_id" : ObjectId("53f4ae1ae750619418a20467"),
"date" : ISODate("2014-09-13T09:02:09.098Z"),
"type" : 0,
"tot" : 2,
"additional_info" : {
"item_id" : ObjectId("540986159ef9ebafd3dcb5d0"),
"shop_id" : ObjectId("53f4cc5a6e09f788a103d0a4"),
"ap_id" : ObjectId("53f4cc5a6e09f788a103d0a5")
},
"transactions" : [
{
"_id" : ObjectId("54140811b6d25137753c1a18"),
"date_creation" : ISODate("2014-09-13T09:02:09.100Z"),
"type" : -1,
"result" : {
"server_used" : "xxx.xxx.xxx.xxx:27017",
"ok" : 1,
"n" : 1,
"updated_executed" : true
},
"date_execution" : ISODate("2014-09-13T09:02:15.370Z")
},
{
"_id" : ObjectId("54140811b6d25137753c1a19"),
"date_creation" : ISODate("2014-09-13T09:02:09.179Z"),
"type" : 1
}
]
}
The only way I was able to do that is the do 2 separates updates (update is a my wrapper funciont that execute the real updates in mongodb and it works fine):
// where
BasicDBObject query = new BasicDBObject();
query.append("transactions._id", m_Task.ID());
// new value for result - 1st upd
BasicDBObject value = new BasicDBObject();
value.put("$set",new BasicDBObject("transactions.$.date_execution",new Date()));
update(this._systemDB, "activities", query, value);
// new value for date_execution - 2nd upd
value = new BasicDBObject();
value.put("$set",new BasicDBObject("transactions.$.result",m_OR.getDocument()));
update(this._systemDB, "activities", query, value);
If I try to do this:
BasicDBObject value = new BasicDBObject();
value.put("$set",new BasicDBObject("transactions.$.date_execution",new Date()));
value.put("$set",new BasicDBObject("transactions.$.result",m_OR.getDocument()));
or = update(this._systemDB, "activities", query, value);
just the 2nd set will be applied.
Is there any way do avoid the double execution and apply the update with just one call?
Basic rule of "hash/map" objects is that you can only have one key. It's the "highlander" rule ( "There can be only one" ) applied in general reason. So just apply differently:
BasicDBObject value = new BasicDBObject();
value.put("$set",
new BasicDBObject("transactions.$.date_execution",new Date())
.add( new BasicDBObject("transactions.$.result",m_OR.getDocument() )
);
So basically "both" field arguments are part of the "$set" statement as in the serialized form:
{
"$set": {
"transactions.$.date_execution": new Date(),
"transactions.$.result": m_Or.getDocument()
}
}
Which is basically what you want in the end.
Your suggestion was right, just had to fix a little the syntax this way:
BasicDBObject value = new BasicDBObject();
value.put("$set",
new BasicDBObject("transactions.$.date_execution",new Date())
.append("transactions.$.result",m_OR.getDocument())
);
This worked perfectly ;)
Thanks!
Samuel
I'm trying to find all of the documents in my db where the the size of my "states" list only contains one state but I'm struggling with the syntax of the java code.
My db looks like this:
{ "_id" : 13218 , "country" : { "MY" : 11 , "US" : 4} , "state" : { "WA" : 4 }}
{ "_id" : 95529 , "country" : { "US" : 6 } , "state" : { "MI" : 6 }}
{ "_id" : 22897 , "country" : { "US" : 4 } , "state" : { "CA" : 2 , "TX" : 1 , "WY" : 1 }}
What I want to do is print out every "_id" found from the US that only has a single state. So, the only "_id" that'd be returned here is 95529.
here is the relevant portion of code:
DBObject query = new BasicDBObject("country.US", new BasicDBObject("$gt", 4));
//query.put("state.2", new BasicDBObject("$exists", true));
//This is my attempt at checking the list length but it doesn't work
DBCursor dbCursor = dBcollection.find(query);
while (dbCursor.hasNext()){
DBObject record = dbCursor.next();
Object _id= record.get("_id");
Object state= record.get("state");
System.out.println(_id + "," + state);
}
current output looks like this:
95529, { "MI" : 6 }
22897, { "CA" : 2 , "TX" : 1 , "WY" : 1 }
The essential problem you have here is that your data is not in fact a "list". As a "hash" or "map" which is what it really is there is no concept of "length" in a MongoDB sense.
You would be better off changing your data to use actual "arrays" which is what a list actually is:
{
"_id" : 13218 ,
"country" : [
{ "code": "MY", "value" : 11 },
{ "code": "US", "value" : 4 },
],
"state" : [{ "code": "WA", "value" : 4 }]
},
{
"_id" : 95529 ,
"country" : [{ "code": "US", "value" : 6 }],
"state" : [{ "code": "MI", "value" : 6 }]
},
{
"_id" : 22897 ,
"country" : [{ "code": "US", "value" : 4 }],
"state" : [
{ "code": "CA", "value" : 2 },
{ "code": "TX", "value" : 1 },
{ "code": "WY", "value" : 1 }
]
}
Then getting those documents that only have a single state is a simple matter of using the $size operator.
DObject query = new BasicDBObject("country",
new BasicDBObject( "$elemMatch", new BasicDBObject(
"code", "US").put( "value", new BasicDBObject( "$gt", 4 )
)
);
query.put( "state": new BasicDBObject( "$size", 1 ) );
This ultimately gives you a lot more flexibilty in issuing queries as you don't need to specify the explicit "key" in each query. Also as noted, there is a concept of length here that does not otherwise exist.
If you keep your data in it's current form then the only way to do this is with the JavaScript evaluation of the $where operator. That is not very efficient as the interpreted code needs to be run for each document in order to determine if the condition matches:
DBObject query = new BasicDBObject("country.US", new BasicDBObject("$gt", 4));
query.put("state", new BasicDBObject( "$type", 3 ));
query.put("$where","return Object.keys( this.state ).length === 1");
Also using the $type operator in order to make sure that "state" is actually present and an "Object" that is expected. So possible, but not a really great idea do to performance.
Try to change your document structure as it will make other sorts of queries possible without using JavaScript evaluation.
I have the following document in MongoDb collection called hosts
{ "_id" : ObjectId("532aeec040a83df82181ff3c"),
"os" : "WINDOWS",
"name" : "Host 1",
"bas" : [
{ "wwn" : "EE:00:11:FF", "id" : "1" },
{ "wwn" : "AA:00:11:BB", "id" : "2" } ]
}
Now I want to update ba of id=1 in Host of name=Host 1 to { "wwn" : "AA:BB:CC:DD", "id" : "11" }. After the update the result will be
{ "_id" : ObjectId("532aeec040a83df82181ff3c"),
"os" : "WINDOWS",
"name" : "Host 1",
"bas" : [
{ "wwn" : "AA:BB:CC:DD", "id" : "11" },
{ "wwn" : "AA:00:11:BB", "id" : "2" } ]
}
For this I used the mongo shell command
db.hosts.update( { name : "Host 1", "bas.id" : "1" },
{ "$set" : { "bas.$" : { wwn : "AA:BB:CC:DD", id : "11"} }} );
which worked great. Now I wanted to do the same using Java drivers and here is my code
BasicDBObject example = new BasicDBObject("name", "Host 1").append("bas\uff0eid", "1");
BasicDBObject newValue = new BasicDBObject("\u0024set", new BasicDBObject("bas\uff0e\u0024", new BasicDBObject().append("wwn", "AA:BB:CC:DD).append("id","11")) ;
collection.update(example, newValue);
In the above code I had to replace . with \uff0 and $ with \u0024 to avoid exception being thrown. This update call is not updating the collection. When I inspected the BasicDBObject.toString() the JSON were same as what I had typed on console. Please help me.
I don't understand why you should need to write $ and . as Unicode escapes. I tried running your code with the actual characters in them and it worked fine with the 2.11 Java driver.
But your code has two problems: 1) the full stop . is \u002e, not \uff0e, which is a very different character. 2) the syntax of the last part of the statement is not correct, but that is probably a copying error.
Here's the code that I used, and it worked fine (I only created the objects and didn't actually run it against the database but I don't see why that wouldn't work).
BasicDBObject example = new BasicDBObject("name", "Host 1").append("bas.id", "1");
BasicDBObject newValue = new BasicDBObject("$set",
new BasicDBObject("bas.$",
new BasicDBObject()
.append("wwn", "AA:BB:CC:DD").append("id","11"))) ;
https://github.com/mongodb/mongo-java-driver/blob/master/src/main/com/mongodb/DBCollectionImpl.java#L249
If your value object doesn't have a key starting with $ then it will check the embedded document for illegal characters. Can you post your original code that had this problem, without the escapes?
So I have a few dbobjects in my mongo database. Here's an example of one of the objects:
{ "_id" : { "$oid" : "525b048580c3fb0d62d2b6fc"} , "city" : "London" , "currentWeather" : [ { "cloudcover" : "25" , "humidity" : "82" , "observation_time" : "08:37 PM" , "precipMM" : "0.0" , "pressure" : "1008" , "temp_C" : "11" , "temp_F" : "52" , "visibility" : "10" , "weatherCode" : "113" , "weatherDesc" : [ { "value" : "Clear"}] , "weatherIconUrl" : [ { "value" : "http://cdn.worldweatheronline.net/images/wsymbols01_png_64/wsymbol_0008_clear_sky_night.png"}] , "winddir16Point" : "W" , "winddirDegree" : "280" , "windspeedKmph" : "19" , "windspeedMiles" : "12"}]}
Now, I need to get all the dbobjects in my database whose value is lower than a given "temp_C", I have used something like this:
BasicDBObject query = new BasicDBObject("temp_C", new BasicDBObject(">", graden));
But it's failing, and I think it is because the property is a subproperty of "currentWeather", yet I have no idea how to address this problem. I am using java to do this.
Looking at your document structure, you're trying to access a subdocument that lives inside an array in your document, so it's a bit more complicated than a standard query:
{ "_id" : { "$oid" : "525b048580c3fb0d62d2b6fc"} , <-- Document
"city" : "London" ,
"currentWeather" : [ <-- Array
{ "cloudcover" : "25", <-- Sub document
...etc...
"pressure" : "1008" ,
"temp_C" : "11",
"temp_F" : "52",
...etc...
}
]
}
In order to get to the nested object, you need to reference its position in the array (in this case, it's zero as it's the first element in the array) and then the field name in the sub document. So your query looks like this:
BasicDBObject query = new BasicDBObject("currentWeather.0.temp_C",
new BasicDBObject("$gt", 11));
Note you had two problems in your original query:
1) You need to reference currentWeather.0.temp_C
2) Your gt operator needs to start with a dollar sign not an ampersand.
Also, you said you wanted the query to return values lower than a given value, in which case you probably want $lt not $gt.
You can't directly use the value of the object of an array in a query. You can use aggregate framework of Mongo. Java Docs For Aggregate are here