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.