Spring graphql Connection in a custom mongo reactive repository that uses Aggregation

76 views Asked by At

I am currently working on a sample project, and I am using the following stack

  • Spring boot 3.1.4
  • spring-boot-starter-data-mongodb-reactive
  • spring-boot-starter-graphql
  • spring-boot-starter-webflux
  • querydsl-apt
  • querydsl-mongodb

My main domain class is Pet,

@QueryEntity
@Document
public record Pet (
        @Id String id,
        String species,
        String breed,
        String name,
        String color,
        String size,
        List<Asset> media){}

and I have created a repository interface to persist PetDocuments

@GraphQlRepository
public interface PetsRepository extends ReactiveCrudRepository<Pet,String>, ReactiveQuerydslPredicateExecutor<Pet>, PetRepositoryCustom {}

I have created a pets.graphqls file

type Query{
    pets: PetConnection
}
type Pet{
    id : ID
    species: String
    breed: String
    name: String
    color: String
    size: String
    media: [MediaAsset]
}

type MediaAsset{
    key: String
    url: String
    mediaType: MediaType
    renditions: [Rendition]
}

type Rendition{
    key: String
    url: String
    mediaType: MediaType
}

enum MediaType{
    ORIGINAL
    THUMBNAIL
    PREVIEW
}

with this, I am able to query pets and I am able to use pagination

Graphql Request

{
  pets{    
    edges{
      node{
        name
      }
    },
    pageInfo{
      hasPreviousPage,
      hasNextPage,
      startCursor,
      endCursor
    }
    
  }  
}

GraphqlResponse

{
  "data": {
    "pets": {
      "edges": [
        {
          "node": {
            "name": "moka"
          }
        },
        {
          "node": {
            "name": "mamba"
          }
        },
        {
          "node": {
            "name": "negra"
          }
        },
        {
          "node": {
            "name": "wero"
          }
        },
        {
          "node": {
            "name": "jules"
          }
        },
        {
          "node": {
            "name": "mona"
          }
        },
        {
          "node": {
            "name": "princesa "
          }
        }
      ],
      "pageInfo": {
        "hasPreviousPage": true,
        "hasNextPage": false,
        "startCursor": "T18x",
        "endCursor": "T183"
      }
    }
  }
}

that works well, but I am using Atlas mongodb as my database, and I have implemented text search, and as documented here the $search aggregation stage is not supported by the aggregation framework, but can work by implementing AggregationOperation

I have implemented the interface like this

import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
import org.bson.Document;

public class TextSearchAggregationOperation implements AggregationOperation {
    private final String index;
    private final String query;
    private final String path;

    public TextSearchAggregationOperation(String index, String query, String path) {
        this.index = index;
        this.query = query;
        this.path = path;
    }

    @Override
    public Document toDocument(AggregationOperationContext context) {
        // Construct the $search stage as a BSON document
        Document searchStage = new Document();
        searchStage.put("$search", new Document()
                .append("index", index)
                .append("text", new Document()
                        .append("query", query)
                        .append("path", new Document()
                                .append("wildcard", path)
                        )
                ));
        return searchStage;
    }
}

And then I have also created a custom repository, an in the implementation I have a method like this

@Override
    public Flux<Pet> performTextSearch(SearchRequest searchRequest, Pageable pageable) {
        Criteria matchCriteria = new Criteria();
        searchRequest.fields().forEach(field -> matchCriteria.and(field.name()).in(field.values()));
        MatchOperation matchOperation = Aggregation.match(matchCriteria);

        TextSearchAggregationOperation textSearchStage = new TextSearchAggregationOperation(INDEX, searchRequest.query(), PATH);
        Aggregation aggregation = Aggregation.newAggregation(
                textSearchStage, matchOperation,
                Aggregation.skip(pageable.getPageNumber() * pageable.getPageSize()),
                Aggregation.limit(pageable.getPageSize())
        );

        return mongoTemplate.aggregate(aggregation, "pet", Pet.class);
    }

this allows Me to perform a text search in atlas mongodb, and let me filter results using the MatchOperation,

I was able to implement offset pagination, by sending Pageable and sending the parameters in the request, however I would like to implement the cursor based pagination, so I am kind of lost on this,

any help would be greatly appreciated, thanks in advance :)

Implemented pagination on Graphql using the aggregation framework, but would like to implement cursor based pagination so I am able to paginate results from a text search in atlas mongodb, I am using webflux.

0

There are 0 answers