When doing queries with Morphia is it possible to limit the returned fields (specify a projection)?
Like this on the command line:
db.Institution.find({name: /^Berlin/}, {slug:1})
Or this with the Java driver:
BasicDBObject projection = new BasicDBObject("slug", 1);
collection.find(new BasicDBObject(),projection);
Thanks
You do, see https://code.google.com/p/morphia/wiki/Query#Ignoring_Fields
Pattern regex = Pattern.compile("^Berlin");
Query<InsitutionEntity> query = mongoDataStore.find(InsitutionEntity.class)
.field("name").equal(regex)
.retrievedFields(true, "slug").asList();
(Didn't test it, but it should work like this)
BasicDBObject filter = new BasicDBObject();
filter.append("name", "whoever");
BasicDBObject projection = new BasicDBObject();
projection.append("fieldOne", 1); // 1 == True, it shows the Field.
projection.append("fieldTwo", 1);
projection.append("_id", 0) // 0 == False, it does not show the "_id"
List list = MorphiaObject.datastore.getCollection(MyClass.class).find(filter, projection).toArray();
for (Object each : list) {
System.out.println("Each: " + each.toString());
}
Related
I want to find the specific field value from mongo sub-document, but its retrieving either full data or null value. I am using mongo 3.0.1 driver. Is there any issue with the syntax for the mongo specific driver.
Json data is:
{
"Demo": {
"Demo Data": {
"Building": {
"A": 1,
"B": 2,
"C": 3,
"D": 4,
},
"Mode": "Building"
}
}
}
The code is as below:
DBCollection collection = db.getCollection("demo");
BasicDBObject field = new BasicDBObject();
BasicDBObject document = new BasicDBObject();
field1.put("_id", 0);
field1.put("Demo", 1);
DBCursor cursor = collection.find(document, field);
BasicDBObject object = new BasicDBObject();
BasicDBObject Mode = new BasicDBObject();
while (cursor.hasNext()) {
object = (BasicDBObject) cursor.next();
Mode.put("Mode", object.get(Mode));
System.out.println("Mode value is"+Mode);
}
but using above code, the output showing as:
Mode value is {"Mode":null}
The requirement is to get the following output:
{"Mode": "Building"}.
Please specify where the condition went wrong. Thanks for any help.
I think you are incorrectly assuming that since you have put field1.put("Demo", 1); , it would return you inner document, whereas no matter what it would always return you full document what it will do is omit other fields, but even then it would be full document which is right from the root.
while (cursor.hasNext()) {
object = (BasicDBObject) cursor.next();
Map map = object.toMap();
Mode.put("Mode", map.get("Demo").get("Demo Data").get("Mode")); //pre-check for NPE
System.out.println("Mode value is"+Mode);
}
The docs note:
To use $text in the $match stage, the $match stage has to be the first stage of the pipeline.
Some example JSON:
{"pid":"b00l16vp", "title": "in our time","categories":{"category1":["factual", "arts culture and the media", "history"]}}
{"pid":"b0079mpp", "title": "doctor who", "categories":{"category2":["childrens", "entertainment and comedy", "animation"],"category1":["signed"]}}
{“pid":"b00htbn3"}
{“pid":"b00gdhqw","categories":{"category2":["factual"],"category3":["scotland"],"category4":["lifestyle and leisure", "food and drink"],"category1":["entertainment", "games and quizzes"]}}
I have the following query:
List<BasicDBObject> pipeline = new ArrayList<>()
BasicDBObject criteria = new BasicDBObject()
BasicDBObject theProjections = new BasicDBObject()
AggregateIterable iterable
//value is coming from a parameter
if (value != null) {
//a text index has been created on the title field
criteria.put('$text', new BasicDBObject('$search', value))
}
//cats is coming from a parameter but it will be an array of Strings
if (cats.length != 0) {
ArrayList<BasicDBObject> orList = new ArrayList<>()
ArrayList<BasicDBObject> andList = new ArrayList<>()
BasicDBList theMegaArray = new BasicDBList()
for (int i = 1; i <= 5; i++) {
String identifier = "categories.category" + i
String cleanIdentifier = '$' + identifier
//If the category does not exist, put in a blank category
theMegaArray.add(new BasicDBObject('$ifNull', Arrays.asList(cleanIdentifier, Collections.EMPTY_LIST)))
}
//merges all of the category arrays into 1
theProjections.put("allCategories", new BasicDBObject('$setUnion', theMegaArray))
orList.add(new BasicDBObject("allCategories", new BasicDBObject('$all', cats)))
andList.add(new BasicDBObject('$or', orList))
criteria.put('$and', andList)
}
pipeline.add(new BasicDBObject('$project', theProjections))
pipeline.add(new BasicDBObject('$match', criteria))
//and by default
iterable = collection.aggregate(pipeline)
The issue is if I want to search on the cats, I need the projection to be in the pipeline first but If I want the text then I need the match to be there first. Is there any way I can do both?
It is a pretty simple solution after all.
I created a new criteria object
BasicDBObject criteriaCat = new BasicDBObject()
Added the categories to this instead of the original criteria.
criteriaCat.put('$and', andList)
Put the $match first in the pipeline then the $project and if there are cats run a $match again on the results.
pipeline.add(new BasicDBObject('$match', criteria))
pipeline.add(new BasicDBObject('$project', theProjections))
if (cats.length != 0) {
pipeline.add(new BasicDBObject('$match', criteriaCat))
}
pipeline.add(new BasicDBObject('$sort', sorting))
//and by default
iterable = collection.aggregate(pipeline)
I'm trying to query mongodb with java. The name of my collection is: reads. Here is an example of a specific document I'm querying for:
{
"_id" : {
"d" : "B66929932",
"r" : "15500304",
"eT" : ISODate("2014-09-29T12:03:00Z")
},
"v" : 169000,
"iT" : ISODate("2015-04-10T20:42:07.577Z")
}
I'm trying to query where r = 15500304, eT = 2014-09-29T12:03:00Z and v = 169000. I'm able to do this in mongo pretty easily:
db.reads.find({ "_id.r" : "15500304", "_id.eT" : ISODate("2014-09-29T12:03:00Z"), "$where" : "this.v == 169000;"}).pretty()
I'm unable to figure out how to structure this in java. So far I've got:
DBCollection collection = db.getCollection("reads");
BasicDBObject andQuery = new BasicDBObject();
List<BasicDBObject> obj = new ArrayList<BasicDBObject>();
obj.add(new BasicDBObject("_id.r", "15500304"));
obj.add(new BasicDBObject("_id.eT", "2014-09-29T12:03:00Z"));
obj.add(new BasicDBObject("v", 169000));
andQuery.put("$and", obj);
DBCursor cursor = collection.find(andQuery);
while(cursor.hasNext()){
System.out.println(cursor.next());
}
My Question is: How do I query using these child nodes and return the matching document?
I'm unable to find any clear advice/examples online. Any and all advice is very appreciated.
You were close. Modify your query to:
DBCollection collection = db.getCollection("reads");
BasicDBObject query = new BasicDBObject();
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
String dateInString = "2014-09-29T12:03:00Z";
Date date = df.parse(dateInString);
query.append("status.name", "Expired")
.append("_id.eT", date)
.append("v", 169000);
Or using QueryBuilder:
DBObject query = QueryBuilder.start()
.put("_id.r").is("15500304")
.put("_id.eT").is(date)
.put("v").is(169000)
.get();
DBCursor cursor = collection.find(query);
while(cursor.hasNext()){
System.out.println(cursor.next());
}
I am using a Java driver to run some mongo text searches.
An example of my previous code is (where values is a String passed in):
DBCollection coll = db.getCollection("testCollection");
//create the basic object
DBObject searchCmd = new BasicDBObject();
//create the search cmd
searchCmd.put("text", "testCollection"); // the name of the collection (string)
// define the search word
searchCmd.put("search", value); // the term to search for (string)
// define the return values
searchCmd.put("project", new BasicDBObject("score", 1).append("name", 1).append("path", 0).append("_id", 0));
// get the results
BasicDBObject commandResult = db.command(searchCmd);
// Just out the results key
BasicDBList results = (BasicDBList) commandResult.get("results");
then I loop over the "results" and I get for each it score by
// Get the number ii
BasicDBObject element = (BasicDBObject) results.get(ii);
// Now get the score
double score = (double) element.get("score");
I want to upgrade to use find since that seems the way 2.6 and later prefers it. So far I have:
DBCollection coll = db.getCollection("testCollection");
BasicDBObject query = new BasicDBObject();
query.append("$text", new BasicDBObject("$search", value));
DBCursor cursor = coll.find(query);
However, I am not sure how to get the score.
I tried doing something like:
query.append("score", new BasicDBObject("$meta", "textScore"));
But this does not work. I would like to be able to get the name and the score so that I can then insert them into a new collection that will also hold the score.
I can get the name easily by:
while (cursor.hasNext())
{
DBObject next = cursor.next();
String name = next.get("name").toString();
}
But how do I get the score?
I found this interesting page: http://api.mongodb.org/java/current/
it appears that find can take a second DBObject which has the fields.
I created a new object:
BasicDBObject fields = new BasicDBObject();
fields.append("score", new BasicDBObject("$meta", "textScore"));
and I am calling find using:
DBCursor cursor = coll.find(query, fields);
and now I can get the score the same way I can get the name.
I want to use mongo aggregate in Java, but I cannot define $group code as:
In javascript:
$group = {
'_id':null,
'money_bank':{
'$sum':{
'$cond':[{'$eq'=>{'$type':'bank'}},'$amount',0]
}
}
In java:
BasicDBList eqList = new BasicDBList();
eqList.add("$type");
eqList.add("bank");
DBObject eqObject = BasicDBObjectBuilder.start().add("$eq", eqList).get();
BasicDBList condList = new BasicDBList();
condList.add(eqObject);
condList.add("$amount");
condList.add(0);
DBObject conObj = BasicDBObjectBuilder.start().add("$cond", condList).get();
DBObject sumObj = BasicDBObjectBuilder.start().add("$sum", conObj).get();
DBObject moneyObj = BasicDBObjectBuilder.start().add("money_bank", sumObj).get();
DBObject idObj = BasicDBObjectBuilder.start().add("_id", null).get();
BasicDBList groupList = new BasicDBList();
groupList.add(idObj);
groupList.add(moneyObj);
DBObject group = new BasicDBObject("$group", groupList);
But when I executed code, the error:
"errmsg" : "exception: a group's fields must be specified in an object"
Please help me in Java.
You've slightly misunderstood the correct way to turn this into a Java call, two of the places you've used a BasicDBList you actually needed to use a BasicDBObject instead - that's what that error means, it means you need to use an object not a list.
I've made the change to your code, but I haven't tested it against MongoDB, I leave that up to you.
The minimal changes needed to give you the equivalent of the Javascript is:
BasicDBObject eqArgs = new BasicDBObject("$type", "bank");
DBObject eqObject = BasicDBObjectBuilder.start().add("$eq", eqArgs).get();
BasicDBList condList = new BasicDBList();
condList.add(eqObject);
condList.add("$amount");
condList.add(0);
DBObject conObj = BasicDBObjectBuilder.start().add("$cond", condList).get();
DBObject sumObj = BasicDBObjectBuilder.start().add("$sum", conObj).get();
BasicDBObject groupArgs = new BasicDBObject();
groupArgs.append("_id", null);
groupArgs.append("money_bank", sumObj);
DBObject group = new BasicDBObject("$group", groupArgs);
// Using an assert to check it's correct, remove in production code
Assert.assertThat(group.toString(), is("{ \"$group\" : { \"_id\" : null , " +
"\"money_bank\" : { " +
"\"$sum\" : { " +
"\"$cond\" : [ { \"$eq\" : { \"$type\" : \"bank\"}} , " +
"\"$amount\" , 0]" +
"}" +
"}" +
"}" +
"}"));
However you can simplify it even more (although I admit the Java is still uglier than the JavaScript):
BasicDBList condList = new BasicDBList();
condList.add(new BasicDBObject("$eq", new BasicDBObject("$type", "bank")));
condList.add("$amount");
condList.add(0);
DBObject sumObj = new BasicDBObject("$sum", new BasicDBObject("$cond", condList));
DBObject group = new BasicDBObject("$group", new BasicDBObject("_id", null).append("money_bank", sumObj));
For some reason the BasicDBObjectBuilder is more verbose than using BasicDBObject.