1

I'm new to Spring-boot and MongoDB. In MongoDB a manual reference between two collections works fine. The mapping in Spring-boot seems not to work. I really don't know what else to check.Below all the relevant details, sorry for the long question.

The reason not to use DBref is because I might need the projections.

The "players" collection has this schema(any other not allowed)

{"_id":{"$oid":"5f56021d61738cc35de79438"},
"name":"Romeo",
"entryDate":{"$date":"2020-08-23T22:00:00.000Z"}}`

The "games" collection has the following schema

   {
    "_id":{"$oid":"5f5614a361738cc35de7943b"},
    "dices":{
       "value1":1,
       "value2":6
     },
    "gameScore":1,
    "player_id":{"$oid":"5f56021d61738cc35de79438"}
   }

The aggregation in MongoDB Compass

[{
    $match: {
        _id: ObjectId('5f56021d61738cc35de79438')
    }
}, {
    $lookup: {
        from: 'games',
        localField: '_id',
        foreignField: 'player_id',
        as: 'games'
    }
}]

yields

enter image description here

In Spring-boot the POJOs are:

@Document(collection = "players")
public class Player {
    @Id
    private String id;
    private String name;
    private LocalDate entryDate= LocalDate.now();
    private List<Game> game;
    
    public Player(){};

    public Player(String name) {
        this.name = name;
    }
//getters and setters for all properties, including game
}
@Document(collection = "games")
public class Game {
    @Id
    private String id;
    private Dices dices;
    private Integer gameScore;
    @Field(value = "player_id")
    private String playerId;

    public Game(){};

    public Game(Dices dices) {
        this.dices = dices;
    }
//getters and setters for all properties
}
public class Dices {

    private int value1;
    private int value2;

    public Dices(){}

    public Dices(int value1, int value2) {
        this.value1 = value1;
        this.value2 = value2;
    }
//getters and setters for both properties

In Postman

GET findAll players shows:

[{"id":"5f56021d61738cc35de79438","name":"Romeo","entryDate":[2020,8,24],"game":null},{"id":"5f5602e361738cc35de79439","name":"Julieta","entryDate":[2020,8,24],"game":null}, ....]

game is shown because I added also getters and setters for this property, just trying to find the way to properly mapping the games as manual references to players

GET findAll games:

[{"id":"5f5614a361738cc35de7943b","dices":{"value1":1,"value2":6},"gameScore":1,"playerId":"5f56021d61738cc35de79438"},

{"id":"5f5619f561738cc35de7943c","dices":{"value1":2,"value2":5},"gameScore":1,"playerId":"5f5602e361738cc35de79439"},

{"id":"5f561a5461738cc35de7943d","dices":{"value1":3,"value2":3},"gameScore":0,"playerId":"5f56021d61738cc35de79438"}, ...]

GET lh:8080/players/5f56021d61738cc35de79438/games yields an empty array, this is why I assume that the mapping between the collections in Spring-boot fails.

The GamesRepository

@Repository
public interface GameRepository extends MongoRepository<Game, String> {

    List<Game> findAll();

    List<Game> findGamesByPlayerId(String playerId);

}

The method in the service

    @Override
    public List<Game> findAllGamesByPlayerId(String playerId) {

        Optional<Player> playerDB= playerRepository.findById(playerId);

        if(playerDB.isPresent()) {
            return  gameRepository.findGamesByPlayerId(playerId);
        }

        else throw new ResourceNotFoundException("Player with id: "+playerId+" does not exist");
    }

and the GameController

    @GetMapping("/{ID}/games")
    public ResponseEntity<List<Game>> getAllGamesByPlayerId (@PathVariable("ID") String playerId){
        return ResponseEntity.ok()
                .body(gameService.findAllGamesByPlayerId(playerId));
    }

Tips are welcome!

kekamv
  • 46
  • 4

1 Answers1

1

Aggregations don't work with MongoRepostory unless you use @DBRef. But using @DBRef is not recommended. What you did in aggregation can be converted into Aggregation pipeline of Spring data. For that you need to autowired the MongoTemplate

@Autowired
MongoTemplate mongoTemplate;

Then you can convert the aggregation you have written. I haven't tested it, since your aggregation is working, this should work.

public List<Object> test(ObjectId id){

    Aggregation aggregation = Aggregation.newAggregation(
        match(Criteria.where("_id").is(id)),
        lookup("games","_id","player_id","games")
    ).withOptions(AggregationOptions.builder().allowDiskUse(Boolean.TRUE).build());

    return mongoTemplate.aggregate(aggregation, mongoTemplate.getCollectionName(Players.class), Object.class).getMappedResults();
}
varman
  • 8,704
  • 5
  • 19
  • 53
  • Sorry, I deleted my previous comment. If I understand correctly I must add the following beans in a configuration class: ```` public @Bean MongoClient mongoClient() { return MongoClients.create(connectionString); } public @Bean MongoTemplate mongoTemplate() { return new MongoTemplate((com.mongodb.MongoClient) mongoClient(), "dicesdb"); } ``` – kekamv Sep 11 '20 at 16:58
  • 1
    I dont use mongo client since mongoTemple includes it. What is your dependancy for mongodb – varman Sep 11 '20 at 17:01
  • 1
    Thanks @varman! you are right, that was my mistake, I was trying to autowire MongoTemplate in an interface ... :/ upsss . I found further steps in this question: https://stackoverflow.com/questions/38288258/spring-boot-with-mongotemplate – kekamv Sep 11 '20 at 17:09
  • @kekamv if this answer helps you, please consider to click on upvote and accept the answer which help people who seek this kind of prblm – varman Sep 21 '20 at 08:30