Spring Boot and Mongo - how to query by nested property - mongodb

I have the following problem - how to query by nested property with #Query?
My Product class (document in Mongo):
#Document(collection = "products")
public class Product {
#Id
private String id;
#DBRef
private ProductProperties properties;
How does it look in Mongo:
{
"_id" : ObjectId("5d5e78d20e8e3d0006079a84"),
"companyId" : "1234",
"properties" : {
"$ref" : "properties",
"$id" : ObjectId("5df8dd2331ea7b4a9384335b")
},
"calendar" : [
{
"startDate" : ISODate("2019-09-04T22:00:00.000Z"),
"endDate" : ISODate("2019-09-09T22:00:00.000Z")
}
],
"_class" : "org.abc.def"
}
ProductProperties class (document in Mongo):
#Document(collection = "product_properties")
public class ProductProperties {
#Id
private String id;
(...)
How does it look in Mongo:
{
"_id" : ObjectId("5df8dd2331ea7b4a9384335b"),
"brand" : "offer Brand_1",
"model" : "offer model_1",
"modelNumber" : "offer model number_1",
"size" : {
...
}
My Spring repository:
public interface ProductRepository extends MongoRepository<Product, String> {
#Query("{'properties.id': ?0 }")
List<Product> findByPropertiesId(String propertiesId);
I've tried also:
List<Product> findByProperties_id(String propertiesId)
or
#Query("{'properties.$id': ?0 }")
List<Product> findByPropertiesId(ObjectId propertiesId);
but WITHOUT success. DO you know what's wrong?
When I invoke:
public List<Product> findProductsByPropertiesId(String properties) {
if (properties == null) {
throw new IllegalArgumentException("onFind: propertiesId should not be null.");
}
return productRepository.findByProperties_Id(properties);
}
I get empty list:(
Maybe there is impossible to do that via Query?

#Query("{'properties.$id': ?0 }")
List<Product> findByPropertiesId(ObjectId propertiesId);
public List<Product> findProductsByPropertiesId(String properties) {
if (properties == null) {
throw new IllegalArgumentException("onFind: propertiesId should not be null.");
}
return productRepository.findByPropertiesId(new ObjectId(properties));
}

Related

Mongodb PojoCodec Found property not present in the ClassModel: id

Be me, perform aggregate query on MongoDb:
db.Rubrica.aggregate([
{$match: {"fiscalCode": "asfjhsdfhgsdfugsdf"}},
{$project: {
"_id":0,
"contactsHeaders":"$contacts.header",
}}
]);
Get the result:
{
"contactsHeaders" : [
{
"id" : "7b47c1637cde49d182b96bcc33d21d0d",
"alias" : "VOL LISTA",
"type" : "L"
},
{
"id" : "ab31c06ecce244bea4ab5b45b89f4fdc",
"alias" : "VOL FEDRO",
"type" : "S"
}
]
}
Now in Java i try to deserialize the Document in:
public class ListOfHeadersWrapper implements Serializable {
List<Header> contactsHeaders;
....
....
public class Header implements Serializable {
private String id;
private String alias;
private TypeEnum type;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
....
....
What I get is:
{
"contacts": [
{
"id": null,
"alias": "VOL LISTA",
"type": "L"
},
{
"id": null,
"alias": "VOL FEDRO",
"type": "S"
}
]
}
id is always null. Logs says Found property not present in the ClassModel: id.
If I deserialize the thing manually with Jackson:
(new ObjectMapper()).readValue(res.toJson(), ListOfHeadersWrapper.class).toString()
I get the correct result:
ListOfHeadersWrapper{contactsHeaders=[Header{id='7b47c1637cde49d182b96bcc33d21d0d', alias='VOL
WHY????
Adding #BsonProperty("id") does the trick:
public class Header implements Serializable {
#BsonProperty("id")
private String id;
...
...
This I think only happens for CosmosDb when used with MongoApi.

Spring data mongodb filter nested object id in collection of String

I would like to filter mongodb collection by multiple nested object's object Id in String with Mongodb aggregation match operation. However spring data mongodb does not converts the String value to object Id in the match operation.
I was able to filter documents by multiple document Ids (primary key, not the nested object's object Id) in String value without any issues as Spring data mongodb converts the String values to oid:
{ "_id" : { "$in" : [{ "$oid" : "61a31853d268434139e7fc11"}, { "$oid" : "61a31853d268434139e7fc12"}]}
What I wanted to achieve is as below :
db.getCollection('products').aggregate(
[
{ "$match" : { "$and" : [{ "type._id" : { "$in" : [
ObjectId("618b99a3b4c24465b074b246"),
ObjectId("60afc0920dab8b6d3ac26355")
] }}]}}
])
But I always get the following :
db.getCollection('products').aggregate(
[
{ "$match" : { "$and" : [{ "type._id" : { "$in" : [
[{ "$oid" : "618b99a3b4c24465b074b246"}, { "$oid" : "60afc0920dab8b6d3ac26355"}]
]}}]}}
])
Spring data mongodb generated 2 dimensional arrays for the OIDs in the $in json
My Mongodb entities:
#Document(collection = "products")
public class Product {
#Id
private String id;
#NotNull
private ProductType type;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Type getType() {
return type;
}
public void setType(ProductType type) {
this.type = type;
}
}
#Document(collection = "product_types")
public class ProductType {
#Id
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
My java code to execute the aggregation:
List<String> typeIds = Arrays.asList("618b99a3b4c24465b074b246", "60ad10ffc723877d8a977149");
List<AggregationOperation> aggregateOperations = new ArrayList<>();
Criteria criteria = Criteria.where("type._id").in(typeIds.stream().map(t -> new ObjectId(t)).collect(Collectors.toList()));
aggregateOperations.add(Aggregation.match(criteria));
Aggregation aggregation = Aggregation.newAggregation(aggregateOperations);
mongoTemplate.aggregate(aggregation, "products", ProductListDTO.class);
The mongodb collection data is as below:
{
"_id" : ObjectId("61a31853d268434139e7fc11"),
"type" : {
"_id" : ObjectId("618b99a3b4c24465b074b246")
}
}
It works as expected. When you log your aggregation pipeline, it writes as {$oid: 'hex'} (Same thing for the date).
Internally driver searches as ObjectId(...) as expected:
//WHEN
Criteria criteria = Criteria.where("type._id")
.in(typeIds.stream().map(ObjectId::new).collect(Collectors.toList()))
.and("date").is(new Date(currDate));
//Searches:
Document{{$match=Document{{type._id=Document{{$in=[618b99a3b4c24465b074b246]}}, date=Tue Dec 14 01:53:09 CET 2021}}}}
//LOG:
{
"aggregate": "__collection__",
"pipeline": [
{
"$match": {
"type._id": {
"$in": [
{
"$oid": "618b99a3b4c24465b074b246"
}
]
},
"date": {
"$date": "2021-12-14T00:53:09.817Z"
}
}
}
]
}

Filtering on a Mongo DB capped Collection using Flux

I have defined my capped collection as below.
#Document("#{#environment.getProperty('customProperties.cappedCollection')}")
#Data
#NoArgsConstructor
#Builder
#AllArgsConstructor
public class ActiveRideCapped {
#Id
private String id;
private String riderId;
private GeoJsonPoint location;
}
I am re-creating this collection at application startup using below code.
#Autowired
MongoOperations mongoOperations;
#PostConstruct
public void createCappedCollection() {
mongoOperations.dropCollection(ActiveRideCapped.class);
mongoOperations.createCollection(ActiveRideCapped.class, CollectionOptions.empty().maxDocuments(20).size(50000).capped());
}
The collection in DB looks like below.
{
"_id" : ObjectId("6037656d2f88af2124ba0d8c"),
"riderId" : "1",
"location" : {
"type" : "Point",
"coordinates" : [
0.1223,
2.2145
]
},
},
{
"_id" : ObjectId("603765532f88af2124ba0d8b"),
"riderId" : "2",
"location" : {
"type" : "Point",
"coordinates" : [
0.5468,
2.7856
]
}
}
I have written a capped repository as below
public interface ActiveRideCappedRepository extends ReactiveMongoRepository<ActiveRideCapped, String> {
#Tailable
Flux<ActiveRideCapped> findActiveRideCappedBy();
}
and I have written below code to create a stream of filtered records.
#Override
public Flux<ActiveRideCapped> getRiderLocationStream(String riderId) {
return activeRideCappedRepository.findActiveRideCappedBy().filter(p -> p.getRiderId()!=riderId);
}
My requirement is, I should get filtered stream based on the riderId I am passing. So if I pass riderId=1 then I should get flux of all records where riderId!=1. But the filtering isn't working! I am getting all the records present in capped collection.
What am I doing wrong here?

Spring Data - Query by List

Is it possible to make a List Query that results in one value? The following does not work. The result is null. The combination of optionValues will result in one variant.
Here is my Data:
OptionValues
[
{
"id" : "5cc248eeaa4a4f7b35454079",
"optionType" : {
"id" : "5cc2301ab2c4cea611ceb13d",
"name" : "size",
"title" : "Size"
},
"value" : "S"
}
]
Variant
{
"id" : "5cc24361b2c4cea611cee8c9,
"optionValues" : [
{
"id" : "5cc248eeaa4a4f7b35454079",
"optionType" : {
"id" : "5cc2301ab2c4cea611ceb13d",
"name" : "size",
"title" : "Size"
},
"value" : "S"
}
],
"price" : 10.99
}
Variant Model
#Data
#Document
public class Variant extends StoreEntity {
#Id
private String id;
#DBRef
private List<OptionValue> optionValues;
...
}
OptionValues Model
#Data
#Document
public class OptionValue {
#Id
private String id;
#DBRef
private OptionType optionType;
private String value;
}
OptionType Model
#Data
#Document
public class OptionValue {
#Id
private String id;
private String name;
private String title;
}
Variant Repository
Variant findByOptionValues(List<OptionValue> optionValues);
Variant findByOptionValuesIn(List optionValues);

findBy to search inside an array in MongoRepository

I have a Mongo document which is like this :
db.user.find()
{
"_id" : ObjectId("560fa0c730a8e74bbd69c094"),
"name" : "abc",
"employee" : [{
"_id" : BinData(3,"v0m0V46pok94fVfwGkFVig=="),
"team" : "Dev Engineer",
}]
}
class User
{
String name;
String id;
}
class Employee
{
UUID id;
String team;
}
public interface EmployeeRepository extends MongoRepository<Employee, String>
{
#Query(value = "{ 'employee._id' : ?0 }")
Medication findByEmployeeId(UUID Id);
}
I want to find the employee by id and write a find method using the employee._id. Is there anyway to do this using MongoRepository, or should I return the entire array and loop through it? I tried the above method findByEmployeeId(UUID Id), but it does not work. I am not sure if the #Query annotation is necessary here. Please suggest!