Home > Enterprise >  Is there a spring for graphql alternative for netflix's @DGSDataloader and DGSData
Is there a spring for graphql alternative for netflix's @DGSDataloader and DGSData

Time:10-23

Trying to migrate a Netflix DGS GraphQL and Neo4J project to now use Spring for GraphQL and Neo4J instead. Hit a roadblock when I wanted to avoid the N 1 problem.

CodePudding user response:

Yes, there is an alternative to avoid the N 1 problem in Spring for GraphQL. It's the @BatchMapping annotation:

Suppose you have the following schema:

type Query {
    artistas: [Artista]
}

type Artista {
    id: ID
    apellido: String
    estilo: String
    obras:[Obra]
}

type Obra{
    artistaId: ID
    titulo: String
    imagen: String
}

And the following @QueryMapping:

@QueryMapping
Flux<Artista> artistas(){
    return Flux.fromIterable(allArtistas);
}

Our Artista DTO contains a List of Obra we may sometimes want, so it can cause us the N 1 problem:

record Artista (Long id, String apellido, String estilo, List<Obra> obras){}
record Obra (Long artistaId, String titulo, String imagen){}

So if you add an additional mapping method annotated with @BatchMapping, you tell the GraphQL engine to fetch that data using a DataLoader under the hood and keeping it at hand for each DB roundtrip, for example.

@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...
}

If you throw some logs here and there you can check it only fetches the Obras once.

Hope it helps!

  • Related