varman
varman

Reputation: 8894

Find all embedded documents from manual reference in MongoDB

I use MongoDB and Spring Boot in a project. I used manual reference to point out a collection, My structure is as follows:

Reel collection

{
    _id : "reel_id_1",
    name: "reel 1",
    category :[
        {
            _id : "category_id_1",
            name: "category 1", 
            videos: ["video_id_1","video_id_2"]
        }
    ]
}

Video collection

{
    _id: "video_id_1",  // first document
    name: "mongo"
}

{
    _id: "video_id_2",  // seconddocument
    name: "java"
}

Java classes are

@Document
@Data
public class Reel {

    @Id
    private ObjectId _id;
    private String name;

    List<Category> category;
}

@Data
public class Category {

    @Id    
    private ObjectId _id=new ObjectId();
    private String name;

    Video videos;
}

@Document
@Data
public class Video {

    @Id
    private ObjectId _id = new ObjectId();
    private String name;
    
}

I tried to join both document via mongoTemplate

public List<Reel> findById(ObjectId _id) {
    LookupOperation lookupOperation = LookupOperation.newLookup()
            .from("video")
            .localField("category.videos")
            .foreignField("_id")
            .as("category.videos");


    UnwindOperation unwindOperation = Aggregation.unwind("category");
    Aggregation agg = newAggregation(unwindOperation,match(Criteria.where("_id").is(_id)),lookupOperation);


    Aggregation aggregation = newAggregation(lookupOperation);
    List<Reel> results = mongoTemplate.aggregate(aggregation, "reel", Reel.class).getMappedResults();
    return results;
}

But it throws an error.

Failed to instantiate java.util.List using constructor NO_CONSTRUCTOR with arguments

But since I use "unwind", I created a new Entity UnwindReel and add Category category instead of List<Category> category. And used

List<UnwindReel> results = mongoTemplate.aggregate(aggregation, "reel", UnwindReel.class).getMappedResults();

It combines only first video (video_id_1) object. How can I get all objects inside videos array? Is there any method to fetch?

Upvotes: 0

Views: 1559

Answers (1)

Valijon
Valijon

Reputation: 13113

Your JSON stored in database has wrong structure. Your Reel class expects list of Category, but in database you have stored as nested object.

You need to add this stage just after $lookup

{
  "$addFields": {
    "category": {
      "$map": {
        "input": "$category.videos",
        "in": {
          "videos": "$$this"
        }
      }
    }
  }
}

Java code

public List<Reel> findById(String _id) {

    Aggregation aggregation = Aggregation.newAggregation(
            Aggregation.match(Criteria.where("_id").is(_id)),
            Aggregation.lookup(mongoTemplate.getCollectionName(Video.class), "category.videos", "_id", "category.videos"),
            new AggregationOperation() {

                @Override
                public Document toDocument(AggregationOperationContext context) {
                    return new Document("$addFields",
                            new Document("category", new Document("$map", new Document("input", "$category.videos")
                                    .append("in", new Document("videos", "$$this")))));
                }
            })
        .withOptions(AggregationOptions.builder().allowDiskUse(Boolean.TRUE).build());

    LOG.debug(
            aggregation.toString().replaceAll("__collection__", mongoTemplate.getCollectionName(Reel.class)));

    return mongoTemplate.aggregate(aggregation, mongoTemplate.getCollectionName(Reel.class), Reel.class)
            .getMappedResults();

}

Recomendations

  1. Do not hard-code collection name, use better mongoTemplate.getCollectionName method
  2. Always log aggregation pipeline before performing, it helps debugging.
  3. If your collection will grow in the future, use {allowDiskUse: true} MongoDb aggregation option.

Upvotes: 1

Related Questions