How can I do projection using Criteria in MongoDB? - java

Following is my Java controller method code
public List<MyClass> getStudentList(String studentName){
studentName = ".*" + studentName + ".*";
/* BasicDBObject field = new BasicDBObject();
field.put("regNo", 1);
field.put("name", 1);
field.put("collName", 1);
field.put("deptName", 1); */
Query query = new Query(Criteria.where("name").regex(studentName, "i"));
return mongoTemplate.find(query,MyClass.class, COLLECTION_NAME);
}
My Db looks like this
"regNo" : "1234", "name" : "ABCD", "collName" : "UNIVERSITY COLLEGE", "deptName" : "B.E. Computer Science and Engineering", "result" : [ { "subjCode" : "CS2251", "grade" : "E", "result" : "PASS", "subjName" : "Design and Analysis of Algorithms" }]
I want to select only regNo, name, collName and deptName. How can I do this?

Add this statement to query:
query.fields().include("regNo").include("name").include("collName").include("deptName").exclude("_id");
APPEND
If your codes like:
public List<MyClass> getStudentList(String studentName){
studentName = ".*" + studentName + ".*";
/* BasicDBObject field = new BasicDBObject();
field.put("regNo", 1);
field.put("name", 1);
field.put("collName", 1);
field.put("deptName", 1); */
Query query = new Query(Criteria.where("name").regex(studentName, "i"));
query.fields().include("regNo").include("name").include("collName").include("deptName").exclude("_id");
return mongoTemplate.find(query,MyClass.class, COLLECTION_NAME);
}
It should be fine.
By the query string you provided as:
var q = {
Query : {
"name" : {
"$regex" : ".*valueSearched.*",
"$options" : "i"
}
},
Fields : {
"collName" : 1,
"results" : 0, // disallowed
"_id" : 0,
"de‚Äå‚ÄãptName" : 1,
"name" : 1,
"regNo" : 1
}
};
You actually perform something similar to
query.fields().include("collName").exclude("results").exclude("_id").include("de‚Äå‚ÄãptName").include("name").include("regNo");
instead.
Please check your code accordingly. Maybe you've called query.fields().exculude("results") somewhere; ELSE, it has a bug from some part. :)
The regulation for including and excluding fields are:
If you include some fields, then only return these fields and
"_id" will return implicitly;
only "_id" is allowed to exclude;
If you exclude some normal fields, other fields will returned and
can not include other fields;

See here,it is deprecated but help you to understand
public DBCursor find(DBObject query,
DBObject fields,
int numToSkip,
int batchSize,
int options)
http://api.mongodb.org/java/current/com/mongodb/DBCollection.html#find%28com.mongodb.DBObject,%20com.mongodb.DBObject,%20int,%20int%29
If you dont want any warning ,you may use DBCursor Object.
public DBCursor(DBCollection collection,
DBObject q,
DBObject k,
ReadPreference preference)
Initializes a new database cursor
Parameters:
collection - collection to use
q - query to perform
k - keys to return from the query
preference - the Read Preference for this query
I don't use java driver but it shouldn't be much difficult.

For this issue you have to use #JsonInclude(value=Include.NON_NULL) in your pojo class like this:
#JsonInclude(value=Include.NON_NULL)
String id;

Related

Spring Data MongoOperations: Not able to remove sub-document in array with pull method

Following is the structure of my MongoDB document userActivity.
{
"_id" : ObjectId("5e49569f93e956eeb28eb8a6"),
"userId" : "123",
"likes" : {
"videos" : [
{
"_id" : "abc",
"title" : "This video is part of test setup",
}
]
}
}
I am using Spring Data MongoOperations to manipulate MongoDB collections. And below is the code to remove a video from videos array in likes sub-document. I have tried to first filter the document as per the user's userId. And then apply filter to update function as per videoId.
public UpdateResult removeVideoLike(String videoId, String userId) {
Query queryUser = Query.query( Criteria.where("userId").is(userId) );
Query queryVideo = Query.query( Criteria.where("id").is(videoId) );
Update update = new Update().pull("likes.videos", queryVideo );
return mongoOperations.updateFirst( queryUser , update, UserActivity.class );
}
This runs without errors but the entry is not removed. The UpdateResult has following values
matchedCount = 1
modifiedCount = 0
upsertedId = null
I am confused if it is able to match the entry in the array, why it is not removing it? What I am missing?

BasicDBObjectBuilder not appending mutiple criteria for a single object

I am using Java driver for mongo-db and trying to add multiple query criteria using BasicDBObjectBuilder. I have a text field where an XML is stored as String so we are using regex to form the query.
Below is my query and the output I am getting:
regexQuery.put("REQUEST_XML",BasicDBObjectBuilder
.start("$regex", ".*Main>[\r\n]<.?.?.?.?action>"+MainValue+".*")
.add("$regex", ".*Details>[\r\n]<.?.?.?.?action>" + DetailValue+ ".*").get());
regexQuery.put("NAME", "Video");
What I am getting as query is :
{ "REQUEST_XML" : { "$regex" : ".*Details>[\r\n]<.?.?.?.?action>Change.*"} , "NAME" : "Video"}
The first part with .start("$regex", ".Main>[\r\n]<.?.?.?.?action>"+MainValue+".") is not getting added to query.
Can you please let me know what is the issue ?
You are overwriting the key value pair. "$regex", ".*Details>[\r\n]<.?.?.?.?action>" + DetailValue+ ".*" overwrites "$regex", ".*Main>[\r\n]<.?.?.?.?action>"+MainValue+".*".
Use $or to pass both regex expression.
Something like
BasicDBObject regexQuery = new BasicDBObject();
regexQuery.put("$or", Arrays.asList(new BasicDBObject("REQUEST_XML", new BasicDBObject("$regex", ".*Main>[\r\n]<.?.?.?.?action>"+".*")),
new BasicDBObject("REQUEST_XML", new BasicDBObject("$regex", ".*Details>[\r\n]<.?.?.?.?action>"+".*"))));
regexQuery.put("NAME", "Video");
This should output query like
{ "$or" : [{ "REQUEST_XML" : { "$regex" : ".*Main>[\r\n]<.?.?.?.?action>.*" } }, { "REQUEST_XML" : { "$regex" : ".*Details>[\r\n]<.?.?.?.?action>.*" } }], "NAME" : "Video" }
Using 3.x driver
import static com.mongodb.client.model.Filters.or;
import static com.mongodb.client.model.Filters.regex;
Bson regexQuery = or(regex("REQUEST_XML", ".*Main>[\r\n]<.?.?.?.?action>"+".*"), regex("$regex", ".*Details>[\r\n]<.?.?.?.?action>"+".*"));

Reverse Regex Selection with Spring MongoDB

I have a mongo collection with objects like these:
[
{
"_id" : "a2d",
"entityType" : "Location",
"type" : "STRING",
},
{
"_id" : "a1_order",
"entityType" : "Order",
"type" : "STRING",
}
]
Trying to append the _entityType to all document's id where it is not present at the end id the id (the first object in the above case).
Using mongo with Spring, but I'm already stuck with the first step, to get all the objects with no entityType in id.
Thinking about something like this, with regex, but I'm not sure how should it look like:
Query query = new Query();
query.addCriteria( Criteria.where( "id" ).regex( "here i need the entity type of the current document" ) );
You can build your regex by '^' ('starts with' Regex).
So you need a function who point in all documents and check this filter
List<Document> result = new ArrayList<Document>();
StringBuilder idPrefix = new StringBuilder();
idPrefix.append("^");
idPrefix.append(idCode);
idPrefix.append("_");
List<Bson> filters = new ArrayList<Bson>();
filters.add(Filters.regex("_id", keyPrefix.toString()));
for (Document d : yourCollections.find(Filters.and(filters)))
list.add(d);
You actually want a "reverse regex" here, as you need to use the data in the document in order to match on another field.
Presently you can really only do this with MongoDB using $where, which evaluates JavaScript on the server. So for spring mongo, you need the BasicQuery instead, so we can construct from BasicDBObject and Code primatives:
BasicDBObject basicDBObject = new BasicDBObject("$where",
new Code("!RegExp('_' + this.entityType + '$','i').test(this.id)"));
BasicQuery query = new BasicQuery(basicDBObject);
That will test the "id" field in the document to see if it matches the value from entityType at the "end of the string" and without considering "case". The ! is a Not condition, so the "reverse" of the logic is applied to "not match" where the field actually did end that way.

MongoDB Java - Fetching id in nested json

I have the following json structure. I am trying to retreive run the following mongo query in java where hData._id is not null.
MongoDb Query: db.Collection.find({},{"hData._id":1, "hData.createdBy":1} )
{
"_id" : ObjectId("55567e594e3256a23565ce58"),
"hData" : {
"isDeleted" : false,
"canDelete" : false,
"canUpdate" : false,
"createdBy" : “xyz”,
"createdDate" : "2015-05-15T15:05:30",
"_id" : "7"
},
"changeDate" : "2015-02-19T16:02:12",
}
The code i have written in java to fetch the hData._id is
MongoCursor<Document> cur = col.find(new BasicDBObject("hData._id", new BasicDBObject("$ne",null)))).iterator();
try{
while(cur.hasNext()){
System.out.println(cur.next().getObjectId("hData._id"));
i++;
}
}finally {
cur.close();
}
However, hData._id is returned as null. Could you help me with this ?
You can't get nested properties using dot notation, e.g. x.y.
So in your example you need to get hData first, then call get on the _id. Like this:
MongoCursor<Document> cur = col.find(new BasicDBObject("hData._id", new BasicDBObject("$ne",null))).iterator();
while(cur.hasNext()){
System.out.println(cur.next().get("hData", Document.class).getString("_id"));
}
Also note that in your example hData._id is shown as a String and not as an ObjectId, hence in my example I've used getString().
EDIT
Since it sounds like you may have mixed types for hData._id here's a more robust example with type checking and some extra debug output to illustrate:
MongoCursor<Document> cur = col.find(new BasicDBObject("hData._id", new BasicDBObject("$ne",null))).iterator();
while(cur.hasNext()){
Document doc = cur.next();
System.out.println("Document _id" + doc.get("_id"));
Document hdata = doc.get("hData", Document.class);
Object id = hdata.get("_id");
System.out.println("hData._id " + id);
// check type if you need to
if (id instanceof String) {
System.out.println("hData._id is String: " + id);
} else if (id instanceof ObjectId) {
System.out.println("hData._id is ObjectId: " + id);
} else {
System.out.println("hData._id is of type " + id.getClass().getName());
}
}
You can use Filters and Projections helper methods.
try (MongoCursor<Document> cur = coll.find(Filters.ne("hData._id", null)).projection(Projections.include("hData._id", "hData.createdBy")).iterator()) {
while(cur.hasNext()){
Document doc = cur.next();
Document hData = doc.get("hData", Document.class);
String id = hData.getString("_id");
String createdBy = hData.getString("createdBy");
}
}

How to get only specific fields from Mongodb document?

From system.profile collection I have documents like this:
{
"op" : "command",
"ns" : "..",
"command" : {
"count" : "..",
"query" : {
"$and" : [
...
]
}
},
"responseLength" : 48,
"millis" : 18,
}
Some queries don't have the command field instead they have 'query' field. I want to check if 'command' field exist. If it does then append that to my Stringbuilder object, if not append 'query'.
UPDATE:
I have tried to use Projection as suggested by Davide by but still I am not finding a way to checking if query exist, if it does then append that.
DBCollection collection = mongoTemplate.getCollection("system.profile");
DBObject query = new BasicDBObject("command", new BasicDBObject("$exists",true));
BasicDBObject fields = new BasicDBObject("command",1).append("millis", 1).append("ts", 1);
DBCursor cursor = collection.find(query, fields);
while(cursor.hasNext()) {
sb.append(cursor.next());
sb.append(System.getProperty("line.separator"));
}
return sb;
}
You need to use the following method:
public DBCursor find(DBObject query, DBObject projection);
From java doc:
projection - specifies which fields MongoDB will return from the documents in the result set.

Categories

Resources