I have been trying to use a geospatial query to fetch data into a pojo with no success.
Here is an example data in my monogdb collection
{
"_id" : ObjectId("597b8c9a21871eeabd5a1cf5"),
"amb_id" : 10,
"amb_number" : "KL25 8945",
"driver_name" : "Amal Shaji",
"driver_licence_id" : "12/4562/2017",
"ownership" : "Amrita Institute of Medical Science",
"ownership_address" : "Peeliyadu Road, Ponekkara, Edappally, Ernakulam",
"location" : {
"type" : "Point",
"coordinates" : [
76.293485,
10.032871
]
}
}
The below mongo query works perfectly fine in the mongoshell
db.trial.find(
{ location :
{ $near :{
$geometry :{
type : "Point" ,
coordinates : [ 76.2 , 9.9 ] } ,
$maxDistance : 20000 }
}
}
)
.pretty();
Here is the pojo that I have been trying to fetch the data into
#Document(collection = "trial")
public class Ambulance {
#Id
String id;
#Field("amb_id")
String ambulanceId;
#Field("amb_number")
String licensePlateNumber;
#Field("driver_name")
String driverName;
#Field("driver_licence_id")
String driverLicenseNumber;
#Field("ownership")
String ownerShip;
#Field("ownership_address")
String ownerShipAddress;
#GeoSpatialIndexed(name="Location")
Double[] location;
//setters and getters
}
Here is the repository I have been using
#ComponentScan
#Repository
public interface AmbulanceRepo extends MongoRepository<Ambulance, String> {
GeoResults<Ambulance> findByLocationNear(Point p, Distance d);
}
and the controller
#RestController
#CrossOrigin(origins = "http://localhost:4200")
#RequestMapping("/")
public class NearbyAmbulanceController {
private AmbulanceRepo ambulanceRepo;
#Autowired
public NearbyAmbulanceController(AmbulanceRepo repo){
this.ambulanceRepo = repo;
}
#RequestMapping(value="/nearbyAmbulance",method = RequestMethod.POST)
#ResponseBody
public GeoResults<Ambulance> getAmbulanceDetails(
#RequestBody LocationRequest locationRequest){
System.out.println("lati "+locationRequest.getLatitude()+ " long "+locationRequest.getLongitude()+" d "+locationRequest.getDistance());
// List<Ambulance> ambulanceList=this.ambulanceRepo.findByLocationNear(new Point(Double.valueOf(locationRequest.getLongitude()),Double.valueOf(locationRequest.getLatitude())),new Distance(locationRequest.getDistance(), Metrics.KILOMETERS));
Point point = new Point(locationRequest.getLatitude(), locationRequest.getLongitude());
Distance distance = new Distance(locationRequest.getDistance(), Metrics.KILOMETERS);
GeoResults<Ambulance> ambulanceList=this.ambulanceRepo.findByLocationNear(point,distance);
System.out.println(ambulanceList);
return ambulanceList;
}
}
Every time I try it I get no results. I am sure that I have data in the given point and nearby locations and I was even able to use the mongoshell to fetch those. I have feeling that the problem is with the way that I have annotated the location field in the entity. Is there anything that I am missing? Any help is appreiciated.
Spring MongoDb Geo spatial query: It is tested well.
public List<MachineDetails> searchbylocation(String lat, String longt, String maxDistance) {
BasicQuery query1 = new BasicQuery("{geoLocation:{ $near: { $geometry: { type: 'Point',coordinates: ["+ lat+","+ longt+" ] }, $minDistance: 10, $maxDistance: "+maxDistance+"}}}");
List<MachineDetails> venues1 = mtemplate.find(query1, MachineDetails.class);
return venues1;
}
Seems like there were more than one database in mongodb that had collections by the name 'trial'. I tried removing all those and now it is working fine. Spring data wasn't able to figure out which collection to be queried on as there were multiple collection with the same name in different databases. Hope this helps someone, cause I have just wasted a couple of days on this.
Related
collection:
{
"username" : "gorim99949#x1post.com",
"channels" : [
{
"channelName" : "yyyyy",
"id" : 383,
"url" : "https://xxxxxxxxx",
}
],
}
#Document(collection = "user_masters")
public class UserMasters {
#Field("username")
private String userName;
#DBRef
private List<UserChannels> UserChannels userChannels;
******getter and setter*********
#Document(collection="channels")
public class UserChannels {
private String url;
************getter/setter****************
Repository class
public UserMasters findByUserName(String username);
#Test method
Query: userRepository.findByUserName(userName).getUserChannels().getUrl();
how can i get value of url from array in the collection in mongodb. I am unable to read values in the array in the collection. thanks in advance for your help
You are going to need a custom query to do that.
Run this query:
db.collection.aggregate({$match: {username: "gorim99949#x1post.com"}}, {$project: {"userChannels": "$channels.url"}});
It will return:
[{"_id": ObjectId("5a934e000102030405000000"),"userChannels": ["https://xxxxxxxxx"]}].
You can remove _id from returning document by setting it to 0 in projection.
I want to query a mongo collection to fetch documents created before a given date. I use the MongoRepository query findByDateBefore(). But turns out, it doesn't return any records. Here's my code implementation (pseudo):
MyDocument model
#Document(collection = "mydocument")
public class MyDocument {
#Id
String name;
Date creationDate;
}
The repository:
#Repository
public interface MyDocumentRepository extends MongoRepository<MyDocument, String> {
List<MyDocument> findByCreationDateBefore(Date date);
// #Query("{'creationDate': {"$lt": ?0}}")
// List<MyDocument> findbyCreationDateBefore(Date date)
}
Service code:
java.util.Date today = new Date();
List<MyDocument> documents = myDocumentRepository.findByCreationDateBefore(today);
The query logged after the service call:
MongoTemplate: find using query: { "creationDate" : { "$lt" : { "$date" : "2020-03-12T13:17:23.784Z"}}} fields: null for class: class com.myrepo.model.MyDocument in collection: mydocument
This return no results. I have tried using custom query (commented code in repository) which also doesn't return any records. Also I tried running the logged query in the mongo console and it does not return any results:
{ "creationDate" : { "$lt" : { "$date" : "2020-03-12T13:17:23.784Z"}}}
But when I use new Date, in mongo console, it works fine. e.g.:
{ "creationDate" : { "$lt" : new Date("2020-03-12T13:17:23.784Z")}}.
Solutions mentioned here doesn't work for me.
I am creating a new endpoint in springboot that will return simple stats on users generated from an aggregate query in a mongo database. However I get a PropertyReferenceException. I have read multiple stackoverflow questions about it, but didn't find one that solved this problem.
We have a mongo data scheme like this:
{
"_id" : ObjectId("5d795993288c3831c8dffe60"),
"user" : "000001",
"name" : "test",
"attributes" : {
"brand" : "Chrome",
"language" : "English" }
}
The database is filled with multiple users and we want using Springboot aggregate the stats of users per brand. There could be any number of attributes in the attributes object.
Here is the aggregation we are doing
Aggregation agg = newAggregation(
group("attributes.brand").count().as("number"),
project("number").and("type").previousOperation()
);
AggregationResults<Stats> groupResults
= mongoTemplate.aggregate(agg, Profile.class, Stats.class);
return groupResults.getMappedResults();
Which produces this mongo query which works:
> db.collection.aggregate([
{ "$group" : { "_id" : "$attributes.brand" , "number" : { "$sum" : 1}}} ,
{ "$project" : { "number" : 1 , "_id" : 0 , "type" : "$_id"}} ])
{ "number" : 4, "type" : "Chrome" }
{ "number" : 2, "type" : "Firefox" }
However when running a simple integration test we get this error:
org.springframework.data.mapping.PropertyReferenceException: No property brand found for type String! Traversed path: Profile.attributes.
From what I understand, it seems that since attributes is a Map<String, String> there might be a schematic problem. And in the mean time I can't modify the Profile object.
Is there something I am missing in the aggregation, or anything I could change in my Stats object?
For reference, here are the data models we're using, to work with JSON and jackson.
The Stats data model:
#Document
public class Stats {
#JsonProperty
private String type;
#JsonProperty
private int number;
public Stats() {}
/* ... */
}
The Profile data model:
#Document
public class Profiles {
#NotNull
#JsonProperty
private String user;
#NotNull
#JsonProperty
private String name;
#JsonProperty
private Map<String, String> attributes = new HashMap<>();
public Stats() {}
/* ... */
}
I found a solution, which was a combination of two problems:
The PropertyReferenceException was indeed caused because attributes is a Map<String, String> which means there is no schemes for Mongo.
The error message No property brand found for type String! Traversed path: Profile.attributes. means that the Map object doesn't have a brand property in it.
In order to fix that without touching my orginal Profile class, I had to create a new custom class which would map the attributes to an attributes object having the properties I want to aggreate on like:
public class StatsAttributes {
#JsonProperty
private String brand;
#JsonProperty
private String language;
public StatsAttributes() {}
/* ... */
}
Then I created a custom StatsProfile which would leverage my StatsAttributes and would be similar to the the original Profile object without modifying it.
#Document
public class StatsProfile {
#JsonProperty
private String user;
#JsonProperty
private StatsAttributes attributes;
public StatsProfile() {}
/* ... */
}
With that I made disapear my problem with the PropertyReferenceException using my new class StatsAggregation in the aggregation:
AggregationResults<Stats> groupResults
= mongoTemplate.aggregate(agg, StatsProfile.class, Stats.class);
However I would not get any results. It seems the query would not find any document in the database. That's where I realied that production mongo objects had the field "_class: com.company.dao.model.Profile" which was tied to the Profile object.
After some research, for the new StatsProfile to work it would need to be a #TypeAlias("Profile"). After looking around, I found that I also needed to precise a collection name which would lead to:
#Document(collection = "profile")
#TypeAlias("Profile")
public class StatsProfile {
/* ... */
}
And with all that, finally it worked!
I suppose that's not the prettiest solution, I wish I would not need to create a new Profile object and just consider the attributes as a StatsAttributes.class somehow in the mongoTemplate query. If anyone knows how to, please share 🙏
I can't seem to get Nested Fields to return when I use the NativeSearchQueryBuilder.withFields(...) method.
Here is my parent Object:
#Document(indexName = "inventory")
public class Inventory
{
#Id
private String id;
#Field(type=FieldType.String)
private String name;
#Field(type=FieldType.Nested, index=FieldIndex.not_analyzed, store=true)
private List<Model> models;
}
And here is the nested Object:
public class Model
{
#Field(type=FieldType.String, index=FieldIndex.not_analyzed, store=true)
private String model;
#Field(type=FieldType.String, index=FieldIndex.not_analyzed, store=true)
private Set<String> series;
}
And the Query
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
nativeSearchQueryBuilder.withQuery(QueryBuilders.matchAllQuery());
nativeSearchQueryBuilder.withFields("models.series");
NativeSearchQuery nativeSearchQuery = nativeSearchQueryBuilder.build();
FacetedPage<Inventory> results = inventoryRepository.search(nativeSearchQuery);
Resulting TotalElements = 529
But each Object in the Content Looks like this (in JSON format):
{
"id":"d5f82880-15bc-45ed-8abb-ff97d0e45da9",
"name": null,
"models": null
}
If I remove the withFields(...) setting, I get back:
{
"id":"d5f82880-15bc-45ed-8abb-ff97d0e45da9",
"name": "Cool Beans",
"models": [
{
"model" : "foo",
"series" : ["bar"]
}
]
}
I've tried models, models.model, models.series, model, series. I can't get withFields working with NestedFields.
Any thoughts?
My understanding of elastic search fields was incorrect. rahulroc tipped me off.
withFields is not the same as source filtering.
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-fields.html
So i'm effectively telling Spring Data ES to do this:
curl localhost:9200/inventory/_search?pretty=true -d '
{
"fields" : ["models.series"],
"query" : {
"match" : {"name" : "cool"}
}
}'
When this is what I want
curl localhost:9200/inventory/_search?pretty=true -d '
{
"_source" : ["models.series"],
"query" : {
"match" : {"name" : "cool"}
}
}'
The withFields approach worked for what I was doing up till I started adding NestedFields. And the current implementation of Spring Data ES that I'm using does not support source filtering.
Source Filtering was just recently added to Spring Data ES 2.0.0.RC1
I am working on a web application using Spring + Jersey + Mongo. I am using elemMatch from spring mongo for some specific result.
Here is my class that will be mapped with the output of the mongo query.
public class ABC {
private String id;
private String dId;
private String lId;
private String hId;
private String pId;
private List<GeneralData> generalRecords;
private List<String> fhistory;
private List<String> mhistory;
private List<String> specialNotes;
}
public class GeneralData {
private Object data;
private HistoryFilter dataType;
}
Now, this is how my data looks like in mongo :
{
"_id" : ObjectId("55ccda7fe4b03cc159c2ce17"),
"_class" : "com.dpdocter.collections.HistoryCollection",
"dId" : "5525ef96e4b077dfc168369b",
"lId" : "5525ef96e4b077dfc16836a1",
"hId : "5525ef96e4b077dfc16836a0",
"pId" : "55ccd9f1e4b03cc159c2ce0b",
"generalRecords" : [
{
"data" : "55ccdde7e4b03cc159c2ce18",
"dataType" : "REPORTS"
},
{
"data" : "55ccda63e4b03cc159c2ce14",
"dataType" : "CLINICAL_NOTES"
}
]
}
I want record based on dId, lId, hId, pId along with elemMatch on generalRecords for a specific dataType (say REPORTS).
And, below is my criteria code to do that.
Criteria matchCriteria = Criteria.where("dId").is(dId).and("lId").is(lId).and("hId").is(hId).and("pId").is(pId).and("generalRecords.dataType").is("REPORTS");
Criteria elementMatchCriteria = Criteria.where("generalRecords").elemMatch(Criteria.where("dataType").is("REPORTS"))
BasicQuery query = new BasicQuery(matchCriteria.getCriteriaObject(), elementMatchCriteria.getCriteriaObject());
ABC abc = mongoOperations.findOne(query, ABC.class);
Result of the above
ABC [id=55ccda7fe4b03cc159c2ce17, dId=null, lId=null, hId=null, pId=null, generalRecords=[GeneralData [data=55ccdde7e4b03cc159c2ce18, dataType=REPORTS]], fhistory=null, mhistory=null, specialNotes=null]
generalRecords is just perfect and as desired, but I just want to include dId, lId, hId, pId, fHistory, mHistory, and specialNotes also. I have already tried :
query.fields().include('fieldName')
also, some other weird query combos, but nothing helps.
Can anyone suggest something.