MongoDB-Java: How to make $geoNear to first do query, then distance? - java

I'm trying to query and sort documents as followed:
Query only for documents older than SOMETIME.
Within range of AROUNDME_RANGE_RADIUS_IN_RADIANS.
Get distance for each document.
Sort them by time. New to Old.
Overall it should return up to 20 results.
But it seems that since $geoNear is by default limited to 100 results, I get unexpected results.
I see $geoNear working in the following order:
Gets docs from the entire collection, by distance.
And only then executes the given Query.
Is there a way to reverse the order?
MongoDB v2.6.5
Java Driver v2.10.1
Thank you.
Example document in my collection:
{
"timestamp" : ISODate("2014-12-27T06:52:17.949Z"),
"text" : "hello",
"loc" : [
34.76701564815013,
32.05852053407342
]
}
I'm using aggregate since from what I understood it's the only way to sort by "timestamp" and get the distance.
BasicDBObject query = new BasicDBObject("timestamp", new BasicDBObject("$lt", SOMETIME));
// aggregate: geoNear
double[] currentLoc = new double[] {
Double.parseDouble(myLon),
Double.parseDouble(myLat)
};
DBObject geoNearFields = new BasicDBObject();
geoNearFields.put("near", currentLoc);
geoNearFields.put("distanceField", "dis");
geoNearFields.put("maxDistance", AROUNDME_RANGE_RADIUS_IN_RADIANS));
geoNearFields.put("query", query);
//geoNearFields.put("num", 5000); // FIXME: a temp solution I would really like to avoid
DBObject geoNear = new BasicDBObject("$geoNear", geoNearFields);
// aggregate: sort by timestamp
DBObject sortFields = new BasicDBObject("timestamp", -1);
DBObject sort = new BasicDBObject("$sort", sortFields);
// aggregate: limit
DBObject limit = new BasicDBObject("$limit", 20);
AggregationOutput output = col.aggregate(geoNear, sort, limit);

You could add a $match stage at the top of the pipleine, to filter the documents before the $geonear stage.
BasicDBObject match = new BasicDBObject("timestamp",
new BasicDBObject("$lt", SOMETIME));
AggregationOutput output = col.aggregate(match,geoNear, sort, limit);
The below piece of code now, is not required,
geoNearFields.put("query", query);

Related

Fetch fields starting with 2 particular substring in mongodb using springboot?

I'm trying to find all the documents with first name starting with Ram or Shyam.
I tried the following which actually worked in mongoDb
db.collection.find({
$or: [
{
name: {
$regex: "^Ram"
}
},
{
"name": {
"$regex": "^Shyam"
}
}
]
})
I got around count of 4k documents with this find.
Now when I tried to convert this mongo shell query into java.
Things do work but only name starting with Shyam get filtered. As a result the count also decreases to 2k.
Can someone please look at the below code and tell me why is it happening?
Why things are working in mongodb and not in java.
Java Equivalent Code --
MongoDatabase database = mongoClient.getDatabase("Friends");
MongoCollection<Document> collection = database.getCollection("Friend");
BasicDBObject filter = new BasicDBObject("$or", Arrays.asList
(new BasicDBObject("name", new BasicDBObject("$regex", "^Ram")).append("name", new
BasicDBObject("$regex", "^Shyam"))));
collection.find(filter).forEach((Consumer<Document>) doc -> {
// some java function
}
I figured out. There is just one some thing as $or operator always take array
In my java code I have converted it into array but just forgot that there is no need to append .
The solution will be --
BasicDBObject filter = new BasicDBObject("$or", Arrays.asList
(new BasicDBObject("name", new BasicDBObject("$regex", "^Ram")),new BasicDbObject("name", new
BasicDBObject("$regex", "^Shyam"))));
collection.find(filter).forEach((Consumer<Document>) doc -> {
// some java function
}
As per your mongo query, you need to match against name field but not on id field. That's the mistake
BasicDBObject filter = new BasicDBObject("$or", Arrays.asList
(new BasicDBObject("name", new BasicDBObject("$regex", "^Ram"))
.append("id", new //Here is the mistake
BasicDBObject("$regex", "^Shyam"))));
It should be
BasicDBObject filter = new BasicDBObject("$or", Arrays.asList
(new BasicDBObject("name", new BasicDBObject("$regex", "^Ram"))
.append("name", new BasicDBObject("$regex", "^Shyam"))));

MongoDB: Query using $gte and $lte in java

I want to perform a query on a field that is greater than or equal to, AND less than or equal to(I'm using java btw). In other words. >= and <=. As I understand, mongoDB has $gte and $lte operators, but I can't find the proper syntax to use it. The field i'm accessing is a top-level field.
I have managed to get this to work:
FindIterable<Document> iterable = db.getCollection("1dag").find(new Document("timestamp", new Document("$gt", 1412204098)));
as well ass...
FindIterable<Document> iterable = db.getCollection("1dag").find(new Document("timestamp", new Document("$lt", 1412204098)));
But how do you combine these with each other?
Currently I'm playing around with a statement like this, but it does not work:
FindIterable<Document> iterable5 = db.getCollection("1dag").find(new Document( "timestamp", new Document("$gte", 1412204098).append("timestamp", new Document("$lte",1412204099))));
Any help?
Basically you require a range query like this:
db.getCollection("1dag").find({
"timestamp": {
"$gte": 1412204098,
"$lte": 1412204099
}
})
Since you need multiple query conditions for this range query, you can can specify a logical conjunction (AND) by appending conditions to the query document using the append() method:
FindIterable<Document> iterable = db.getCollection("1dag").find(
new Document("timestamp", new Document("$gte", 1412204098).append("$lte", 1412204099)));
The constructor new Document(key, value) only gets you a document with one key-value pair. But in this case you need to create a document with more than one. To do this, create an empty document, and then add pairs to it with .append(key, value).
Document timespan = new Document();
timespan.append("$gt", 1412204098);
timespan.append("$lt", 1412204998);
// timespan in JSON:
// { $gt: 1412204098, $lt: 1412204998}
Document condition = new Document("timestamp", timespan);
// condition in JSON:
// { timestamp: { $gt: 1412204098, $lt: 1412204998} }
FindIterable<Document> iterable = db.getCollection("1dag").find(condition);
Or if you really want to do it with a one-liner without temporary variables:
FindIterable<Document> iterable = db.getCollection("1dag").find(
new Document()
.append("timestamp", new Document()
.append("$gt",1412204098)
.append("$lt",1412204998)
)
);

Can I get distinct values of a field with only the latest date in the Aggregation framework?

I have a collection full of taxi data which looks something like this (simplified):
{
"TaxiLicense" : "TET123",
"GetOff" : "2015-01-10,00:02:11",
"GetOffLongitude" : 121.41
}
Since this real time data, the Taxi is constantly sending new documents to the collection with a new GetOff time along with GPS coordinates. I only want the GPS coordinates for each distinct Taxi License at the most recent GetOff time.
Is there a way for the Aggregation Framework to do this in Java or do I need to aggregate all the entries and then have my Java program find the latest time for each unique taxi?
I'm currently working with
DBObject taxigroup = new BasicDBObject("$group",
new BasicDBObject("_id",
new BasicDBObject("License","$TaxiLicense")
.append("getoff","$GetOff").append("longitude","GetOffLongitude"))
);
AggregationOutput aggout = taxistationOfCollection.aggregate( Arrays.asList(taxigroup));
You are basically looking for the $last operator. This is commonly used with $sort and it returns the "last" document properties found at the grouping boundary.
Basic pipeline:
[
{ "$sort": { "TaxiLicence": 1, "GetOff": 1 } },
{ "$group": {
"_id": "$TaxiLicence",
"GetOff": { "$last": "$GetOff" },
"GetOffLongitude": { "$last": "$GetOffLongitude" }
}}
]
Or specifically to construct with the Java Driver:
DBObject sort = new BasicDBObject("$sort",
new BasicDBObject("TaxiLicence", 1)
.append("GetOff",1)
);
DBObject group = new BasicDBObject("$group",
new BasicDBObject("_id", "$TaxiLicence"
.append("GetOff", new BasicDBObject( "$first", "$GetOff" ) )
.append("GeOffLongitude", new BasicDBObject( "$first", "$GeOffLongitude" ))
);
AggregationOutput aggout = taxistationOfCollection.aggregate(sort,group);

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

mongodb java driver hide id field in aggregation/projection operation

I'm performing an aggregation operation using the java mongodb driver, and I followed the example from the docs (pasted below). According to this, the _id field should be hidden. However, in my experience with my own code as well as the output of this example, the _id field doesn't hide even when setting the projection value to 0 (it works from the mongo shell). Does anyone know if this is a bug in the mongodb java driver? Or am I doing something incorrectly?
// create our pipeline operations, first with the $match
DBObject match = new BasicDBObject("$match", new BasicDBObject("type", "airfare") );
// build the $projection operation
DBObject fields = new BasicDBObject("department", 1);
fields.put("amount", 1);
fields.put("_id", 0);
DBObject project = new BasicDBObject("$project", fields );
// Now the $group operation
DBObject groupFields = new BasicDBObject( "_id", "$department");
groupFields.put("average", new BasicDBObject( "$avg", "$amount"));
DBObject group = new BasicDBObject("$group", groupFields);
// run aggregation
AggregationOutput output = collection.aggregate( match, project, group );
The _id field you are getting at the end is from the $group operator. If you want to rename it back to department, add another $project to the end of the pipeline and translate _id to department.

Categories