Попытка перенести проект Netflix DGS GraphQL и Neo4J, чтобы вместо этого использовать Spring для GraphQL и Neo4J. Наткнулся на блокпост, когда хотел избежать проблемы N+1.
Да, есть альтернатива, позволяющая избежать проблемы N+1 в Spring для GraphQL. Это аннотация @BatchMapping:
Предположим, у вас есть следующая схема:
type Query {
artistas: [Artista]
}
type Artista {
id: ID
apellido: String
estilo: String
obras:[Obra]
}
type Obra{
artistaId: ID
titulo: String
imagen: String
}
И следующий @QueryMapping:
@QueryMapping
Flux<Artista> artistas(){
return Flux.fromIterable(allArtistas);
}
Наш DTO Artista содержит список Obra, который нам иногда может понадобиться, поэтому это может вызвать у нас проблему N + 1:
record Artista (Long id, String apellido, String estilo, List<Obra> obras){}
record Obra (Long artistaId, String titulo, String imagen){}
Поэтому, если вы добавите дополнительный метод сопоставления, аннотированный с помощью @BatchMapping, вы скажете механизму GraphQL извлекать эти данные, используя DataLoader под капотом, и держать их под рукой, например, для каждого кругового обхода БД.
@BatchMapping(typeName = "Artista")
Mono<Map<Artista, List<Obra>>> obras(List<Artista> artistas){
var artistasIds = artistas.stream()
.map(Artista::id)
.toList();
var todasLasObras = obtenerObras(artistasIds);
return todasLasObras.collectList()
.map(obras -> {
Map<Long, List<Obra>> obrasDeCadaArtistaId = obras.stream()
.collect(Collectors.groupingBy(Obra::artistaId));
return artistas.stream()
.collect(Collectors.toMap(
unArtista -> unArtista, //K, el Artista
unArtista -> obrasDeCadaArtistaId.get(Long.parseLong(unArtista.id().toString())))); //V, la lista de obras
});
}
private Flux<Obra> obtenerObras(List<Long> artistasIds) {
// ...your service-specific way of getting all the Obras from each artistaId...
}
Если вы разбросаете несколько журналов здесь и там, вы можете проверить, что они получают Obras только один раз.
Надеюсь, это поможет!