I have List of complex object and the complex object contains more than two fields. Something like - {car1, car2, car3} and car has name and type field
Is there a simpler way of inserting a list of car? Something like
DBObject updateObject = new BasicDBObject().append("$push", new BasicDBObject().append("cars", cars)
I tried with $pushAll and it does not seems to be work. I did bit more research and I found it needs information about mapping and that is one of the reasons why this insertion is failing.
What's the best way to do this insertion into MongoDB? Some sample code or direction would be helpful. Please note this has to be done through Java.
Well, if you don't want to translate your car objects to DBObjects manually, there are Mapping Frameworks out there. like Morphia.
Personally, I would just wire up a mapping method manually though. Code could look like this (untested, typos to be expected)
BasicDBObject updateObject = new BasicDBObject();
BasicDBList dbCarList = mapCars(cars);
updateObject.append("$push", new BasicDBObject("cars", dbCarList));
...
private BasicDBList mapCars(List<Car> cars) {
BasicDBList result = new BasicDBList();
for (Car car: cars) {
BasicDBObject dbCar = new BasicDBObject();
dbCar.append("name", car.getName());
result.add(dbCar);
}
return result;
}
Update: as Sammaye pointed out in the comments, replace $push with $set for replacing the list. $push will append non-existing elements to the array without removing what's there form before.
Related
Hello so I am trying to get list of IDs from mongoDB , wrote this code that returns map of id:value I just want it to return just values .
query=new Query(Criteria.where("_id").is("47b3b1ab-2d80-42cf-b289-e3d45497b59f"));
query.fields().include("recordList.id").exclude("_id");
System.out.println( mongoTemplate.findOne(query, Map.class,"Company3"));
{recordList=[{id=rec4vCGPy3EnXRuCM}, {id=recAivYlqtDzZP62C}, {id=recbcLfxuLLB6Jjn0}, {id=reckIA8RdQtDUKCYI}, {id=rectnZZzBJ2iKN8eO}]}
But I need something like this
[rec4vCGPy3EnXRuCM, recAivYlqtDzZP62C, recbcLfxuLLB6Jjn0, reckIA8RdQtDUKCYI, rectnZZzBJ2iKN8eO]
Yes I know I can manipulate result like this to get desired result but I want to know if its possible to achieve same result directly from DB and not like this
List<Map<String,String>> list = (List<Map<String, String>>) mongoTemplate.findOne(query, Map.class,"Company3").get("recordList");
List<String> idList=new ArrayList<>();
for (Map<String, String> stringStringMap : list) {
idList.add(stringStringMap.get("id"));
}
This is what my data looks like
mongodb document. Sorry for inserting image , couldnt copy it without it being unreadable .
oblivion02's solution was a little bit wrong but definitely hinted me in right direction , thank you.
Query query=new Query(Criteria.where("_id").is("adfe377d-6e5b-48f0-b5bb-12b09f57285d"));
System.out.println(mongoTemplate.findDistinct(query,"recordList.id","Company4",String.class));
Just these two lines give me a nice clean list of just id values
[rec4vCGPy3EnXRuCM, recAivYlqtDzZP62C, recbcLfxuLLB6Jjn0, reckIA8RdQtDUKCYI]
Maybe distinct could help. https://docs.mongodb.com/manual/reference/method/db.collection.distinct/
Query query=new Query(Criteria.where("_id").is("adfe377d-6e5b-48f0-b5bb-12b09f57285d"));
System.out.println(mongoTemplate.findDistinct(query,"recordList.id","Company4",String.class));
You can not do that using Mongodb. This database is document oriented, meaning that given a criteria (in this case, an id), you will get a list of documents satifying the criteria where each document has some properties and some values.
To make it easier, you could rewrite your code so you could map your result to a pojo which only contains the list of ids you want and no key.
It would be something similar to the following:
public class Result {
private List<String> ids;
// getters and setters here
#override
public String toString(){
return ids.toString();
}
}
Now your repository method retrieving data will look like the following:
query = new Query(Criteria.where("_id").is("47b3b1ab-2d80-42cf-b289-e3d45497b59f"));
// No need for this
//query.fields().include("recordList.id").exclude("_id");
System.out.println( mongoTemplate.findOne(query, Result.class,"Company3"));
I'd like to imagine there's existing API functionality for this. Suppose there was Java code that looks something like this:
JavaRDD<Integer> queryKeys = ...; //values not particularly important
List<Document> allMatches = db.getCollection("someDB").find(queryKeys); //doesn't work, I'm aware
JavaPairRDD<Integer, Iterator<ObjectContainingKey>> dbQueryResults = ...;
Goal of this: After a bunch of data transformations, I end up with an RDD of integer keys that I'd like to make a single db query with (rather than a bunch of queries) based on this collection of keys.
From there, I'd like to turn the query results into a pair RDD of the key and all of its results in an iterator (making it easy to hit the ground going again for the next steps I'm intending to take). And to clarify, I mean a pair of the key and its results as an iterator.
I know there's functionality in MongoDB capable of coordinating with Spark, but I haven't found anything that'll work with this yet (it seems to lean towards writing to a database rather than querying it).
I managed to figure this out in an efficient enough manner.
JavaRDD<Integer> queryKeys = ...;
JavaRDD<BasicDBObject> queries = queryKeys.map(value -> new BasicDBObject("keyName", value));
BasicDBObject orQuery = SomeHelperClass.buildOrQuery(queries.collect());
List<Document> queryResults = db.getCollection("docs").find(orQuery).into(new ArrayList<>());
JavaRDD<Document> parallelResults = sparkContext.parallelize(queryResults);
JavaRDD<ObjectContainingKey> results = parallelResults.map(doc -> SomeHelperClass.fromJSONtoObj(doc));
JavaPairRDD<Integer, Iterable<ObjectContainingKey>> keyResults = results.groupBy(obj -> obj.getKey());
And the method buildOrQuery here:
public static BasicDBObject buildOrQuery(List<BasicDBObject> queries) {
BasicDBList or = new BasicDBList();
for(BasicDBObject query : queries) {
or.add(query);
}
return new BasicDBObject("$or", or);
}
Note that there's a fromJSONtoObj method that will convert your object back from JSON into all of the required field variables. Also note that obj.getKey() is simply a getter method associated to whatever "key" it is.
I'm trying to get a list of mongo "_ids" from a database using Java. I don't need any other part of the objects in the database, just the "_id".
This is what I'm doing right now:
// Another method queries for all objects of a certain type within the database.
Collection<MyObject> thingies = this.getMyObjects();
Collection<String> ids = new LinkedList<String>();
for (MyObject thingy : thingies) {
ids.add(thingy.getGuid());
}
This seems horribly inefficient though... is there a way just to query mongo for objects of a certain type and return only their "_ids" without having to reassemble the entire object and extract it?
Thanks!
The find() method has an overload where you can pass the keys that you want to retrieve back from the query or those that you don't want.
So you could try this:
BasicDBObject qyery = new BasicDBObject("someKey","someValue");
BasicDBObject keys = new BasicDBObject("_id", 1);
DBCursor cursor = collection.find(query, keys);
I'm very new to MongoDB, and I'm using it along with the Java driver. I have this document structure:
{ "_id" : ObjectId("4f7d2ba6fd5a306d82687d48"), "room" : "Den" }
{ "_id" : ObjectId("4f7d2baafd5a306d82687d49"), "room" : "Foyer" }
{ "_id" : ObjectId("4f7d2fdcfd5a306d82687d4a"), "room" : "Master Bedroom" }
{ "_id" : ObjectId("4f7d301afd5a306d82687d4b"), "room" : "Guest Bedroom" }
{ "_id" : ObjectId("4f7d2b98fd5a306d82687d47"), "code" : "A", "lights" : [ { "name" : "Overhead", "code" : "1" } ], "room" : "Kitchen" }
Where the last line is of particular interest in illustrating what I want to do. Each document is a room and may have a "lights" key corresponding to a value that is an array of sub-documents. From a modeling perspective, I have a house, which has 0-n rooms, each of which has 0-n lights in it. What I want to do in Java is take the name of the room as a parameter, and return a collection of DBObject corresponding to the sub-documents in the lights array -- "get me all lights for room 'kitchen'", for example.
So far, proceeding incrementally in TDD style, I've constructed this query:
public static final String ROOM_KEY = "room";
public static final String EQUALS_KEY = "$eq";
private BasicDBObject buildRoomNameQuery(String roomName) {
BasicDBObject myQuery = new BasicDBObject();
myQuery.put(ROOM_KEY, new BasicDBObject(EQUALS_KEY, roomName));
return myQuery;
}
I realize that this is going to get me the entire room document for the room name I pass in. I'm a bit stuck on what the best way to proceed from here is to get what I want. Is what I'm doing even possible with a simple query, or will I have to retrieve the array and iterate through it in code, casting the elements as DBObject? I'm also open to suggestions for a better document structure for my purpose -- I'm not married to this structure by any means.
For a bit of perspective, I'm quite well versed in SQL and traditional relational databases, if that helps in terms of explanatory analogies. Also, if I'm butchering the MongoDB terminology, please correct me. Thanks in advance.
So, you can do something like this:
DBCollection coll = db.getCollection("test");
BasicDBObject query = new BasicDBObject("room", "Kitchen");
// optional, limit the fields to only have the lights field
BasicDBObject fields = new BasicDBObject("lights",1).append("_id",false);
DBCursor curs = coll.find(query, fields);
while(curs.hasNext()) {
DBObject o = curs.next();
// shows the whole result document
System.out.println(o.toString());
BasicDBList lights = (BasicDBList) o.get("lights");
// shows the lights array -- this is actually a collection of DBObjects
System.out.println(lights.toString());
// optional: break it into a native java array
BasicDBObject[] lightArr = lights.toArray(new BasicDBObject[0]);
for(BasicDBObject dbObj : lightArr) {
// shows each item from the lights array
System.out.println(dbObj);
}
}
Also, I recommend using the QueryBuilder in the Java driver--it's a bit more concise than creating Queries from DBObjects. Even better, check out Morphia, which is an object mapper that uses the Java driver. It natively supports entity models that have lists in them, and serializes/deserializes them to Mongo without needing to deal with the DBObject stuff.
Look at spring mongo package. A really good way to work with mongo using POJO documents
http://www.springsource.org/spring-data/mongodb
You will not need to perform casting and work with strings
You can use an iterator for the fields
Iterator<DBObject> fields = curs.iterator();
while(fields.hasNext()){
DBObject field = (DBObject) fields.next().get("lights");
System.out.println(field.get("name"));
}
For newer versions, consider the use of the Document. To avoid unchecked casts and linter warnings, along with writing your own loop, use the libary's get(final Object key, final Class<T> clazz) method:
List<Document> comments = posts.get("comments", docClazz)
where docClazz is something that you create once:
final static Class<? extends List> docClazz = new ArrayList<Document().getClass();
java.lang.UnsupportedOperationException: This operation is not supported on Query Results
at org.datanucleus.store.query.AbstractQueryResult.contains(AbstractQueryResult.java:250)
at java.util.AbstractCollection.retainAll(AbstractCollection.java:369)
at namespace.MyServlet.doGet(MyServlet.java:101)
I'm attempting to take one list I retrieved from a datastore query, and keep only the results which are also in a list I retrieved from a list of keys. Both my lists are populated as expected, but I can't seem to user retainAll on either one of them.
// List<Data> listOne = new ArrayList(query.execute(theQuery));
// DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
// List<Data> listTwo = new ArrayList(ds.get(keys).values());
// listOne.retainAll(listTwo);
EDIT
Ok, in an attempt to simplify, since this is apparently multiple problems in one, I have stopped using the low level API for datastore and instead of am just pulling one by one with a loop.
List<MyClass> test = (List<MyClass>) query.execute();
List<MyClass> test2 = new ArrayList<MyClass>();
for (String key : favorites) {
test2.add(pm.getObjectById(MyClass.class, key));
}
log.info(test.toString());
test.retainAll(test2);
The above works. It doesn't throw the exception. The below does throw the exception. The only difference is the log.info. I'm stumped.
List<MyClass> test = (List<MyClass>) query.execute();
List<MyClass> test2 = new ArrayList<MyClass>();
for (String key : favorites) {
test2.add(pm.getObjectById(MyClass.class, key));
}
test.retainAll(test2);
It will not let me do new ArrayList() on the query result since it returns an array of objects.
You however need to put them in a new ArrayList(). The returned List implementation apparently doesn't support retainAll(). That's what the exception is telling you.
A "plain" ArrayList supports it. If passing through the ArrayList constructor is not possible due to difference in generic type, then you'll need to manually loop over it and cast each item before adding.
List<Data> listTwo = new ArrayList<Data>();
for (Object object : ds.get(keys).values()) {
listTwo.add((Data) object);
}
listOne.retainAll(listTwo);
Update: as per your update, the entities are apparently lazily loaded/filled. Most ORM's (DataNucleus is one) may indeed do that. As I don't use DataNucleus, I can't go in detail how to fix that in a "nice" way. But you at least now know the root cause of the problem and it can be solved the same way as above. Fill the list test in a loop as well.
If the type of collection you use for your "list of keys" does not support retainAll that exception will be thrown. Which type are you using?
TIP: you don't need to iterate to fill listTwo.
just do:
listTwo.addAll(ds.get(keys).values())