QueryBuilder and BasicDBObjectBuilder usage in MongoDB 3.3.0 above - java

PART 1
Following up solutions to Querying Mongo Collection using QueryBuilder in Mongo 3.3.0
, I tried implementing the suggested methods to implement collection.find(). But was stuck with different parameters been passed a BasicDBObjectBuilder to the same as follows -
BasicDBObjectBuilder queryBuilder = BasicDBObjectBuilder.start();
query.getParams().entrySet().stream().forEach(entry -> queryBuilder.add(entry.getKey(), entry.getValue()));
BasicDBObjectBuilder outputQuery = BasicDBObjectBuilder.start();
outputQuery.add(nameKey, 1);
This doesn't compile :
FindIterable<TDocType> tDocTypeList = collection.find(queryBuilder.get(), outputQuery.get());
This also won't compile :
FindIterable<TDocType> tDocTypeList = collection.find((Bson)queryBuilder.get(), (Bson)outputQuery.get());
And this won't compile either :
org.bson.Document queryBuilder = new org.bson.Document();
query.getParams().entrySet().stream().forEach(entry -> queryBuilder.put(entry.getKey(), entry.getValue()));
org.bson.Document outputQuery = new org.bson.Document();
outputQuery.put(nameKey, 1);
FindIterable<TDocType> tDocTypeList = collection.find(queryBuilder, outputQuery);
Question - How do I specify a projection for the results required out of find() from collections?
PART 2
At one end I can simply replace mongo 3.0.4 java driver's code -
DBObject dbObject = collection.findOne(new QueryBuilder().put(ids).is(id).get())
to
Bson filter = Filters.eq(ids, id);
TDocType doc = collection.find(filter).first();
Now if we have an implementation where we build query through an iteration as in the sample code -
for(Map.Entry<String, Object> entry : query.getParams().entrySet()) {
// this is where its building the query
if(some condition) {
queryBuilder.put(entry.getKey()).is(entry.getValue());
}
if(some other condition) {
queryBuilder.put(entry.getKey()).in(query.getValues());
}
}
Question - Is there a way to implement such appending query Filters
with current mongo 3.3.0+ as well?

The second argument of find method is result type. Try as below.
FindIterable<TDocType> tDocTypeList = dbCollection.find(filter, TDocType.class);
Update for projection
FindIterable<TDocType> tDocTypeList = dbCollection.find(filter, TDocType.class).projection(outputQuery);
Update for appending filters
List<Bson> filters = new ArrayList<>();
for (Map.Entry<String, Object> entry : query.getParams().entrySet()) {
// this is where its building the query
if (some condition){
filters.add(Filters.eq(entry.getKey(), entry.getValue()));
}
if (some other condition){
filters.add(Filters.in(entry.getKey(), query.getValues()));
}
}
FindIterable<TDocType> docType = dbCollection.find(Filters.and(filters));

Related

Why the aggregate function does not give the desired result?

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.

Top Hits Aggregation support in Jest SearchResult

I am using below function to make aggregation query:
private TermsBuilder getAggregations(String[] outputFields) {
TermsBuilder topLevelAggr = AggregationBuilders.terms("level1").field("field1").size(0);
TermsBuilder aggr2 = AggregationBuilders.terms("level2").field("field2").size(0);
TermsBuilder aggr3 = AggregationBuilders.terms("level3").field("field3").size(0);
TermsBuilder aggr4 = AggregationBuilders.terms("level4").field("field4").size(0);
TopHitsBuilder topHitsBuilder = AggregationBuilders.topHits("doc").setSize(1).addSort("fieldValue", SortOrder.DESC);
aggr4.subAggregation(topHitsBuilder);
aggr3.subAggregation(aggr4);
aggr2.subAggregation(aggr3);
topLevelAggr.subAggregation(aggr2);
topHitsBuilder.setFetchSource(outputFields, new String[]{});
return topLevelAggr;
}
I am getting the correct aggregation query from this code, but after executing the query I am not able to extract the top_hits aggregation result. I am using
io.searchbox.core.SearchResult class to get the query result. In this class I couldn't find any support for Top_Hits aggregation.
Please help. Thanks.
You can use the top hits aggregation java API.
Here's an example from elasticsearch docs:
// sr is here your SearchResponse object
Terms agg = sr.getAggregations().get("agg");
// For each entry
for (Terms.Bucket entry : agg.getBuckets()) {
String key = entry.getKey(); // bucket key
long docCount = entry.getDocCount(); // Doc count
logger.info("key [{}], doc_count [{}]", key, docCount);
// We ask for top_hits for each bucket
TopHits topHits = entry.getAggregations().get("top");
for (SearchHit hit : topHits.getHits().getHits()) {
logger.info(" -> id [{}], _source [{}]", hit.getId(), hit.getSourceAsString());
}
}

Streaming the result of an aggregate operation using spring-data-mongodb

I am using spring-data-mongodb and I want to use a cursor for an aggregate operation.
MongoTemplate.stream() gets a Query, so I tried creating the Aggregation instance, convert it to a DbObject using Aggregation.toDbObject(), created a BasicQuery using the DbObject and then invoke the stream() method.
This returns an empty cursor.
Debugging the spring-data-mongodb code shows that MongoTemplate.stream() uses the FindOperation, which makes me thinkspring-data-mongodb does not support streaming an aggregation operation.
Has anyone been able to stream the results of an aggregate query using spring-data-mongodb?
For the record, I can do it using the Java mongodb driver, but I prefer using spring-data.
EDIT Nov 10th - adding sample code:
MatchOperation match = Aggregation.match(Criteria.where("type").ne("AType"));
GroupOperation group = Aggregation.group("name", "type");
group = group.push("color").as("colors");
group = group.push("size").as("sizes");
TypedAggregation<MyClass> agg = Aggregation.newAggregation(MyClass.class, Arrays.asList(match, group));
MongoConverter converter = mongoTemplate.getConverter();
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext = converter.getMappingContext();
QueryMapper queryMapper = new QueryMapper(converter);
AggregationOperationContext context = new TypeBasedAggregationOperationContext(MyClass.class, mappingContext, queryMapper);
// create a BasicQuery to be used in the stream() method by converting the Aggregation to a DbObject
BasicQuery query = new BasicQuery(agg.toDbObject("myClass", context));
// spring-mongo attributes the stream() method to find() operationsm not to aggregate() operations so the stream returns an empty cursor
CloseableIterator<MyClass> iter = mongoTemplate.stream(query, MyClass.class);
// this is an empty cursor
while(iter.hasNext()) {
System.out.println(iter.next().getName());
}
The following code, not using the stream() method, returns the expected non-empty result of the aggregation:
AggregationResults<HashMap> result = mongoTemplate.aggregate(agg, "myClass", HashMap.class);
For those who are still trying to find the answer to this:
From spring-data-mongo version 2.0.0.M4 onwards (AFAIK) MongoTemplate got an aggregateStream method.
So you can do the following:
AggregationOptions aggregationOptions = Aggregation.newAggregationOptions()
// this is very important: if you do not set the batch size, you'll get all the objects at once and you might run out of memory if the returning data set is too large
.cursorBatchSize(mongoCursorBatchSize)
.build();
data = mongoTemplate.aggregateStream(Aggregation.newAggregation(
Aggregation.group("person_id").count().as("count")).withOptions(aggregationOptions), collectionName, YourClazz.class);

Add new data to existing collection in mongo in java

I need two to add new items to the existing data in mongo db.
This is mongo db I have the following data.
{
"_id" : ObjectId("53ce11e7d0881d32d9fa935f"),
"name" : "massive riots",
"lastFeachedTime" : "Jul 15, 2014 12:55:27 PM"
}
Here I have to find the data based on name and the I have to add another two items two it.
Here is my code.
DBObject queryObject = new BasicDBObject().append("name", keyword);
if (null == newFetchTime) {
}
DBObject updateObject = new BasicDBObject();
updateObject.put("nextPageToken", nextPageToken);
updateObject.put("prevPageToken", prevPageToken);
Utils utils = new Utils();
DBCollection collection = utils.getStaging().getCollection("test");
collection.update(queryObject, updateObject, true, false);
But I am do update the existing value get removed and the new data get added.
Can any one tell me how to add the items to the existing data in mongo db.
You want the $set operator in your update. This allows the specified fields to be altered without affecting any of the existing fields in the document, unless the specified field exists in which case that field is overwritten:
DBObject update = new BasicDBObject(
"$set", new BasicDBObject()
.append("nextPageToken",nextPageToken)
.append("prevPageToken",prevPageToken)
);
Works out to the equivalent in shell:
{ "$set" : { "nextPageToken" : nextPageToken , "prevPageToken" : prevPageToken }}

get some attributes from mongodb except one or two

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());
}

Categories