I am using spring-data-mongo and trying to access the dbref objects with params. My project looks like this:
My models are as follows:
i. First Document is "Cars"
@Document("cars")
class CarDocument {
@Id
private String id;
private String name;
private String madeInCountry;
private String model;
private String madeInYear;
}
ii. Second document is "tools"
Document("tools")
class ToolDocument {
@Id
private String id;
private String name;
private String madeInCountry;
private String madeInYear;
private List<UsedIn> usedIn = new ArrayList<>();
}
iii. The third is embedded model "UsedIn" in (ii.) Third embedded model represents where tools are used to make cars in the manufacturing house.
class UsedIn {
@DBRef
private CarDocument car;
private DateTime usedDate;
private String usedByUsername;
}
My DAO's are as follows:
public interface CarDAO extends MongoRepository<CarDocument, String>
{
public CarDocument findByMadeInCountry(String madeInCountry);
}
public interface ToolDAO extends MongoRepository<ToolDocument, String>
{
public ToolDocument findByMadeInCountry(String madeInCountry);
}
Now I need list of all the "Tools" which is used in the specific car. Say a. when car is madeInCountry: "germany" and b. tool is madeInCountry: "germany"
I see that we can't apply search directly on DBRef documents. like :
String madeInCountry = "germany";
toolDAO.findByMadeInCountryAndUsedInCarMadeInCountry(madeInCountry,madeInCountry);
I get this error:
"Invalid path reference car.madeInCountry! Associations can only be pointed to directly or via their id property!"
How to this?
Do I need to do two DAO calls? Say i. first get all the cars with madeInCountry is germany
String madeInCountry = "germany";
carDAO.findByMadeInCountry(madeInCountry);
ii. findTools by the list of carDocuments and String.
I dont know, how to call this dao with list of CarDocuments and madeInCountry String ?
Do I need to use some $lookup functionality?
Thanks
DBRef is MongoDB's native element to express references to other documents with an explicit format { $db : …, $ref : …, $id : … } that holds information about the target database, collection, and id value of the references element, best suited to link to documents distributed across different collections.
The issue arises because the collection name is not explicitly given in your model class, so spring-data derives the collection name from the class name ( Courses ) into camel case ( courses ). Since your actual collection is called Courses , no results are found.
DBRefs are references from one document to another using the value of the first document's _id field, collection name, and, optionally, its database name, as well as any other fields. DBRefs allow you to more easily reference documents stored in multiple collections or databases.
MongoTemplate provides a simple way for you to save, update, and delete your domain objects and map those objects to documents stored in MongoDB. You can save, update and delete the object as shown below. MongoOperations is the interface that MongoTemplate implements.
You can try below aggregation.
Update your UsedIn
class to below.
private Long carId;
private CarDocument car;
private Date usedDate;
private String usedByUsername;
Mongo Shell Query:
db.tools.aggregate([{
"$match": {
"madeInCountry": "germany"
}
}, {
"$unwind": "$usedIn"
}, {
"$lookup": {
"from": "cars",
"localField": "usedIn.carId",
"foreignField": "_id",
"as": "usedIn.car"
}
}, {
"$unwind": "$usedIn.car"
}, {
"$match": {
"usedIn.car.madeInCountry": "germany"
}
}, {
"$group": {
_id: "$_id",
usedIns: {
"$push": "$usedIn"
}
}
}])
Spring Aggregation Code:
Criteria toolQuery = Criteria.where("madeInCountry").in("germany");
MatchOperation toolMatchOperation = new MatchOperation(toolQuery);
LookupOperation lookupOperation = LookupOperation.newLookup().
from("cars").
localField("usedIn.carId").
foreignField("_id").
as("usedIn.car");
Criteria carQuery = Criteria.where("usedIn.car.madeInCountry").is("germany");
MatchOperation carMatchOperation = new MatchOperation(carQuery);
TypedAggregation<ToolDocument> aggregation = Aggregation.newAggregation(ToolDocument.class, toolMatchOperation, Aggregation.unwind("usedIn"), lookupOperation, Aggregation.unwind("usedIn.car"), carMatchOperation,
Aggregation.group("id").push("usedIn").as("usedIn"));
List<ToolDocument> results = mongoTemplate.aggregate(aggregation, ToolDocument.class).getMappedResults();
Methods to load the data.
Car Data
public void saveCar() {
carDao.deleteAll();
CarDocument carDocument1 = new CarDocument();
carDocument1.setId(1L);
carDocument1.setName("audi");
carDocument1.setMadeInCountry("germany");
carDao.save(carDocument1);
}
Tool Data
public void saveTool() {
toolDao.deleteAll();
ToolDocument toolDocument1 = new ToolDocument();
toolDocument1.setId(1L);
toolDocument1.setName("wrench");
toolDocument1.setMadeInCountry("germany");
UsedIn usedIn1 = new UsedIn();
usedIn1.setCarId(1L);
usedIn1.setUsedByUsername("user");
usedIn1.setUsedDate(new Date());
List<UsedIn> usedIns1 = new ArrayList<>();
usedIns1.add(usedIn1);
toolDocument1.setUsedIn(usedIns1);
toolDao.save(toolDocument1);
}
Update:
To answer your question about accessing the DBRefs
ii. findTools by the list of carDocuments and String.
I dont know, how to call this dao with list of CarDocuments and madeInCountry String ?
public List<ToolDocument> findByMadeInCountryAndUsedInCarIn(String madeInCountry, List<CarDocument> carDocuments);
Like I noted in the first comment the second call that you need is a call over embedded dbref with list of car documents value. The query will look for a match and return all the car documents when a match is found for a tool document. Meaning you will get tool documents which is made in Germany that has atleast one used in - cardocument made in Germany.
Sharded update($lookup
equalivent)( Idea taken from here MongoDB to Use Sharding with $lookup Aggregation Operator )
List<ToolDocument> toolDocuments = toolDao.findByMadeInCountry("germany");
List<Long> carIds = toolDocuments.stream().map(tool -> tool.getUsedIn().stream().map(UsedIn::getCarId).collect(Collectors.toList())).flatMap(List::stream).collect(Collectors.toList());
List<CarDocument> carDocuments = carDao.findByMadeInCountryAndIdIn("germany", carIds);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With