Was trying to change the data type of all values in a specific field, without use of iterators.
Here tid is the field name
I tried running the code in Mongo using
var ch ={"$addFields" : { "tid" : { "$convert":{"input":"$tid" , "to" : 2}}}}
db.test.aggregate(ch);
Where test is my collection
Java Code :
BasicDBObject fieldObject = new BasicDBObject();
fieldObject.put("$convert",new BasicDBObject().append("input",
"$tid").append("to", 2));
BasicDBObject addField = new BasicDBObject("$addFields",new
BasicDBObject("tid",fieldObject));
System.out.println(addField);
List<BasicDBObject> options = new ArrayList<>();
options.add(addField);
details.aggregate(options);
When I ran the code in mongo command line, the data types are changing from Integer to String.
But no change when I run the same through java code. Is there any issue with my Java Code.
I have to aggregate some data in mongodb through the java driver, and save the aggregation result in a new collection, and later print this collection in a Jtable; the id field can be not simple in my case (i can't know if it is a single value or not) so I followed this:
How to write multiple group by id fields in Mongodb java driver
when I want to save the result in a new collection, doing this:
for (DBObject result : output.results()) {
report.insert(result);
}
i have this error:
Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: fields stored in the db can't have . in them. (Bad Key: 'store.address.store_state')
this is a piece of the collection where I do the aggregation:
{
"_id" : ObjectId("549ef5d17c8db9efa75d49c9"),
"store" : {
"store_id" : NumberLong(2),
"store_type" : "Small Grocery",
"region_id" : NumberLong(78),
"store_name" : "Store 2",
"store_number" : NumberLong(2),
"address" : {
"store_street_address" : "5203 Catanzaro Way",
"store_city" : "Bellingham",
"store_state" : "WA",
"store_postal_code" : "55555",
"store_country" : "USA",
"store_manager" : "Smith"
},
.....}
Can anyone help me?
Note: ID Field can be simple (only a field) or complex (two or more fields)
The new collection I want to create must have all the fields that compose the ID in the aggregation, and all the fields result by aggregation. That's beacause I had to print that collection in this way: Retrieve data from MongoDB collection into Swing JTable
for example:
DBObject fields = new BasicDBObject("unit_sales", 1);
fields.put("store.store_type", 1);
fields.put("store.store_name", 1);
fields.put("store.address.store_city", 1);
fields.put("_id", 0);
DBObject project = new BasicDBObject("$project", fields );
Map<String, Object> dbObjIdMap = new HashMap<String, Object>();
dbObjIdMap.put("store_type", "$store.store_type");
dbObjIdMap.put("store_name", "$store.store_name");
dbObjIdMap.put("store_city", "$store.address.store_city");
DBObject groupFields = new BasicDBObject( "_id", dbObjIdMap);
groupFields.put("average", new BasicDBObject("$avg", "$unit_sales"));
DBObject group = new BasicDBObject("$group", groupFields);
AggregationOutput output = collection.aggregate(project, group);
So i want as result a table with column store_type,store_name, store_city, average.
I know I'm exposing my problem here so bad, but i'm not english and i don't speak very well the language, so please try to understand me.. sorry
I would like to get some information which is in a mongoDB except some attributes.
I tried it in cmd and it worked:
db.orders.find({name:"chabeee"},{_id:0, name:1, worksAt:1})
Then I get this result:
{ "name" : "chabeee", "worksAt" : "jobAtBp" }
{ "name" : "chabeee", "worksAt" : "jobAtRE" }
Its okay, but I want to get in a Java Program. How can I do that?
You have to create one additional BasicDBObject, which will be used for pointing out which exact keys to be fetched. And finally the DBCollection#find(DBObject ref, DBObject keys) method has to be invoked in order to pass the desired projection keys.
BasicDBObject query = new BasicDBObject("name", "chabeee");
BasicDBObject keys = new BasicDBObject();
keys.put("_id", 0);
keys.put("name", 1);
keys.put("worksAt", 1);
BasicDBCursor result = collection.find(query, keys);
Then you just have to iterate over the BasicDBCursor and verify the result.
while (cursor.hasNext()) {
System.out.println(cursor.next());
}
Here is a simple pojo:
public class Description {
private String code;
private String name;
private String norwegian;
private String english;
}
And please see the following code to apply an upsert to MongoDb via spring MongoTemplate:
Query query = new Query(Criteria.where("code").is(description.getCode()));
Update update = new Update().set("name", description.getName()).set("norwegian", description.getNorwegian()).set("english", description.getEnglish());
mongoTemplate.upsert(query, update, "descriptions");
The line to generate the Update object specifies every field of the Item class manually.
But if my Item object changes then my Dao layer breaks.
So is there a way to avoid doing this, so that all fields from my Item class are applied automatically to the update?
E.g.
Update update = new Update().fromObject(item);
Note that my pojo does not extend DBObject.
I found a pretty good solution for this question
//make a new description here
Description d = new Description();
d.setCode("no");
d.setName("norwegian");
d.setNorwegian("norwegian");
d.setEnglish("english");
//build query
Query query = new Query(Criteria.where("code").is(description.getCode()));
//build update
DBObject dbDoc = new BasicDBObject();
mongoTemplate.getConverter().write(d, dbDoc); //it is the one spring use for convertions.
Update update = Update.fromDBObject(dbDoc);
//run it!
mongoTemplate.upsert(query, update, "descriptions");
Plz note that Update.fromDBObject return an update object with all fields in dbDoc. If you just want to update non-null fields, you should code a new method to exclude null fields.
For example, the front-end post a doc like below:
//make a new description here
Description d = new Description();
d.setCode("no");
d.setEnglish("norwegian");
We only need to update the field 'language':
//return Update object
public static Update fromDBObjectExcludeNullFields(DBObject object) {
Update update = new Update();
for (String key : object.keySet()) {
Object value = object.get(key);
if(value!=null){
update.set(key, value);
}
}
return update;
}
//build udpate
Update update = fromDBObjectExcludeNullFields(dbDoc);
The solution for a new spring-data-mongodb version 2.X.X.
The API has evolved, since 2.X.X version there is:
Update.fromDocument(org.bson.Document object, String... exclude)
instead of (1.X.X):
Update.fromDBObject(com.mongodb.DBObject object, String... exclude)
The full solution:
//make a new description here
Description d = new Description();
d.setCode("no");
d.setName("norwegian");
d.setNorwegian("norwegian");
d.setEnglish("english");
Query query = new Query(Criteria.where("code").is(description.getCode()));
Document doc = new Document(); // org.bson.Document
mongoTemplate.getConverter().write(item, doc);
Update update = Update.fromDocument(doc);
mongoTemplate.upsert(query, update, "descriptions");
It works!
you can use save : (if non exist = insert else = upsert)
save(Object objectToSave, String collectionName)
read : javadoc
Just like previous answers said, use mongoTemplate.getConverter().write() and Update.fromDocument() functions. But i found Update.fromDocument() won't add "$set" key and won't work directly, the solution is to add "$set" yourself, like below (PS: I'm using 2.2.1.RELEASE version):
public static Update updateFromObject(Object object, MongoTemplate mongoTemplate) {
Document doc = new Document();
mongoTemplate.getConverter().write(object, doc);
return Update.fromDocument(new Document("$set", doc));
}
If you want to upsert Pojos incl. property String id; you have to exclude the _id field in the fromDBObject method Update.fromDBObject(dbDoc,"_id").
Otherwise you get the Exception:
org.springframework.dao.DuplicateKeyException: { "serverUsed" : "127.0.0.1:27017" , "ok" : 1 , "n" : 0 , "updatedExisting" : false , "err" : "E11000 duplicate key error collection: db.description index: _id_ dup key: { : null }" , "code" : 11000}; nested exception is com.mongodb.MongoException$DuplicateKey: { "serverUsed" : "127.0.0.1:27017" , "ok" : 1 , "n" : 0 , "updatedExisting" : false , "err" : "E11000 duplicate key error collection: db.description index: _id_ dup key: { : null }" , "code" : 11000}
because the _id field of the first is null
{
"_id" : null,
...
}
Fullcode based on #PaniniGelato answer would be
public class Description(){
public String id;
...
}
Description d = new Description();
d.setCode("no");
d.setName("norwegian");
d.setNorwegian("norwegian");
d.setEnglish("english");
//build query
Query query = new Query(Criteria.where("code").is(description.getCode()));
//build update
DBObject dbDoc = new BasicDBObject();
mongoTemplate.getConverter().write(d, dbDoc); //it is the one spring use for convertions.
Update update = Update.fromDBObject(dbDoc, "_id");
//run it!
mongoTemplate.upsert(query, update, "descriptions");
Then the upsert is working in the cases of insert and update. Corrections & thoughts are welcome ;)
This is what I am doing for the time being. Not so much elegant way to do it, but it does save a precious DB call:
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.util.JSON;
/**
* Perform an upsert operation to update ALL FIELDS in an object using native mongo driver's methods
* since mongoTemplate's upsert method doesn't allow it
* #param upsertQuery
* #param object
* #param collectionName
*/
private void performUpsert(Query upsertQuery, Object object, String collectionName){
ObjectMapper mapper = new ObjectMapper();
try {
String jsonStr = mapper.writeValueAsString(object);
DB db = mongoTemplate.getDb();
DBCollection collection = db.getCollection(collectionName);
DBObject query = upsertQuery.getQueryObject();
DBObject update = new BasicDBObject("$set", JSON.parse(jsonStr));
collection.update(query, update, true, false);
} catch (IOException e) {
LOGGER.error("Unable to persist the metrics in DB. Error while parsing object: {}", e);
}
}
There are two cases here that need to be distinguished:
Update an item that was previously fetched from the DB.
Update or insert (upsert) an item you created by code.
In Case 1) You can simply use mongoTemplate.save(pojo, "collection"), because your POJO will already have a filled ObjectID in its id field.
In case 2) You have to explain to mongo what "already exists" means in case of your domain model: By default the mongoTemplate.save() method updates an existing item, if there is one with that same ObjectId. But with a newly instantiated POJO you do not have that id. Therefore the mongoTemplate.upsert() method has a query parameter that you can create like this:
MyDomainClass pojo = new MyDomainClass(...);
Query query = Query.query(Criteria.where("email").is("user1#domain.com"));
DBObject dbDoc = new BasicDBObject();
mongoTemplate.getConverter().write(pojo, dbDoc); //it is the one spring use for convertions.
dbDoc.removeField("_id"); // just to be sure to not create any duplicates
Update update = Update.fromDBObject(dbDoc);
WriteResult writeResult = mongoTemplate.upsert(query, update, UserModel.class);
I ran into the same problem. In het current Spring Data MongoDB version no such thing is available. You have to update the seperate fields by hand.
However it is possible with another framework: Morphia.
This framework has a wrapper for DAO functionality: https://github.com/mongodb/morphia/wiki/DAOSupport
You can use the DAO API to do things like this:
SomePojo pojo = daoInstance.findOne("some-field", "some-value");
pojo.setAProperty("changing this property");
daoInstance.save(pojo);
I think that:
Description add a property
#Id
private String id;
then get a document by the query condition,set Description's id by document's id.
and save
Just use ReflectionDBObject - if you make Description extend it, you should just get your object's fields transferred to Update reflectively, automagically. The note from above about null fields included in the update still holds true.
public void saveOrUpdate(String json) {
try {
JSONObject jsonObject = new JSONObject(json);
DBObject update1 = new BasicDBObject("$set", JSON.parse(json));
mongoTemplate.getCollection("collectionName").update(new Query(Criteria.where("name").is(jsonObject.getString("name"))).getQueryObject(), update1, true, false);
} catch (Exception e) {
throw new GenericServiceException("Error while save/udpate. Error msg: " + e.getMessage(), e);
}
}
this is very simple way to save json string into collection using mongodb
and spring.
This method can be override to use as JSONObject.
#Override
public void updateInfo(UpdateObject algorithm) {
Document document = new Document();
mongoTemplate.getConverter().write(algorithm, document);
Update update = Update.fromDocument(document);
mongoTemplate.updateFirst(query(where("_id").is(algorithm.get_id())), update, UpdateObject.class);
}
After upsert, I was Tring to fetch same record but it was given me the old one.
But in dB I am having new records.
I am using Java Mongo driver for the DB interaction. I have regular updates to be performed on the DB rows and the object that is quite nested. Something like this :
MyObject:
{
_id: dbGeneratedId,
myId: "A String ID that i created",
myTime: "new Date()",
myList:
[
{
myString: "abcdefghij",
myInteger: 9000
},
{
myString: "qwertyasdf",
myInteger: 9001
},
{
myString: "loremipsum",
myInteger: 9002
}
]
}
Each update involves either adding a new List item under myList or appending some string to the myString object in each of the List item. I found a lot of references for writing/finding items and none for updating items in a nested object. Can someone help me with this.
Edit 1: It would also be helpful if someone points out how to get one of the List items based on a myInteger search
PS: new to mongo thro Java, excuse my ignorance
You can insert new list item using the $push operator.
You can run the following command on the shell to add new list item.
db.myObject.update({"myId" : "A String ID that i created"},{$push:{myList: {myString:"new string", myInteger:9003}}})
You can add list item using Java Driver as follows.
DBCollection coll = db.getCollection("myObject");
DBObject query = new BasicDBObject("myId", "A String ID that i created");
DBObject listItem = new BasicDBObject();
listItem.put("myString", "my new string");
listItem.put("myInteger", 9003);
DBObject updateObj = new BasicDBObject("myList", listItem);
coll.update(query, new BasicDBObject("$push", updateObj));
You can get single element as follows on the shell.
db.myObject.find({"myList.myInteger" : 9003}, {"myList.$" : 1})
From Java Driver you can run same code as follows :
DBCursor cur = coll.find(new BasicDBObject("myList.myInteger", 9003), new BasicDBObject("myList.$", 1));
You can remove object as follows :
db.nested.update({"myId" : "A String ID that i created"},{$pull:{myList: {myString:"new string", myInteger:9003}}})
In Java you can do it as follows :
DBCollection coll = db.getCollection("myObject");
DBObject query = new BasicDBObject("myId", "A String ID that i created");
DBObject listItem = new BasicDBObject();
listItem.put("myString", "my new string");
listItem.put("myInteger", 9003);
DBObject updateObj = new BasicDBObject("myList", listItem);
coll.update(query, new BasicDBObject("$pull", updateObj));
PS : Currently it is not possible to update all items in an array. There is an open issue on the Jira. You can check it from JIRA-1243