How could I update a specific field in subdocument of array element ?
My question is similar to the following below, but in my case I need to update just a subdocument value.
MongoDB: How do I update a single subelement in an array, referenced by the index within the array?
I have the following documento model :
{
_id : "xpto",
other_stuff ... ,
templates : [
{
templateId:"template-a"
body: {
en_US:"<p>Hello World!</p>"
}
},
{
templateId:"template-b"
body: {
es_ES:"<p>Holla !</p>"
}
}
]
}
So, In mongodb shell the following statement works perfectly for me:
db.apiClient.update({"_id":"xpto","templates.templateId":"template-b"}, {$set:{"templates.$.body.es_ES":"<h1>Gracias !</h1>"}})
However , when i try to do it with Mongo Java Driver , I get an IllegalArgumentException.
BasicDBObject selectQuery = new BasicDBObject("_id", "xpto");
selectQuery.put("templates.templateId", "template-b");
BasicDBObject updateQuery = new BasicDBObject();
for(String locale : template.getBody().keySet()) {
String updateBodyLocaleExpression = new StringBuilder()
.append("templates.$.body.").append(locale).toString();
String updateBodyLocaleValue = template.getBody().get(locale);
updateQuery.put(updateBodyLocaleExpression, updateBodyLocaleValue);
}
updateQuery.put("$set", updateQuery);
getCollection(COLLECTION_NAME).update(selectQuery, updateQuery, true, true);
It throws the following exception :
Caused by: java.lang.IllegalArgumentException: Invalid BSON field name templates.$.body.es_ES
at org.bson.AbstractBsonWriter.writeName(AbstractBsonWriter.java:494)
at com.mongodb.DBObjectCodec.encode(DBObjectCodec.java:127)
at com.mongodb.DBObjectCodec.encode(DBObjectCodec.java:61)
at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:63)
at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:29)
at com.mongodb.connection.RequestMessage.addDocument(RequestMessage.java:253)
at com.mongodb.connection.RequestMessage.addCollectibleDocument(RequestMessage.java:219)
at com.mongodb.connection.UpdateMessage.encodeMessageBodyWithMetadata(UpdateMessage.java:77)
at com.mongodb.connection.RequestMessage.encodeWithMetadata(RequestMessage.java:160)
at com.mongodb.connection.WriteProtocol.execute(WriteProtocol.java:85)
Is something wrong with my code ?
Thanks.
Yes. You construct wrong updateQuery. You put fields templates.$.body... in the BasicDbObject and then add same document into $set field into itself. MongoDB tries to update field templates.$.body. and here $ is the part of the field name instead of operator.
Here is working example:
//List is for testing purposes only
List<String> locales = Arrays.asList("en_US", "en_UK");
Document query = new Document("_id", "xpto")
.append("templates.templateId", "template-b");
Document updateQuery = new Document();
for (String locale : locales) {
updateQuery.put("templates.$.body." + locale, "<pre>Updated " + locale + "</pre>");
}
collection.updateOne(query, new Document("$set", updateQuery));
Document is almost the same as BasicDbObject but more general.
Related
I am updating mongodb item in collection using the below command.
Now i want to take the set and filter value in java.
db.practice.update({"name" : "testmysqlupdate"},{$set:{"name" : "5"}})
I am able to take the value which need to be update i.e. name : 5, but not the conditional value i.e. "name" : "testmysqlupdate"
static ChangeStreamDocument<BasicDBObject> dbOb;
static MongoCursor<ChangeStreamDocument<BasicDBObject>> cursor;
cursor = collection.watch().fullDocument(FullDocument.UPDATE_LOOKUP).iterator();
System.out.println("value of update stream us " + dbOb.getUpdateDescription().getUpdatedFields().toString());
System.out.println("Id Value is " + dbOb.getDocumentKey().toString());
document = dbOb.getUpdateDescription().getUpdatedFields().toString();
BasicDBObject searchQuery = new BasicDBObject();
searchQuery.put("name", "testmysqlupdate");
BasicDBObject newDocument = new BasicDBObject();
newDocument.append("$set", new BasicDBObject().append("name", "5"));
collection.update(searchQuery, newDocument);
Try this to update a field in MongoDB.
I am trying to do this query :
db.indexInverse.find({_id:ObjectId("5a153409b9c52933c8aab068")}).pretty()
with Java ...
actually i try many things ... but that don't find my document ...
Document vraiDoc = collectionIndexInverse.find(Filters.eq("_id", new ObjectId("\"" + idDoc + "\""))).first();
OR
Bson query = (Bson) new BsonString("{ _id:ObjectId(\"" + idDoc + "\")}");
Document vraiDoc = collectionIndexInverse.find(query).first();
OR
Bson query = (Bson) new BsonString("{ _id:ObjectId(idDoc)}");
Document vraiDoc = collectionIndexInverse.find(query).first();
In your first attempt to are double quoting the ObjectId value.
This will work:
Document vraiDoc = collectionIndexInverse.find(
Filters.eq("_id", new ObjectId("5a153409b9c52933c8aab068"))
).first();
More generally, given these documents:
{
"_id" : ObjectId("59b86ff639f9ba0f9c0dccf6"),
...
},
{
"_id" : ObjectId("59c0e662ac11a822d4e5da27"),
...
}
The following command will return the first document ...
find(Filters.eq("_id", new ObjectId("59b86ff639f9ba0f9c0dccf6"))).first();
... and the following command will return the second document:
find(Filters.eq("_id", new ObjectId("59c0e662ac11a822d4e5da27"))).first();
I have tried to fetch data for the particular column value in the mongo document but its displaying whole data.
Following is the mongo document:
{
"_id" : ObjectId("59db2321811a592384865711"),
"User_ID" : "demo",
"Project_ID" : "demo-1",
"Project_Information" : {
"Project_Description" : "Sample",
"Primary_Building_Type" : "Office",
"State" : "AR",
"Analysis_Type" : "1",
"Project_Billing_Number" : "WY",
"Country" : "USA",
"Climate_Zone" : "3A",
"Zip_Code" : "71611"
"City" : "WA",
"Units" : "IP"
}
}
I want to fetch the following output:
[
{
"User_ID": "demo",
"Project_Description": "Sample"
}]
I have tried using dot: Project_Information.Project_Description.The code is as below:
public Object[] addDemo1(String User_ID) throws Exception {
DB db = ConnectToDB.getConnection();
Properties prop = new Properties();
InputStream input = null;
input = GetProjectStatus.class.getClassLoader().getResourceAsStream("config.properties");
prop.load(input);
String col = prop.getProperty("COLLECTION_PI");
System.out.println("data is.." + col);
DBCollection collection = db.getCollection(col);
BasicDBObject obj = new BasicDBObject();
BasicDBObject fields = new BasicDBObject();
BasicDBObject fields2 = new BasicDBObject();
List<DBObject> obj1 = null;
if (User_ID != null && !User_ID.equals("") && User_ID.length() > 0) {
obj.put("User_ID", User_ID);
fields.put("_id", 0);
fields.put("User_ID", 1);
fields.put("Project_ID", 1);
fields.append("Project_Information.Project_Description", "Project_Description");
BasicDBObject fields1 = new BasicDBObject();
fields1.put("User_ID", User_ID);
}
DBCursor cursor = collection.find(obj, fields);
System.out.println("count is:" + cursor.count());
obj1 = cursor.toArray();
System.out.println("" + obj1);
cursor.close();
db.getMongo().close();
return obj1.toArray();
}
But it displays the whole structure of Project_Information.
Please specify how to achieve this. Thanks for help.
Using a 2.x MongoDB Java Driver
Here's an example using the MongoDB 2.x Java driver:
DBCollection collection = mongoClient.getDB("stackoverflow").getCollection("demo");
BasicDBObject filter = new BasicDBObject();
BasicDBObject projection = new BasicDBObject();
// project on "Project_Information.Project_Description"
projection.put("Project_Information.Project_Description", 1);
DBCursor documents = collection.find(filter, projection);
for (DBObject document : documents) {
// the response contains a sub document under the key: "Project_Information"
DBObject projectInformation = (DBObject) document.get("Project_Information");
// the "Project_Description" is in this sub document
String projectDescription = (String) projectInformation.get("Project_Description");
// prints "Sample"
System.out.println(projectDescription);
// to return this single String value in an Object[] (as implied by your OP) just do create the Object[] like this and then return it ...
Object[] r = new Object[] {projectDescription};
// prints the entire projected document e.g.
// { "_id" : { "$oid" : "59db2321811a592384865711" }, "Project_Information" : { "Project_Description" : "Sample" } }
System.out.println(document.toString());
}
Using a 3.x MongoDB Java Driver
Here's an example using the MongoDB 3.x Java driver:
// this finds all documents in a given collection (note: no parameter supplied to the find() call)
// and for each document it projects on Project_Information.Project_Description
FindIterable<Document> documents =
mongoClient.getDatabase("...").getCollection("...")
.find()
// for each attrbute you want to project you must include its dot notation path and the value 1 ...
// this is the equivalent of specifying {'Project_Information.Project_Description': 1} in the MongoDB shell
.projection(new Document("Project_Information.Project_Description", 1));
for (Document document : documents) {
// the response contains a sub document under the key: "Project_Information"
Document projectInformation = (Document) document.get("Project_Information");
// the "Project_Description" is in this sub document
String projectDescription = projectInformation.getString("Project_Description");
// prints "Sample"
System.out.println(projectDescription);
// to return this single String value in an Object[] (as implied by your OP) just do create the Object[] like this and then return it ...
Object[] r = new Object[] {projectDescription};
// prints the entire projected document e.g. { "_id" : { "$oid" : "59db2321811a592384865711" }, "Project_Information" : { "Project_Description" : "Sample" } }
System.out.println(document.toJson());
}
Java libraries won't let you directly access using dot.
They have build in getter and setter methods.
You have not mentioned which package you are using.
Here's the query that you need:
db.mycol.find({},{User_ID:1,"Project_Information.Project_Description":1})
It will give:
{ "_id" : ObjectId("59db2321811a592384865711"),
"User_ID" : "demo",
"Project_Information" : { "Project_Description" : "Sample" }
}
You will have to convert the query in whatever format your package accepts.
Here's a tutorial:
https://www.mongodb.com/blog/post/getting-started-with-mongodb-and-java-part-i
I am using mongodb with Java 3.0 driver. I have a scenario where I have to perform logical and i.e, $and on my queries. For example, I have two documents already created and I am trying to do something like this:
iterable = mongoDatabase.getCollection("restaurants").find(
new Document("$and", asList(abc,
updatedDocumentTypeOne)));
where abc is one document and updatedDocumentTypeOne is another document. I found this in mongodb manual but I am getting error as first create asList Method.
Or how to replicate the following in Java:
db.inventory.find( {
$and : [
{ $or : [ { price : 0.99 }, { price : 1.99 } ] },
{ $or : [ { sale : true }, { qty : { $lt : 20 } } ] }
]
} )`
You can also try the code below that adds a filter for query replication in Java:
// Where db is the object reference of "inventory" collection's database
MongoCollection<Document> inventory = db.getCollection("inventory");
//List for result return
List<Document> result = new ArrayList<Document>();
//Query replication in Java and return result into the list
inventory.find(Filters.and(
Filters.or(Filters.eq("price", 0.99),Filters.eq("price", "1.99")),
Filters.or(Filters.eq("sale", true),Filters.lt("qty", 20))
)).into(result);
Change from asList() to Arrays.asList()
Instead of writing Arrays.asList(), you have specified as asList(). So compiler is searching for the method asList(), which is NOT available.
Check the below code :
iterable = mongoDatabase.getCollection("restaurants").find(
new Document("$and", Arrays.asList(abc,
updatedDocumentTypeOne)));
For your above query, You can code as below :
/* First OR condition */
Document price1 = new BasicDBObject("price",0.99);
Document price2 = new BasicDBObject("price",1.99);
BasicDBList or_first = new BasicDBList();
or_first.add(price1);
or_first.add(price2);
DBObject query = new BasicDBObject("$or", or_first);
/* Second OR condition */
boolean val = true;
Document sale = new BasicDBObject("sale",val);
Document qty = new BasicDBObject("qty", new BasicDocument("$lt",20));
BasicDBList or_second = new BasicDBList();
or_second.add(sale);
or_second.add(qty);
DBObject query = new BasicDBObject("$or", or_second);
/* AND condition logic */
BasicDBList and_op = new BasicDBList();
and_op.add(or_first);
and_op.add(or_second);
iterable = mongoDatabase.getCollection("restaurants").find( new Document("$and", and_op ));
From a collection named "persons", I want to retreive all records where date is between
"14-11-2014" and "20-11-2014" <-- These are both in string format (dd-mm-yyyy)
AND
user: "Erik"
My mongoDB
{
"_id" : "546c9f26dbeaa7186ab042c4", <------this one should NOT be retreived
"Task: "Sometask" because of the user
"date" : "20-11-2014",
"user" : "Dean"
},
{
"_id" : "546caef6dbeaa7186ab042c5", <--------- This one should be retreived
"task": "A task",
"date" : "20-11-2014",
"user" : "Erik"
}
{
"_id" : "546caef6dbeaa7186ab042c5", <----- This one should NOT be retreived
"task": "A task", because of the date
"date" : "13-11-2014",
"user" : "Erik"
}
I am using java mongo java driver 2.11.3
Maybe there is some solution using BasicDBObject?
I'm very curious.. thanks
EDIT
I'm using:
public static String findTimelines(String begin, String end, String username) throws UnknownHostException, JSONException{
DBCollection dbCollection = checkConnection("timelines");
BasicDBObject query = new BasicDBObject();
query.put("date", BasicDBObjectBuilder.start("$gte", begin).add("$lte",end).get());
query.put("user", username);
dbCollection.find(query).sort(new BasicDBObject("date", -1));
DBCursor cursor = dbCollection.find(query);
return JSON.serialize(cursor);
}
Does work until you query something like "28-11-2014" to "01-12-2014", It doesn't return anything even though there is a object with date: "30-11-2014". I think this is because of the month change.
Also that object is retreived when you do: "28-11-2014" to "30-11-2014" because of the month staying the same
Please help!
Try something like this
BasicDBObject query = new BasicDBObject();
query.put("date", BasicDBObjectBuilder.start("$gte", fromDate).add("$lte", toDate).get());
collection.find(query).sort(new BasicDBObject("date", -1));
This is the query you would use:
db.posts.find({date: {$gte: start, $lt: end}, user: 'Erik'});
You should first parse your date using SimpleDateFormat or alike to get a Date object.
Then put together your query using BasicDBObject:
BasicDBObject q = new BasicDBObject();
q.append("date", new BasicDBObject().append("$gte", start).append("$lt", end));
q.append("user", "Erik");
collection.find(q);