Is there an easy way to get the ID (ObjectID) of the last inserted document of a mongoDB instance using the Java driver?
I just realized you can do this:
BasicDBObject doc = new BasicDBObject( "name", "Matt" );
collection.insert( doc );
ObjectId id = (ObjectId)doc.get( "_id" );
To avoid casting from Object to ObjectId, given a com.mongodb.client.MongoCollection collection and a org.bson.Document doc, you can do the following:
collection.insert(doc);
ObjectId id = doc.getObjectId("_id");
It's safe to do
doc.set("_id", new ObjectId())
if you look at driver code
if ( ensureID && id == null ){
id = ObjectId.get();
jo.put( "_id" , id );
}
public static ObjectId get(){
return new ObjectId();
}
I do not know about the Java driver but for posterity, the getLastError command can be run to get the _id of a write, even an upsert (as of 1.5.4)
After a document is inserted into the MongoDB collection, the successful insertion should update required fields (viz. _id). You may query the inserted object for the _id.
In MongoTemplate.class has a method
protected <T> void doInsert(String collectionName, T objectToSave, MongoWriter<T> writer) {
assertUpdateableIdIfNotSet(objectToSave);
initializeVersionProperty(objectToSave);
maybeEmitEvent(new BeforeConvertEvent<T>(objectToSave, collectionName));
DBObject dbDoc = toDbObject(objectToSave, writer);
maybeEmitEvent(new BeforeSaveEvent<T>(objectToSave, dbDoc, collectionName));
Object id = insertDBObject(collectionName, dbDoc, objectToSave.getClass());
populateIdIfNecessary(objectToSave, id);
maybeEmitEvent(new AfterSaveEvent<T>(objectToSave, dbDoc, collectionName));
}
and the method will set id for us
protected void populateIdIfNecessary(Object savedObject, Object id) {
if (id == null) {
return;
}
if (savedObject instanceof BasicDBObject) {
DBObject dbObject = (DBObject) savedObject;
dbObject.put(ID_FIELD, id);
return;
}
MongoPersistentProperty idProp = getIdPropertyFor(savedObject.getClass());
if (idProp == null) {
return;
}
ConversionService conversionService = mongoConverter.getConversionService();
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(savedObject.getClass());
PersistentPropertyAccessor accessor = entity.getPropertyAccessor(savedObject);
if (accessor.getProperty(idProp) != null) {
return;
}
new ConvertingPropertyAccessor(accessor, conversionService).setProperty(idProp, id);
}
we can see if the entity is a sub-class of BasicDBObject,it will set a id for us.
I think the answer to this is "No".
What you can do is provide your the _id yourself, either manually, or implement the CollectibleCodec mechanism (which is exactly what BasicBDDocument does). However all these solutions involve generating the ID clientside.
Having said that, I don't think there's any problem with generating the _id clientside.
This is insert operation:
DBCollection table1 = db.getCollection("Collection name");
BasicDBObject document = new BasicDBObject();
document.put("_id",value);
document.put("Name", name);
table1.insert(document);
After insert u get last inserted id:
DBCollection tableDetails = db.getCollection("collection name");
BasicDBObject queryDetails = new BasicDBObject();
queryDetails.put("_id", value);
DBCursor cursorDetails =tableDetails.find(queryDetails);
DBObject oneDetails;
oneDetails=cursorDetails.next();
String data=oneDetails.get("_id").toString();
System.out.println(data);
after getting value convert to inter type.
Related
I wrote query in MongoDB which retrieved two columns, one for id and the other is array. I have tried to read the array using Java but I cannot.
try {
Bson filter = eq("_id", "1260718680159199238");
Bson project = eq("Tweets.Text", 1L);
MongoClient mongoClient = new MongoClient(
new MongoClientURI(
"mongodb://localhost:27017/?readPreference=primary&appname=MongoDB%20Compass%20Isolated%20Edition&ssl=false"
)
);
MongoDatabase database = mongoClient.getDatabase("Amazon-tweets");
MongoCollection<Document> collection = database.getCollection("tweets");
FindIterable<Document> result = collection.find(filter).projection(project);
for (Document doc : result) {
String s = doc.getString("Tweets.1");
System.out.println("orig " + s);
}
}//END try
catch (Exception e) {
}//
You can't use the dot notation to retrieve values like that post query.
Depending on the driver version you're using you can use either of these solutions:
(I'm assuming each tweet is a separate document here, but same should apply if it's something different)
List<Document> tweets = (List<Document>) doc.get("Tweets");
List<Document> tweets = doc.getList("Tweets", Document.class); // since 3.10
tweets.forEach(...
You can find the documentation here: https://mongodb.github.io/mongo-java-driver/
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.
In the Java Mongo DB driver version 3 the API has changed as compared to the version 2. So a code like this does not compile anymore:
BasicDBObject personObj = new BasicDBObject();
collection.insert(personObj)
A Collection insert works only with a Mongo Document.
Dealing with the old code I need to ask the question:
What is the best way to convert a BasicDBObject to a Document?
We Can Convert BasicDBObject
to Document by the following way
public static Document getDocument(DBObject doc)
{
if(doc == null) return null;
return new Document(doc.toMap());
}
as Document itself is an implementation of Map<String,Object>.
and BasicDBObject can be too be catch in DBObject as BasicDBObject is an implementation of DBObject.
#Black_Rider for you too
I think the easiest thing to do is just change your code to use a Document as opposed to BasicDBObject.
So change
BasicDBObject doc = new BasicDBObject("name", "john")
.append("age", 35)
.append("kids", kids)
.append("info", new BasicDBObject("email", "john#mail.com")
.append("phone", "876-134-667"));
To
import org.bson.Document;
...
Document doc = new Document("name", "john")
.append("age", 35)
.append("kids", kids)
.append("info", new BasicDBObject("email", "john#mail.com")
.append("phone", "876-134-667"));
and then insert into the collection
coll.insertOne(doc);
You'll need to change other bits of code to work with MongoDB 3+
MongoDatabase vs. DB
MongoCollection vs DBCollection
The Document is very similar to the BasicDBObject. I am not quite sure what you are referring to as a way to convert BasicDBObjects to Documents, but the Document object provides some very useful methods.
Document.parse(string) will return a Document if you feed it in a JSON string.
Document.append("key", Value) will add to the fields of a Document.
As for the code in your post, this should compile with version 3:
Document personObj = new Document();
collection.insertOne(personObj)
See
Java Driver 3.0 Guide
and
MongoDB Java Driver 3.0 Documentation
#SuppressWarnings("unchecked")
public static Document getDocument(BasicDBObject doc)
{
if(doc == null) return null;
Map<String, Object> originalMap = doc.toMap();
Map<String, Object> resultMap = new HashMap<>(doc.size());
for(Entry<String, Object> entry : originalMap.entrySet())
{
String key = entry.getKey();
Object value = entry.getValue();
if(value == null)
{
continue;
}
if(value instanceof BasicDBObject)
{
value = getDocument((BasicDBObject)value);
}
if(value instanceof List<?>)
{
List<?> list = (List<?>) value;
if(list.size() > 0)
{
// check instance of first element
Object firstElement = list.get(0);
if(firstElement instanceof BasicDBObject)
{
List<Document> resultList = new ArrayList<>(list.size());
for(Object listElement : list)
{
resultList.add(getDocument((BasicDBObject)listElement));
}
value = resultList;
}
else
{
value = list;
}
}
}
resultMap.put(key, value);
}
Document result = new Document(resultMap);
return result;
}
I am inserting some value into mongodb in this way.
MongoClient mongo = new MongoClient( "localhost" , 27017 );
DB db = mongo.getDB("test");
DBCollection table = db.getCollection("paramDescMapper");
String key = UUID.randomUUID().toString();
String value = "{\"param0\":\"Car Make\",\"param1\":\"Car Model\",\"param2\":\"Car Variant\",\"param3\":\"Car Year\",\"param4\":\"Car Number\"}";
JSONObject jsonObject = new JSONObject(value);
// create a document to store key and value
BasicDBObject document = new BasicDBObject();
document.put("apiKey", key);
document.put("apiParamDesc", jsonObject.toString());
table.insert(document);
It is inserting data in this way.
{ "_id" : { "$oid" : "534251125f1ab7ec747298cd"} , "apiKey" : "1eb9b9e3-3af1-4b25-b7ea-1f2fcb1d9af6" , "apiParamDesc" : "{\"param0\":\"Car Make\",\"param1\":\"Car Model\",\"param2\":\"Car Variant\",\"param3\":\"Car Year\",\"param4\":\"Car Number\"}"}
Now i want to get apiParamDesc value using apiKey. Like how we get data in mysql.
Select apiParamDesc where apiKey =
'1eb9b9e3-3af1-4b25-b7ea-1f2fcb1d9af6';
I googled a lot but could not found anything. This is how i am trying to get this apiParamDesc
BasicDBObject whereQuery = new BasicDBObject();
whereQuery.put("apiKey", "1eb9b9e3-3af1-4b25-b7ea-1f2fcb1d9af6");
DBCursor cursor = table.find(whereQuery);
while(cursor.hasNext()) {
System.out.println(cursor.next());
}
But this is giving me entire row. I want only apiParamDesc in a String.
Please help me.
Thanks
You can easily do it with aggregation framework. Below is the example which can resolve your issue:
// create our pipeline operations, first with the $match
DBObject match = new BasicDBObject("$match", new BasicDBObject("apiKey", "1eb9b9e3-3af1-4b25-b7ea-1f2fcb1d9af6"));
// build the $projection operation
DBObject fields = new BasicDBObject("apiParamDesc", 1);
fields.put("_id", 0);
DBObject project = new BasicDBObject("$project", fields );
// run aggregation
AggregationOutput output = collection.aggregate( match, project);
You can also make use of only db.coll.find(< criteria >, < projection >);
BasicDBObject query = new BasicDBObject(new BasicDBObject("apiKey", "1eb9b9e3-3af1-4b25-b7ea-1f2fcb1d9af6"), new BasicDBObject("apiParamDesc", 1).append("_id", 0));
//Which is equivalent to a follwoing query
//'db.coll.find({"apiKey": "1eb9b9e3-3af1-4b25-b7ea-1f2fcb1d9af6"}, {"apiParamDesc": 1,"_id": 0});'
cursor = coll.find(query);
One more thing to update you on "I want only apiParamDesc in a String.", is if you are storing string like
"apiParamDesc" : "{\"param0\":\"Car Make\",\"param1\":\"Car Model\",\"param2\":\"Car Variant\",\"param3\":\"Car Year\",\"param4\":\"Car Number\"}
You cannot query on those sub level fields like param0, param1 ...
You data should look like :
{
"_id":{
"$oid":"534251125f1ab7ec747298cd"
},
"apiKey":"1eb9b9e3-3af1-4b25-b7ea-1f2fcb1d9af6",
"apiParamDesc":{
"param0":"Car Make",
"param1":"Car Model",
"param2":"Car Variant",
"param3":"Car Year",
"param4":"Car Number"
}
}
I want only apiParamDesc in a String.
You cannot however, you can get a document (object) returned with only the apiParamDesc as its single field (my Java is rusty):
BasicDBObject whereQuery = new BasicDBObject();
whereQuery.put("apiKey", "1eb9b9e3-3af1-4b25-b7ea-1f2fcb1d9af6");
BasicDBObject fields = new BasicDBObject();
fields.put("apiParamDesc", 1);
fields.put("_id", 0);
DBCursor cursor = table.find(whereQuery, fields);
while(cursor.hasNext()) {
System.out.println(cursor.next());
}
I am trying to update a document using as criteria the Mongo ObjectId which I store in a parallel field contentGroupId but it is not working.
What is the best practice for updating a document in Mongo using the Java Driver when you are using the internal id as the primary key?
#Override
public void updateContentGroup( ContentGroup contentGroup ) {
DBCollection contentGroupCollection = db.getCollection( "contentGroups" );
Gson gson = new Gson();
String json = gson.toJson( contentGroup );
DBObject contentGroupDoc = (DBObject) JSON.parse( json );
BasicDBObject criteriaObject = new BasicDBObject();
criteriaObject.append( "_id", "ObjectId(\"520a56b730047339c26ec1fa\")");
contentGroupCollection.update( criteriaObject, contentGroupDoc );
}
Thanks-in-advance,
Guido
I think your problem is here:
criteriaObject.append( "_id", "ObjectId(\"520a56b730047339c26ec1fa\")");
Should be:
criteriaObject.append( "_id", new ObjectId("520a56b730047339c26ec1fa");
That way the field value is treated as an object id, not as string (containing the literal "ObjectId")