Merge pull request #5567 from loicmathieu/feat/mongo-panache-rx

Reactive MongoDB with Panache
This commit is contained in:
George Gastaldi
2020-02-14 11:17:49 -03:00
committed by GitHub
38 changed files with 3987 additions and 33 deletions

View File

@@ -321,6 +321,11 @@
<artifactId>quarkus-panache-common-deployment</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-mongodb-panache-deployment</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-mongodb-client-deployment</artifactId>

View File

@@ -582,6 +582,109 @@ data class Person (
): PanacheMongoEntity()
----
== Reactive Entities and Repositories
MongoDB with Panache allows using reactive style implementation for both entities and repositories.
For this, you need to use the Reactive variants when defining your entities : `ReactivePanacheMongoEntity` or `ReactivePanacheMongoEntityBase`,
and when defining your repositories: `ReactivePanacheMongoRepository` or `ReactivePanacheMongoRepositoryBase`.
The reactive variant of the `Person` class will be:
[source,java]
----
public class ReactivePerson extends ReactivePanacheMongoEntity {
public String name;
public LocalDate birth;
public Status status;
// return name as uppercase in the model
public String getName(){
return name.toUpperCase();
}
// store all names in lowercase in the DB
public void setName(String name){
this.name = name.toLowerCase();
}
}
----
You will have access to the same functionalities of the _imperative_ variant inside the reactive one: bson annotations, custom ID, PanacheQL, ...
But the methods on your entities or repositories will all return reactive types.
See the equivalent methods from the imperative example with the reactive variant:
[source,java]
----
// creating a person
ReactivePerson person = new ReactivePerson();
person.name = "Loïc";
person.birth = LocalDate.of(1910, Month.FEBRUARY, 1);
person.status = Status.Alive;
// persist it
CompletionStage<Void> cs1 = person.persist();
person.status = Status.Dead;
// Your must call update() in order to send your entity modifications to MongoDB
CompletionStage<Void> cs2 = person.update();
// delete it
CompletionStage<Void> cs3 = person.delete();
// getting a list of all persons
CompletionStage<List<ReactivePerson>> allPersons = ReactivePerson.listAll();
// finding a specific person by ID
CompletionStage<ReactivePerson> personById = ReactivePerson.findById(personId);
// finding a specific person by ID via an Optional
CompletionStage<Optional<ReactivePerson>> optional = ReactivePerson.findByIdOptional(personId);
personById = optional.thenApply(o -> o.orElseThrow(() -> new NotFoundException()));
// finding all living persons
CompletionStage<List<ReactivePerson>> livingPersons = ReactivePerson.list("status", Status.Alive);
// counting all persons
CompletionStage<Long> countAll = ReactivePerson.count();
// counting all living persons
CompletionStage<Long> countAlive = ReactivePerson.count("status", Status.Alive);
// delete all living persons
CompletionStage<Long> deleteCount = ReactivePerson.delete("status", Status.Alive);
// delete all persons
deleteCount = ReactivePerson.deleteAll();
----
TIP: If you use MongoDB with Panache in conjunction with RESTEasy, you can directly return a reactive type inside your JAX-RS resource endpoint as RESTEasy will handle it correctly.
The same query facility exists for the reactive types, but the `stream()` methods act differently: they return a reactive stream `Publisher` instead of a `Stream`.
It allows more advanced reactive use cases, for example you can use it to send server-sent events (SSE) via RESTEasy:
[source,java]
----
import org.jboss.resteasy.annotations.SseElementType;
import org.reactivestreams.Publisher;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
@GET
@Path("/stream")
@Produces(MediaType.SERVER_SENT_EVENTS)
@SseElementType(MediaType.APPLICATION_JSON)
public Publisher<ReactivePerson> streamPersons() {
return ReactivePerson.streamAll();
}
----
TIP: `@SseElementType(MediaType.APPLICATION_JSON)` tells RESTEasy to serialize the object in JSON.
== How and why we simplify MongoDB API
When it comes to writing MongoDB entities, there are a number of annoying things that users have grown used to

View File

@@ -4,6 +4,7 @@ import java.util.List;
import java.util.concurrent.CompletionStage;
import org.bson.Document;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.conversions.Bson;
import org.eclipse.microprofile.reactive.streams.operators.PublisherBuilder;
@@ -30,6 +31,13 @@ public interface ReactiveMongoCollection<T> {
*/
MongoNamespace getNamespace();
/**
* Gets the codec registry of this collection.
*
* @return the codec registry
*/
CodecRegistry getCodecRegistry();
/**
* Get the class of documents stored in this collection.
*

View File

@@ -4,6 +4,7 @@ import java.util.List;
import java.util.concurrent.CompletionStage;
import org.bson.Document;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.conversions.Bson;
import org.eclipse.microprofile.reactive.streams.operators.PublisherBuilder;
import org.eclipse.microprofile.reactive.streams.operators.ReactiveStreams;
@@ -59,6 +60,11 @@ public class ReactiveMongoCollectionImpl<T> implements ReactiveMongoCollection<T
return collection.getNamespace();
}
@Override
public CodecRegistry getCodecRegistry() {
return collection.getCodecRegistry();
}
@Override
public Class<T> getDocumentClass() {
return collection.getDocumentClass();

View File

@@ -71,11 +71,6 @@ public class PanacheMongoEntityEnhancer extends PanacheEntityEnhancer<MetamodelI
protected void generateAccessorGetField(MethodVisitor mv, EntityField field) {
mv.visitFieldInsn(Opcodes.GETFIELD, thisClass.getInternalName(), field.name, field.descriptor);
}
@Override
public void visitEnd() {
super.visitEnd();
}
}
public void collectFields(ClassInfo classInfo) {

View File

@@ -45,11 +45,16 @@ import io.quarkus.mongodb.panache.PanacheMongoRecorder;
import io.quarkus.mongodb.panache.PanacheMongoRepository;
import io.quarkus.mongodb.panache.PanacheMongoRepositoryBase;
import io.quarkus.mongodb.panache.ProjectionFor;
import io.quarkus.mongodb.panache.axle.ReactivePanacheMongoEntity;
import io.quarkus.mongodb.panache.axle.ReactivePanacheMongoEntityBase;
import io.quarkus.mongodb.panache.axle.ReactivePanacheMongoRepository;
import io.quarkus.mongodb.panache.axle.ReactivePanacheMongoRepositoryBase;
import io.quarkus.panache.common.deployment.PanacheEntityClassesBuildItem;
import io.quarkus.panache.common.deployment.PanacheFieldAccessEnhancer;
import io.quarkus.panache.common.deployment.PanacheRepositoryEnhancer;
public class PanacheResourceProcessor {
// blocking types
static final DotName DOTNAME_PANACHE_REPOSITORY_BASE = DotName.createSimple(PanacheMongoRepositoryBase.class.getName());
private static final DotName DOTNAME_PANACHE_REPOSITORY = DotName.createSimple(PanacheMongoRepository.class.getName());
static final DotName DOTNAME_PANACHE_ENTITY_BASE = DotName.createSimple(PanacheMongoEntityBase.class.getName());
@@ -58,6 +63,15 @@ public class PanacheResourceProcessor {
private static final DotName DOTNAME_PROJECTION_FOR = DotName.createSimple(ProjectionFor.class.getName());
private static final DotName DOTNAME_BSON_PROPERTY = DotName.createSimple(BsonProperty.class.getName());
// reactive types: Axle
static final DotName DOTNAME_AXLE_PANACHE_REPOSITORY_BASE = DotName
.createSimple(ReactivePanacheMongoRepositoryBase.class.getName());
private static final DotName DOTNAME_AXLE_PANACHE_REPOSITORY = DotName
.createSimple(ReactivePanacheMongoRepository.class.getName());
static final DotName DOTNAME_AXLE_PANACHE_ENTITY_BASE = DotName
.createSimple(ReactivePanacheMongoEntityBase.class.getName());
private static final DotName DOTNAME_AXLE_PANACHE_ENTITY = DotName.createSimple(ReactivePanacheMongoEntity.class.getName());
private static final DotName DOTNAME_OBJECT_ID = DotName.createSimple(ObjectId.class.getName());
private static final DotName DOTNAME_OBJECT = DotName.createSimple(Object.class.getName());
@@ -284,4 +298,58 @@ public class PanacheResourceProcessor {
extractMappings(classPropertyMapping, superClass, index);
}
}
@BuildStep
void buildAxle(CombinedIndexBuildItem index,
ApplicationIndexBuildItem applicationIndex,
BuildProducer<BytecodeTransformerBuildItem> transformers) throws Exception {
ReactivePanacheMongoRepositoryEnhancer daoEnhancer = new ReactivePanacheMongoRepositoryEnhancer(index.getIndex());
Set<String> daoClasses = new HashSet<>();
for (ClassInfo classInfo : index.getIndex().getAllKnownImplementors(DOTNAME_AXLE_PANACHE_REPOSITORY_BASE)) {
// Skip PanacheRepository
if (classInfo.name().equals(DOTNAME_AXLE_PANACHE_REPOSITORY))
continue;
if (PanacheRepositoryEnhancer.skipRepository(classInfo))
continue;
daoClasses.add(classInfo.name().toString());
}
for (ClassInfo classInfo : index.getIndex().getAllKnownImplementors(DOTNAME_AXLE_PANACHE_REPOSITORY)) {
if (PanacheRepositoryEnhancer.skipRepository(classInfo))
continue;
daoClasses.add(classInfo.name().toString());
}
for (String daoClass : daoClasses) {
transformers.produce(new BytecodeTransformerBuildItem(daoClass, daoEnhancer));
}
ReactivePanacheMongoEntityEnhancer modelEnhancer = new ReactivePanacheMongoEntityEnhancer(index.getIndex());
Set<String> modelClasses = new HashSet<>();
// Note that we do this in two passes because for some reason Jandex does not give us subtypes
// of PanacheMongoEntity if we ask for subtypes of PanacheMongoEntityBase
for (ClassInfo classInfo : index.getIndex().getAllKnownSubclasses(DOTNAME_AXLE_PANACHE_ENTITY_BASE)) {
if (classInfo.name().equals(DOTNAME_AXLE_PANACHE_ENTITY))
continue;
if (modelClasses.add(classInfo.name().toString()))
modelEnhancer.collectFields(classInfo);
}
for (ClassInfo classInfo : index.getIndex().getAllKnownSubclasses(DOTNAME_AXLE_PANACHE_ENTITY)) {
if (modelClasses.add(classInfo.name().toString()))
modelEnhancer.collectFields(classInfo);
}
for (String modelClass : modelClasses) {
transformers.produce(new BytecodeTransformerBuildItem(modelClass, modelEnhancer));
}
if (!modelEnhancer.entities.isEmpty()) {
PanacheFieldAccessEnhancer panacheFieldAccessEnhancer = new PanacheFieldAccessEnhancer(
modelEnhancer.getModelInfo());
for (ClassInfo classInfo : applicationIndex.getIndex().getKnownClasses()) {
String className = classInfo.name().toString();
if (!modelClasses.contains(className)) {
transformers.produce(new BytecodeTransformerBuildItem(className, panacheFieldAccessEnhancer));
}
}
}
}
}

View File

@@ -0,0 +1,86 @@
package io.quarkus.mongodb.panache.deployment;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import org.bson.codecs.pojo.annotations.BsonIgnore;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.FieldInfo;
import org.jboss.jandex.IndexView;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import io.quarkus.gizmo.DescriptorUtils;
import io.quarkus.mongodb.panache.axle.runtime.ReactiveMongoOperations;
import io.quarkus.panache.common.deployment.EntityField;
import io.quarkus.panache.common.deployment.EntityModel;
import io.quarkus.panache.common.deployment.MetamodelInfo;
import io.quarkus.panache.common.deployment.PanacheEntityEnhancer;
public class ReactivePanacheMongoEntityEnhancer extends PanacheEntityEnhancer<MetamodelInfo<EntityModel<EntityField>>> {
public final static String MONGO_OPERATIONS_NAME = ReactiveMongoOperations.class.getName();
public final static String MONGO_OPERATIONS_BINARY_NAME = MONGO_OPERATIONS_NAME.replace('.', '/');
private static final DotName DOTNAME_BSON_IGNORE = DotName.createSimple(BsonIgnore.class.getName());
final Map<String, EntityModel> entities = new HashMap<>();
public ReactivePanacheMongoEntityEnhancer(IndexView index) {
super(index, PanacheResourceProcessor.DOTNAME_AXLE_PANACHE_ENTITY_BASE);
modelInfo = new MetamodelInfo<>();
}
@Override
public ClassVisitor apply(String className, ClassVisitor outputClassVisitor) {
return new PanacheMongoEntityClassVisitor(className, outputClassVisitor, modelInfo, panacheEntityBaseClassInfo,
indexView.getClassByName(DotName.createSimple(className)));
}
static class PanacheMongoEntityClassVisitor extends PanacheEntityClassVisitor<EntityField> {
public PanacheMongoEntityClassVisitor(String className, ClassVisitor outputClassVisitor,
MetamodelInfo<EntityModel<EntityField>> modelInfo, ClassInfo panacheEntityBaseClassInfo,
ClassInfo entityInfo) {
super(className, outputClassVisitor, modelInfo, panacheEntityBaseClassInfo, entityInfo);
}
@Override
protected void injectModel(MethodVisitor mv) {
mv.visitLdcInsn(thisClass);
}
@Override
protected String getModelDescriptor() {
return "Ljava/lang/Class;";
}
@Override
protected String getPanacheOperationsBinaryName() {
return MONGO_OPERATIONS_BINARY_NAME;
}
@Override
protected void generateAccessorSetField(MethodVisitor mv, EntityField field) {
mv.visitFieldInsn(Opcodes.PUTFIELD, thisClass.getInternalName(), field.name, field.descriptor);
}
@Override
protected void generateAccessorGetField(MethodVisitor mv, EntityField field) {
mv.visitFieldInsn(Opcodes.GETFIELD, thisClass.getInternalName(), field.name, field.descriptor);
}
}
public void collectFields(ClassInfo classInfo) {
EntityModel<EntityField> entityModel = new EntityModel<>(classInfo);
for (FieldInfo fieldInfo : classInfo.fields()) {
String name = fieldInfo.name();
if (Modifier.isPublic(fieldInfo.flags()) && !fieldInfo.hasAnnotation(DOTNAME_BSON_IGNORE)) {
entityModel.addField(new EntityField(name, DescriptorUtils.typeToString(fieldInfo.type())));
}
}
modelInfo.addEntityModel(entityModel);
}
}

View File

@@ -0,0 +1,62 @@
package io.quarkus.mongodb.panache.deployment;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.IndexView;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import io.quarkus.mongodb.panache.axle.ReactivePanacheMongoRepository;
import io.quarkus.mongodb.panache.axle.ReactivePanacheMongoRepositoryBase;
import io.quarkus.panache.common.deployment.PanacheRepositoryEnhancer;
public class ReactivePanacheMongoRepositoryEnhancer extends PanacheRepositoryEnhancer {
public final static DotName PANACHE_REPOSITORY_BASE_NAME = DotName
.createSimple(ReactivePanacheMongoRepositoryBase.class.getName());
public final static DotName PANACHE_REPOSITORY_NAME = DotName.createSimple(ReactivePanacheMongoRepository.class.getName());
public ReactivePanacheMongoRepositoryEnhancer(IndexView index) {
super(index, PanacheResourceProcessor.DOTNAME_AXLE_PANACHE_REPOSITORY_BASE);
}
@Override
public ClassVisitor apply(String className, ClassVisitor outputClassVisitor) {
return new PanacheMongoRepositoryClassVisitor(className, outputClassVisitor, panacheRepositoryBaseClassInfo,
this.indexView);
}
static class PanacheMongoRepositoryClassVisitor extends PanacheRepositoryClassVisitor {
public PanacheMongoRepositoryClassVisitor(String className, ClassVisitor outputClassVisitor,
ClassInfo panacheRepositoryBaseClassInfo, IndexView indexView) {
super(className, outputClassVisitor, panacheRepositoryBaseClassInfo, indexView);
}
@Override
protected DotName getPanacheRepositoryDotName() {
return PANACHE_REPOSITORY_NAME;
}
@Override
protected DotName getPanacheRepositoryBaseDotName() {
return PANACHE_REPOSITORY_BASE_NAME;
}
@Override
protected String getPanacheOperationsBinaryName() {
return ReactivePanacheMongoEntityEnhancer.MONGO_OPERATIONS_BINARY_NAME;
}
@Override
protected void injectModel(MethodVisitor mv) {
// inject Class
mv.visitLdcInsn(entityType);
}
@Override
protected String getModelDescriptor() {
return "Ljava/lang/Class;";
}
}
}

View File

@@ -24,11 +24,11 @@
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-panacheql</artifactId>
<artifactId>quarkus-mongodb-client</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-mongodb-client</artifactId>
<artifactId>quarkus-panacheql</artifactId>
</dependency>
<!-- This is optional to avoid pulling libraries if jax-rs/jsonb is not on the path-->
<!-- But needed to provides default serializer/deserializer for JsonB -->
@@ -74,19 +74,6 @@
</goals>
</execution>
</executions>
<configuration>
<fileSets>
<fileSet>
<directory>${project.build.directory}/classes</directory>
<excludes>
<!-- We avoid indexing it as it needs to be included in the apps only if jaxrs and jsonb are. -->
<exclude>**/PanacheMongoJsonbContextResolver.class</exclude>
<!-- We avoid indexing it as it needs to be included in the apps only if jackson is. -->
<exclude>**/ObjectMapperProducer.class</exclude>
</excludes>
</fileSet>
</fileSets>
</configuration>
</plugin>
</plugins>
</build>

View File

@@ -0,0 +1,30 @@
package io.quarkus.mongodb.panache.axle;
import org.bson.types.ObjectId;
/**
* Represents an entity with a generated ID field {@link #id} of type {@link ObjectId}. If your
* Mongo entities extend this class they gain the ID field and auto-generated accessors
* to all their public fields, as well as all the useful methods from {@link ReactivePanacheMongoEntityBase}.
*
* If you want a custom ID type or strategy, you can directly extend {@link ReactivePanacheMongoEntityBase}
* instead, and write your own ID field. You will still get auto-generated accessors and
* all the useful methods.
*
* @see ReactivePanacheMongoEntityBase
*/
public abstract class ReactivePanacheMongoEntity extends ReactivePanacheMongoEntityBase {
/**
* The auto-generated ID field.
* This field is set by Mongo when this entity is persisted.
*
* @see #persist()
*/
public ObjectId id;
@Override
public String toString() {
return this.getClass().getSimpleName() + "<" + id + ">";
}
}

View File

@@ -0,0 +1,892 @@
package io.quarkus.mongodb.panache.axle;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletionStage;
import java.util.stream.Stream;
import org.bson.Document;
import org.reactivestreams.Publisher;
import io.quarkus.mongodb.ReactiveMongoCollection;
import io.quarkus.mongodb.ReactiveMongoDatabase;
import io.quarkus.mongodb.panache.axle.runtime.ReactiveMongoOperations;
import io.quarkus.panache.common.Parameters;
import io.quarkus.panache.common.Sort;
import io.quarkus.panache.common.impl.GenerateBridge;
/**
* Represents an entity. If your Mongo entities extend this class they gain auto-generated accessors
* to all their public fields, as well as a lot of useful
* methods. Unless you have a custom ID strategy, you should not extend this class directly but extend
* {@link ReactivePanacheMongoEntity} instead.
*
* @see ReactivePanacheMongoEntity
*/
public abstract class ReactivePanacheMongoEntityBase {
// Operations
/**
* Persist this entity in the database.
* This will set it's ID field if not already set.
*
* @see #persist(Iterable)
* @see #persist(Stream)
* @see #persist(Object, Object...)
*/
public CompletionStage<Void> persist() {
return ReactiveMongoOperations.persist(this);
}
/**
* Update this entity in the database.
*
* @see #update(Iterable)
* @see #update(Stream)
* @see #update(Object, Object...)
*/
public CompletionStage<Void> update() {
return ReactiveMongoOperations.update(this);
}
/**
* Persist this entity in the database or update it if it already exist.
*
* @see #persistOrUpdate(Iterable)
* @see #persistOrUpdate(Stream)
* @see #persistOrUpdate(Object, Object...)
*/
public CompletionStage<Void> persistOrUpdate() {
return ReactiveMongoOperations.persistOrUpdate(this);
}
/**
* Delete this entity from the database, if it is already persisted.
*
* @see #delete(String, Object...)
* @see #delete(String, Map)
* @see #delete(String, Parameters)
* @see #deleteAll()
*/
public CompletionStage<Void> delete() {
return ReactiveMongoOperations.delete(this);
}
// Queries
/**
* Find an entity of this type by ID.
*
* @param id the ID of the entity to find.
* @return the entity found, or <code>null</code> if not found.
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> CompletionStage<T> findById(Object id) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find an entity of this type by ID.
*
* @param id the ID of the entity to find.
* @return if found, an optional containing the entity, else <code>Optional.empty()</code>.
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> CompletionStage<Optional<T>> findByIdOptional(Object id) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities using a query, with optional indexed parameters.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params optional sequence of indexed parameters
* @return a new {@link ReactivePanacheQuery} instance for the given query
* @see #find(String, Sort, Object...)
* @see #find(String, Map)
* @see #find(String, Parameters)
* @see #list(String, Object...)
* @see #stream(String, Object...)
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> ReactivePanacheQuery<T> find(String query, Object... params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities using a query and the given sort options, with optional indexed parameters.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param sort the sort strategy to use
* @param params optional sequence of indexed parameters
* @return a new {@link ReactivePanacheQuery} instance for the given query
* @see #find(String, Object...)
* @see #find(String, Sort, Map)
* @see #find(String, Sort, Parameters)
* @see #list(String, Sort, Object...)
* @see #stream(String, Sort, Object...)
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> ReactivePanacheQuery<T> find(String query, Sort sort,
Object... params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities using a query, with named parameters.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params {@link Map} of named parameters
* @return a new {@link ReactivePanacheQuery} instance for the given query
* @see #find(String, Sort, Map)
* @see #find(String, Object...)
* @see #find(String, Parameters)
* @see #list(String, Map)
* @see #stream(String, Map)
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> ReactivePanacheQuery<T> find(String query,
Map<String, Object> params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities using a query and the given sort options, with named parameters.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param sort the sort strategy to use
* @param params {@link Map} of indexed parameters
* @return a new {@link ReactivePanacheQuery} instance for the given query
* @see #find(String, Map)
* @see #find(String, Sort, Object...)
* @see #find(String, Sort, Parameters)
* @see #list(String, Sort, Map)
* @see #stream(String, Sort, Map)
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> ReactivePanacheQuery<T> find(String query, Sort sort,
Map<String, Object> params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities using a query, with named parameters.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params {@link Parameters} of named parameters
* @return a new {@link ReactivePanacheQuery} instance for the given query
* @see #find(String, Sort, Parameters)
* @see #find(String, Map)
* @see #find(String, Parameters)
* @see #list(String, Parameters)
* @see #stream(String, Parameters)
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> ReactivePanacheQuery<T> find(String query, Parameters params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities using a query and the given sort options, with named parameters.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param sort the sort strategy to use
* @param params {@link Parameters} of indexed parameters
* @return a new {@link ReactivePanacheQuery} instance for the given query
* @see #find(String, Parameters)
* @see #find(String, Sort, Map)
* @see #find(String, Sort, Parameters)
* @see #list(String, Sort, Parameters)
* @see #stream(String, Sort, Parameters)
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> ReactivePanacheQuery<T> find(String query, Sort sort,
Parameters params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities using a BSON query.
*
* @param query a {@link Document} query
* @return a new {@link ReactivePanacheQuery} instance for the given query
* @see #find(String, Parameters)
* @see #find(String, Sort, Map)
* @see #find(String, Sort, Parameters)
* @see #list(String, Sort, Parameters)
* @see #stream(String, Sort, Parameters)
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> ReactivePanacheQuery<T> find(Document query) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities using a a BSON query and a BSON sort.
*
* @param query a {@link Document} query
* @param sort the {@link Document} sort
* @return a new {@link ReactivePanacheQuery} instance for the given query
* @see #find(String, Parameters)
* @see #find(String, Sort, Map)
* @see #find(String, Sort, Parameters)
* @see #list(String, Sort, Parameters)
* @see #stream(String, Sort, Parameters)
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> ReactivePanacheQuery<T> find(Document query, Document sort) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find all entities of this type.
*
* @return a new {@link ReactivePanacheQuery} instance to find all entities of this type.
* @see #findAll(Sort)
* @see #listAll()
* @see #streamAll()
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> ReactivePanacheQuery<T> findAll() {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find all entities of this type, in the given order.
*
* @param sort the sort order to use
* @return a new {@link ReactivePanacheQuery} instance to find all entities of this type.
* @see #findAll()
* @see #listAll(Sort)
* @see #streamAll(Sort)
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> ReactivePanacheQuery<T> findAll(Sort sort) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities matching a query, with optional indexed parameters.
* This method is a shortcut for <code>find(query, params).list()</code>.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params optional sequence of indexed parameters
* @return a {@link List} containing all results, without paging
* @see #list(String, Sort, Object...)
* @see #list(String, Map)
* @see #list(String, Parameters)
* @see #find(String, Object...)
* @see #stream(String, Object...)
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> CompletionStage<List<T>> list(String query, Object... params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities matching a query and the given sort options, with optional indexed parameters.
* This method is a shortcut for <code>find(query, sort, params).list()</code>.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param sort the sort strategy to use
* @param params optional sequence of indexed parameters
* @return a {@link List} containing all results, without paging
* @see #list(String, Object...)
* @see #list(String, Sort, Map)
* @see #list(String, Sort, Parameters)
* @see #find(String, Sort, Object...)
* @see #stream(String, Sort, Object...)
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> CompletionStage<List<T>> list(String query, Sort sort,
Object... params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities matching a query, with named parameters.
* This method is a shortcut for <code>find(query, params).list()</code>.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params {@link Map} of named parameters
* @return a {@link List} containing all results, without paging
* @see #list(String, Sort, Map)
* @see #list(String, Object...)
* @see #list(String, Parameters)
* @see #find(String, Map)
* @see #stream(String, Map)
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> CompletionStage<List<T>> list(String query,
Map<String, Object> params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities matching a query and the given sort options, with named parameters.
* This method is a shortcut for <code>find(query, sort, params).list()</code>.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param sort the sort strategy to use
* @param params {@link Map} of indexed parameters
* @return a {@link List} containing all results, without paging
* @see #list(String, Map)
* @see #list(String, Sort, Object...)
* @see #list(String, Sort, Parameters)
* @see #find(String, Sort, Map)
* @see #stream(String, Sort, Map)
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> CompletionStage<List<T>> list(String query, Sort sort,
Map<String, Object> params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities matching a query, with named parameters.
* This method is a shortcut for <code>find(query, params).list()</code>.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params {@link Parameters} of named parameters
* @return a {@link List} containing all results, without paging
* @see #list(String, Sort, Parameters)
* @see #list(String, Object...)
* @see #list(String, Map)
* @see #find(String, Parameters)
* @see #stream(String, Parameters)
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> CompletionStage<List<T>> list(String query, Parameters params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities matching a query and the given sort options, with named parameters.
* This method is a shortcut for <code>find(query, sort, params).list()</code>.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param sort the sort strategy to use
* @param params {@link Parameters} of indexed parameters
* @return a {@link List} containing all results, without paging
* @see #list(String, Parameters)
* @see #list(String, Sort, Object...)
* @see #list(String, Sort, Map)
* @see #find(String, Sort, Parameters)
* @see #stream(String, Sort, Parameters)
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> CompletionStage<List<T>> list(String query, Sort sort,
Parameters params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities using a BSON query.
* This method is a shortcut for <code>find(query).list()</code>.
*
* @param query a {@link Document} query
* @return a new {@link ReactivePanacheQuery} instance for the given query
* @see #find(String, Parameters)
* @see #find(String, Sort, Map)
* @see #find(String, Sort, Parameters)
* @see #list(String, Sort, Parameters)
* @see #stream(String, Sort, Parameters)
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> CompletionStage<List<T>> list(Document query) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities using a a BSON query and a BSON sort.
* This method is a shortcut for <code>find(query, sort).list()</code>.
*
* @param query a {@link Document} query
* @param sort the {@link Document} sort
* @return a new {@link ReactivePanacheQuery} instance for the given query
* @see #find(String, Parameters)
* @see #find(String, Sort, Map)
* @see #find(String, Sort, Parameters)
* @see #list(String, Sort, Parameters)
* @see #stream(String, Sort, Parameters)
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> CompletionStage<List<T>> list(Document query, Document sort) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find all entities of this type.
* This method is a shortcut for <code>findAll().list()</code>.
*
* @return a {@link List} containing all results, without paging
* @see #listAll(Sort)
* @see #findAll()
* @see #streamAll()
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> CompletionStage<List<T>> listAll() {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find all entities of this type, in the given order.
* This method is a shortcut for <code>findAll(sort).list()</code>.
*
* @param sort the sort order to use
* @return a {@link List} containing all results, without paging
* @see #listAll()
* @see #findAll(Sort)
* @see #streamAll(Sort)
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> CompletionStage<List<T>> listAll(Sort sort) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities matching a query, with optional indexed parameters.
* This method is a shortcut for <code>find(query, params).stream()</code>.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params optional sequence of indexed parameters
* @return a {@link Stream} containing all results, without paging
* @see #stream(String, Sort, Object...)
* @see #stream(String, Map)
* @see #stream(String, Parameters)
* @see #find(String, Object...)
* @see #list(String, Object...)
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> Publisher<T> stream(String query, Object... params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities matching a query and the given sort options, with optional indexed parameters.
* This method is a shortcut for <code>find(query, sort, params).stream()</code>.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param sort the sort strategy to use
* @param params optional sequence of indexed parameters
* @return a {@link Stream} containing all results, without paging
* @see #stream(String, Object...)
* @see #stream(String, Sort, Map)
* @see #stream(String, Sort, Parameters)
* @see #find(String, Sort, Object...)
* @see #list(String, Sort, Object...)
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> Publisher<T> stream(String query, Sort sort, Object... params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities matching a query, with named parameters.
* This method is a shortcut for <code>find(query, params).stream()</code>.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params {@link Map} of named parameters
* @return a {@link Stream} containing all results, without paging
* @see #stream(String, Sort, Map)
* @see #stream(String, Object...)
* @see #stream(String, Parameters)
* @see #find(String, Map)
* @see #list(String, Map)
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> Publisher<T> stream(String query, Map<String, Object> params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities matching a query and the given sort options, with named parameters.
* This method is a shortcut for <code>find(query, sort, params).stream()</code>.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param sort the sort strategy to use
* @param params {@link Map} of indexed parameters
* @return a {@link Stream} containing all results, without paging
* @see #stream(String, Map)
* @see #stream(String, Sort, Object...)
* @see #stream(String, Sort, Parameters)
* @see #find(String, Sort, Map)
* @see #list(String, Sort, Map)
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> Publisher<T> stream(String query, Sort sort,
Map<String, Object> params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities matching a query, with named parameters.
* This method is a shortcut for <code>find(query, params).stream()</code>.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params {@link Parameters} of named parameters
* @return a {@link Stream} containing all results, without paging
* @see #stream(String, Sort, Parameters)
* @see #stream(String, Object...)
* @see #stream(String, Map)
* @see #find(String, Parameters)
* @see #list(String, Parameters)
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> Publisher<T> stream(String query, Parameters params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities matching a query and the given sort options, with named parameters.
* This method is a shortcut for <code>find(query, sort, params).stream()</code>.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param sort the sort strategy to use
* @param params {@link Parameters} of indexed parameters
* @return a {@link Stream} containing all results, without paging
* @see #stream(String, Parameters)
* @see #stream(String, Sort, Object...)
* @see #stream(String, Sort, Map)
* @see #find(String, Sort, Parameters)
* @see #list(String, Sort, Parameters)
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> Publisher<T> stream(String query, Sort sort, Parameters params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities using a BSON query.
* This method is a shortcut for <code>find(query).stream()</code>.
*
* @param query a {@link Document} query
* @return a new {@link ReactivePanacheQuery} instance for the given query
* @see #find(String, Parameters)
* @see #find(String, Sort, Map)
* @see #find(String, Sort, Parameters)
* @see #list(String, Sort, Parameters)
* @see #stream(String, Sort, Parameters)
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> Publisher<T> stream(Document query) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities using a a BSON query and a BSON sort.
* This method is a shortcut for <code>find(query, sort).stream()</code>.
*
* @param query a {@link Document} query
* @param sort the {@link Document} sort
* @return a new {@link ReactivePanacheQuery} instance for the given query
* @see #find(String, Parameters)
* @see #find(String, Sort, Map)
* @see #find(String, Sort, Parameters)
* @see #list(String, Sort, Parameters)
* @see #stream(String, Sort, Parameters)
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> Publisher<T> stream(Document query, Document sort) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find all entities of this type.
* This method is a shortcut for <code>findAll().stream()</code>.
*
* @return a {@link Stream} containing all results, without paging
* @see #streamAll(Sort)
* @see #findAll()
* @see #listAll()
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> Publisher<T> streamAll() {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find all entities of this type, in the given order.
* This method is a shortcut for <code>findAll(sort).stream()</code>.
*
* @param sort the sort order to use
* @return a {@link Stream} containing all results, without paging
* @see #streamAll()
* @see #findAll(Sort)
* @see #listAll(Sort)
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> Publisher<T> streamAll(Sort sort) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Counts the number of this type of entity in the database.
*
* @return the number of this type of entity in the database.
* @see #count(String, Object...)
* @see #count(String, Map)
* @see #count(String, Parameters)
*/
@GenerateBridge
public static CompletionStage<Long> count() {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Counts the number of this type of entity matching the given query, with optional indexed parameters.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params optional sequence of indexed parameters
* @return the number of entities counted.
* @see #count()
* @see #count(String, Map)
* @see #count(String, Parameters)
*/
@GenerateBridge
public static CompletionStage<Long> count(String query, Object... params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Counts the number of this type of entity matching the given query, with named parameters.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params {@link Map} of named parameters
* @return the number of entities counted.
* @see #count()
* @see #count(String, Object...)
* @see #count(String, Parameters)
*/
@GenerateBridge
public static CompletionStage<Long> count(String query, Map<String, Object> params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Counts the number of this type of entity matching the given query, with named parameters.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params {@link Parameters} of named parameters
* @return the number of entities counted.
* @see #count()
* @see #count(String, Object...)
* @see #count(String, Map)
*/
@GenerateBridge
public static CompletionStage<Long> count(String query, Parameters params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Counts the number of this type of entity matching the given query
*
* @param query a {@link Document} query
* @return he number of entities counted.
* @see #count()
* @see #count(String, Object...)
* @see #count(String, Map)
*/
@GenerateBridge
public static CompletionStage<Long> count(Document query) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Delete all entities of this type from the database.
*
* @return the number of entities deleted.
* @see #delete(String, Object...)
* @see #delete(String, Map)
* @see #delete(String, Parameters)
*/
@GenerateBridge
public static CompletionStage<Long> deleteAll() {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Delete all entities of this type matching the given query, with optional indexed parameters.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params optional sequence of indexed parameters
* @return the number of entities deleted.
* @see #deleteAll()
* @see #delete(String, Map)
* @see #delete(String, Parameters)
*/
@GenerateBridge
public static CompletionStage<Long> delete(String query, Object... params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Delete all entities of this type matching the given query, with named parameters.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params {@link Map} of named parameters
* @return the number of entities deleted.
* @see #deleteAll()
* @see #delete(String, Object...)
* @see #delete(String, Parameters)
*/
@GenerateBridge
public static CompletionStage<Long> delete(String query, Map<String, Object> params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Delete all entities of this type matching the given query, with named parameters.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params {@link Parameters} of named parameters
* @return the number of entities deleted.
* @see #deleteAll()
* @see #delete(String, Object...)
* @see #delete(String, Map)
*/
@GenerateBridge
public static CompletionStage<Long> delete(String query, Parameters params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Delete all entities of this type matching the given query
*
* @param query a {@link Document} query
* @return he number of entities counted.
* @see #count()
* @see #count(String, Object...)
* @see #count(String, Map)
*/
@GenerateBridge
public static CompletionStage<Long> delete(Document query) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Insert all given entities.
*
* @param entities the entities to insert
* @see #persist()
* @see #persist(Stream)
* @see #persist(Object,Object...)
*/
public static CompletionStage<Void> persist(Iterable<?> entities) {
return ReactiveMongoOperations.persist(entities);
}
/**
* Insert all given entities.
*
* @param entities the entities to insert
* @see #persist()
* @see #persist(Iterable)
* @see #persist(Object,Object...)
*/
public static CompletionStage<Void> persist(Stream<?> entities) {
return ReactiveMongoOperations.persist(entities);
}
/**
* Insert all given entities.
*
* @param entities the entities to update
* @see #persist()
* @see #persist(Stream)
* @see #persist(Iterable)
*/
public static CompletionStage<Void> persist(Object firstEntity, Object... entities) {
return ReactiveMongoOperations.persist(firstEntity, entities);
}
/**
* Update all given entities.
*
* @param entities the entities to update
* @see #update()
* @see #update(Stream)
* @see #update(Object,Object...)
*/
public static CompletionStage<Void> update(Iterable<?> entities) {
return ReactiveMongoOperations.update(entities);
}
/**
* Update all given entities.
*
* @param entities the entities to insert
* @see #update()
* @see #update(Iterable)
* @see #update(Object,Object...)
*/
public static CompletionStage<Void> update(Stream<?> entities) {
return ReactiveMongoOperations.update(entities);
}
/**
* Update all given entities.
*
* @param entities the entities to update
* @see #update()
* @see #update(Stream)
* @see #update(Iterable)
*/
public static CompletionStage<Void> update(Object firstEntity, Object... entities) {
return ReactiveMongoOperations.update(firstEntity, entities);
}
/**
* Persist all given entities or update them if they already exist.
*
* @param entities the entities to update
* @see #persistOrUpdate()
* @see #persistOrUpdate(Stream)
* @see #persistOrUpdate(Object,Object...)
*/
public static CompletionStage<Void> persistOrUpdate(Iterable<?> entities) {
return ReactiveMongoOperations.persistOrUpdate(entities);
}
/**
* Persist all given entities.
*
* @param entities the entities to insert
* @see #persistOrUpdate()
* @see #persistOrUpdate(Iterable)
* @see #persistOrUpdate(Object,Object...)
*/
public static CompletionStage<Void> persistOrUpdate(Stream<?> entities) {
return ReactiveMongoOperations.persistOrUpdate(entities);
}
/**
* Persist all given entities.
*
* @param entities the entities to update
* @see #persistOrUpdate()
* @see #persistOrUpdate(Stream)
* @see #persistOrUpdate(Iterable)
*/
public static CompletionStage<Void> persistOrUpdate(Object firstEntity, Object... entities) {
return ReactiveMongoOperations.persistOrUpdate(firstEntity, entities);
}
/**
* Allow to access the underlying Mongo Collection.
*/
@GenerateBridge
public static <T extends ReactivePanacheMongoEntityBase> ReactiveMongoCollection<T> mongoCollection() {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Allow to access the underlying Mongo Database.
*/
@GenerateBridge
public static ReactiveMongoDatabase mongoDatabase() {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
}

View File

@@ -0,0 +1,15 @@
package io.quarkus.mongodb.panache.axle;
import org.bson.types.ObjectId;
/**
* Represents a Repository for a specific type of entity {@code Entity}, with an ID type
* of {@code ObjectId}. Implementing this repository will gain you the exact same useful methods
* that are on {@link ReactivePanacheMongoEntityBase}. If you have a custom ID strategy, you should
* implement {@link ReactivePanacheMongoRepositoryBase} instead.
*
* @param <Entity> The type of entity to operate on
*/
public interface ReactivePanacheMongoRepository<Entity> extends ReactivePanacheMongoRepositoryBase<Entity, ObjectId> {
}

View File

@@ -0,0 +1,889 @@
package io.quarkus.mongodb.panache.axle;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletionStage;
import java.util.stream.Stream;
import org.bson.Document;
import org.reactivestreams.Publisher;
import io.quarkus.mongodb.ReactiveMongoCollection;
import io.quarkus.mongodb.ReactiveMongoDatabase;
import io.quarkus.mongodb.panache.axle.runtime.ReactiveMongoOperations;
import io.quarkus.panache.common.Parameters;
import io.quarkus.panache.common.Sort;
import io.quarkus.panache.common.impl.GenerateBridge;
/**
* Represents a Repository for a specific type of entity {@code Entity}, with an ID type
* of {@code Id}. Implementing this repository will gain you the exact same useful methods
* that are on {@link ReactivePanacheMongoEntityBase}. Unless you have a custom ID strategy, you should not
* implement this interface directly but implement {@link ReactivePanacheMongoRepository} instead.
*
* @param <Entity> The type of entity to operate on
* @param <Id> The ID type of the entity
* @see ReactivePanacheMongoRepository
*/
public interface ReactivePanacheMongoRepositoryBase<Entity, Id> {
// Operations
/**
* Persist the given entity in the database.
* This will set it's ID field if not already set.
*
* @param entity the entity to insert.
* @see #persist(Iterable)
* @see #persist(Stream)
* @see #persist(Object, Object...)
*/
public default CompletionStage<Void> persist(Entity entity) {
return ReactiveMongoOperations.persist(entity);
}
/**
* Update the given entity in the database.
*
* @param entity the entity to update.
* @see #update(Iterable)
* @see #update(Stream)
* @see #update(Object, Object...)
*/
public default CompletionStage<Void> update(Entity entity) {
return ReactiveMongoOperations.update(entity);
}
/**
* Persist the given entity in the database or update it if it already exist.
*
* @param entity the entity to update.
* @see #persistOrUpdate(Iterable)
* @see #persistOrUpdate(Stream)
* @see #persistOrUpdate(Object, Object...)
*/
public default CompletionStage<Void> persistOrUpdate(Entity entity) {
return ReactiveMongoOperations.persistOrUpdate(entity);
}
/**
* Delete the given entity from the database, if it is already persisted.
*
* @param entity the entity to delete.
* @see #delete(String, Object...)
* @see #delete(String, Map)
* @see #delete(String, Parameters)
* @see #deleteAll()
*/
public default CompletionStage<Void> delete(Entity entity) {
return ReactiveMongoOperations.delete(entity);
}
// Queries
/**
* Find an entity of this type by ID.
*
* @param id the ID of the entity to find.
* @return the entity found, or <code>null</code> if not found.
*/
@GenerateBridge
public default CompletionStage<Entity> findById(Id id) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find an entity of this type by ID.
*
* @param id the ID of the entity to find.
* @return if found, an optional containing the entity, else <code>Optional.empty()</code>.
*/
@GenerateBridge
public default CompletionStage<Optional<Entity>> findByIdOptional(Object id) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities using a query, with optional indexed parameters.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params optional sequence of indexed parameters
* @return a new {@link ReactivePanacheQuery} instance for the given query
* @see #find(String, Sort, Object...)
* @see #find(String, Map)
* @see #find(String, Parameters)
* @see #list(String, Object...)
* @see #stream(String, Object...)
*/
@GenerateBridge
public default ReactivePanacheQuery<Entity> find(String query, Object... params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities using a query and the given sort options, with optional indexed parameters.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param sort the sort strategy to use
* @param params optional sequence of indexed parameters
* @return a new {@link ReactivePanacheQuery} instance for the given query
* @see #find(String, Object...)
* @see #find(String, Sort, Map)
* @see #find(String, Sort, Parameters)
* @see #list(String, Sort, Object...)
* @see #stream(String, Sort, Object...)
*/
@GenerateBridge
public default ReactivePanacheQuery<Entity> find(String query, Sort sort, Object... params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities using a query, with named parameters.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params {@link Map} of named parameters
* @return a new {@link ReactivePanacheQuery} instance for the given query
* @see #find(String, Sort, Map)
* @see #find(String, Object...)
* @see #find(String, Parameters)
* @see #list(String, Map)
* @see #stream(String, Map)
*/
@GenerateBridge
public default ReactivePanacheQuery<Entity> find(String query, Map<String, Object> params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities using a query and the given sort options, with named parameters.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param sort the sort strategy to use
* @param params {@link Map} of indexed parameters
* @return a new {@link ReactivePanacheQuery} instance for the given query
* @see #find(String, Map)
* @see #find(String, Sort, Object...)
* @see #find(String, Sort, Parameters)
* @see #list(String, Sort, Map)
* @see #stream(String, Sort, Map)
*/
@GenerateBridge
public default ReactivePanacheQuery<Entity> find(String query, Sort sort, Map<String, Object> params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities using a query, with named parameters.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params {@link Parameters} of named parameters
* @return a new {@link ReactivePanacheQuery} instance for the given query
* @see #find(String, Sort, Parameters)
* @see #find(String, Map)
* @see #find(String, Parameters)
* @see #list(String, Parameters)
* @see #stream(String, Parameters)
*/
@GenerateBridge
public default ReactivePanacheQuery<Entity> find(String query, Parameters params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities using a query and the given sort options, with named parameters.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param sort the sort strategy to use
* @param params {@link Parameters} of indexed parameters
* @return a new {@link ReactivePanacheQuery} instance for the given query
* @see #find(String, Parameters)
* @see #find(String, Sort, Map)
* @see #find(String, Sort, Parameters)
* @see #list(String, Sort, Parameters)
* @see #stream(String, Sort, Parameters)
*/
@GenerateBridge
public default ReactivePanacheQuery<Entity> find(String query, Sort sort, Parameters params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities using a BSON query.
*
* @param query a {@link Document} query
* @return a new {@link ReactivePanacheQuery} instance for the given query
* @see #find(String, Parameters)
* @see #find(String, Sort, Map)
* @see #find(String, Sort, Parameters)
* @see #list(String, Sort, Parameters)
* @see #stream(String, Sort, Parameters)
*/
@GenerateBridge
public default ReactivePanacheQuery<Entity> find(Document query) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities using a a BSON query and a BSON sort.
*
* @param query a {@link Document} query
* @param sort the {@link Document} sort
* @return a new {@link ReactivePanacheQuery} instance for the given query
* @see #find(String, Parameters)
* @see #find(String, Sort, Map)
* @see #find(String, Sort, Parameters)
* @see #list(String, Sort, Parameters)
* @see #stream(String, Sort, Parameters)
*/
@GenerateBridge
public default ReactivePanacheQuery<Entity> find(Document query, Document sort) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find all entities of this type.
*
* @return a new {@link ReactivePanacheQuery} instance to find all entities of this type.
* @see #findAll(Sort)
* @see #listAll()
* @see #streamAll()
*/
@GenerateBridge
public default ReactivePanacheQuery<Entity> findAll() {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find all entities of this type, in the given order.
*
* @param sort the sort order to use
* @return a new {@link ReactivePanacheQuery} instance to find all entities of this type.
* @see #findAll()
* @see #listAll(Sort)
* @see #streamAll(Sort)
*/
@GenerateBridge
public default ReactivePanacheQuery<Entity> findAll(Sort sort) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities matching a query, with optional indexed parameters.
* This method is a shortcut for <code>find(query, params).list()</code>.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params optional sequence of indexed parameters
* @return a {@link List} containing all results, without paging
* @see #list(String, Sort, Object...)
* @see #list(String, Map)
* @see #list(String, Parameters)
* @see #find(String, Object...)
* @see #stream(String, Object...)
*/
@GenerateBridge
public default CompletionStage<List<Entity>> list(String query, Object... params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities matching a query and the given sort options, with optional indexed parameters.
* This method is a shortcut for <code>find(query, sort, params).list()</code>.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param sort the sort strategy to use
* @param params optional sequence of indexed parameters
* @return a {@link List} containing all results, without paging
* @see #list(String, Object...)
* @see #list(String, Sort, Map)
* @see #list(String, Sort, Parameters)
* @see #find(String, Sort, Object...)
* @see #stream(String, Sort, Object...)
*/
@GenerateBridge
public default CompletionStage<List<Entity>> list(String query, Sort sort, Object... params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities matching a query, with named parameters.
* This method is a shortcut for <code>find(query, params).list()</code>.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params {@link Map} of named parameters
* @return a {@link List} containing all results, without paging
* @see #list(String, Sort, Map)
* @see #list(String, Object...)
* @see #list(String, Parameters)
* @see #find(String, Map)
* @see #stream(String, Map)
*/
@GenerateBridge
public default CompletionStage<List<Entity>> list(String query, Map<String, Object> params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities matching a query and the given sort options, with named parameters.
* This method is a shortcut for <code>find(query, sort, params).list()</code>.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param sort the sort strategy to use
* @param params {@link Map} of indexed parameters
* @return a {@link List} containing all results, without paging
* @see #list(String, Map)
* @see #list(String, Sort, Object...)
* @see #list(String, Sort, Parameters)
* @see #find(String, Sort, Map)
* @see #stream(String, Sort, Map)
*/
@GenerateBridge
public default CompletionStage<List<Entity>> list(String query, Sort sort, Map<String, Object> params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities matching a query, with named parameters.
* This method is a shortcut for <code>find(query, params).list()</code>.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params {@link Parameters} of named parameters
* @return a {@link List} containing all results, without paging
* @see #list(String, Sort, Parameters)
* @see #list(String, Object...)
* @see #list(String, Map)
* @see #find(String, Parameters)
* @see #stream(String, Parameters)
*/
@GenerateBridge
public default CompletionStage<List<Entity>> list(String query, Parameters params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities matching a query and the given sort options, with named parameters.
* This method is a shortcut for <code>find(query, sort, params).list()</code>.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param sort the sort strategy to use
* @param params {@link Parameters} of indexed parameters
* @return a {@link List} containing all results, without paging
* @see #list(String, Parameters)
* @see #list(String, Sort, Object...)
* @see #list(String, Sort, Map)
* @see #find(String, Sort, Parameters)
* @see #stream(String, Sort, Parameters)
*/
@GenerateBridge
public default CompletionStage<List<Entity>> list(String query, Sort sort, Parameters params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities using a BSON query.
* This method is a shortcut for <code>find(query).list()</code>.
*
* @param query a {@link Document} query
* @return a new {@link ReactivePanacheQuery} instance for the given query
* @see #find(String, Parameters)
* @see #find(String, Sort, Map)
* @see #find(String, Sort, Parameters)
* @see #list(String, Sort, Parameters)
* @see #stream(String, Sort, Parameters)
*/
@GenerateBridge
public default ReactivePanacheQuery<Entity> list(Document query) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities using a a BSON query and a BSON sort.
* This method is a shortcut for <code>find(query, sort).list()</code>.
*
* @param query a {@link Document} query
* @param sort the {@link Document} sort
* @return a new {@link ReactivePanacheQuery} instance for the given query
* @see #find(String, Parameters)
* @see #find(String, Sort, Map)
* @see #find(String, Sort, Parameters)
* @see #list(String, Sort, Parameters)
* @see #stream(String, Sort, Parameters)
*/
@GenerateBridge
public default ReactivePanacheQuery<Entity> list(Document query, Document sort) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find all entities of this type.
* This method is a shortcut for <code>findAll().list()</code>.
*
* @return a {@link List} containing all results, without paging
* @see #listAll(Sort)
* @see #findAll()
* @see #streamAll()
*/
@GenerateBridge
public default CompletionStage<List<Entity>> listAll() {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find all entities of this type, in the given order.
* This method is a shortcut for <code>findAll(sort).list()</code>.
*
* @param sort the sort order to use
* @return a {@link List} containing all results, without paging
* @see #listAll()
* @see #findAll(Sort)
* @see #streamAll(Sort)
*/
@GenerateBridge
public default CompletionStage<List<Entity>> listAll(Sort sort) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities matching a query, with optional indexed parameters.
* This method is a shortcut for <code>find(query, params).stream()</code>.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params optional sequence of indexed parameters
* @return a {@link Stream} containing all results, without paging
* @see #stream(String, Sort, Object...)
* @see #stream(String, Map)
* @see #stream(String, Parameters)
* @see #find(String, Object...)
* @see #list(String, Object...)
*/
@GenerateBridge
public default Publisher<Entity> stream(String query, Object... params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities matching a query and the given sort options, with optional indexed parameters.
* This method is a shortcut for <code>find(query, sort, params).stream()</code>.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param sort the sort strategy to use
* @param params optional sequence of indexed parameters
* @return a {@link Stream} containing all results, without paging
* @see #stream(String, Object...)
* @see #stream(String, Sort, Map)
* @see #stream(String, Sort, Parameters)
* @see #find(String, Sort, Object...)
* @see #list(String, Sort, Object...)
*/
@GenerateBridge
public default Publisher<Entity> stream(String query, Sort sort, Object... params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities matching a query, with named parameters.
* This method is a shortcut for <code>find(query, params).stream()</code>.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params {@link Map} of named parameters
* @return a {@link Stream} containing all results, without paging
* @see #stream(String, Sort, Map)
* @see #stream(String, Object...)
* @see #stream(String, Parameters)
* @see #find(String, Map)
* @see #list(String, Map)
*/
@GenerateBridge
public default Publisher<Entity> stream(String query, Map<String, Object> params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities matching a query and the given sort options, with named parameters.
* This method is a shortcut for <code>find(query, sort, params).stream()</code>.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param sort the sort strategy to use
* @param params {@link Map} of indexed parameters
* @return a {@link Stream} containing all results, without paging
* @see #stream(String, Map)
* @see #stream(String, Sort, Object...)
* @see #stream(String, Sort, Parameters)
* @see #find(String, Sort, Map)
* @see #list(String, Sort, Map)
*/
@GenerateBridge
public default Publisher<Entity> stream(String query, Sort sort, Map<String, Object> params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities matching a query, with named parameters.
* This method is a shortcut for <code>find(query, params).stream()</code>.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params {@link Parameters} of named parameters
* @return a {@link Stream} containing all results, without paging
* @see #stream(String, Sort, Parameters)
* @see #stream(String, Object...)
* @see #stream(String, Map)
* @see #find(String, Parameters)
* @see #list(String, Parameters)
*/
@GenerateBridge
public default Publisher<Entity> stream(String query, Parameters params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities matching a query and the given sort options, with named parameters.
* This method is a shortcut for <code>find(query, sort, params).stream()</code>.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param sort the sort strategy to use
* @param params {@link Parameters} of indexed parameters
* @return a {@link Stream} containing all results, without paging
* @see #stream(String, Parameters)
* @see #stream(String, Sort, Object...)
* @see #stream(String, Sort, Map)
* @see #find(String, Sort, Parameters)
* @see #list(String, Sort, Parameters)
*/
@GenerateBridge
public default Publisher<Entity> stream(String query, Sort sort, Parameters params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities using a BSON query.
* This method is a shortcut for <code>find(query).stream()</code>.
*
* @param query a {@link Document} query
* @return a new {@link ReactivePanacheQuery} instance for the given query
* @see #find(String, Parameters)
* @see #find(String, Sort, Map)
* @see #find(String, Sort, Parameters)
* @see #list(String, Sort, Parameters)
* @see #stream(String, Sort, Parameters)
*/
@GenerateBridge
public default Publisher<Entity> stream(Document query) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find entities using a a BSON query and a BSON sort.
* This method is a shortcut for <code>find(query, sort).stream()</code>.
*
* @param query a {@link Document} query
* @param sort the {@link Document} sort
* @return a new {@link ReactivePanacheQuery} instance for the given query
* @see #find(String, Parameters)
* @see #find(String, Sort, Map)
* @see #find(String, Sort, Parameters)
* @see #list(String, Sort, Parameters)
* @see #stream(String, Sort, Parameters)
*/
@GenerateBridge
public default Publisher<Entity> stream(Document query, Document sort) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find all entities of this type.
* This method is a shortcut for <code>findAll().stream()</code>.
*
* @return a {@link Stream} containing all results, without paging
* @see #streamAll(Sort)
* @see #findAll()
* @see #listAll()
*/
@GenerateBridge
public default Publisher<Entity> streamAll(Sort sort) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Find all entities of this type, in the given order.
* This method is a shortcut for <code>findAll(sort).stream()</code>.
*
* @return a {@link Stream} containing all results, without paging
* @see #streamAll()
* @see #findAll(Sort)
* @see #listAll(Sort)
*/
@GenerateBridge
public default Publisher<Entity> streamAll() {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Counts the number of this type of entity in the database.
*
* @return the number of this type of entity in the database.
* @see #count(String, Object...)
* @see #count(String, Map)
* @see #count(String, Parameters)
*/
@GenerateBridge
public default CompletionStage<Long> count() {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Counts the number of this type of entity matching the given query, with optional indexed parameters.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params optional sequence of indexed parameters
* @return the number of entities counted.
* @see #count()
* @see #count(String, Map)
* @see #count(String, Parameters)
*/
@GenerateBridge
public default CompletionStage<Long> count(String query, Object... params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Counts the number of this type of entity matching the given query, with named parameters.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params {@link Map} of named parameters
* @return the number of entities counted.
* @see #count()
* @see #count(String, Object...)
* @see #count(String, Parameters)
*/
@GenerateBridge
public default CompletionStage<Long> count(String query, Map<String, Object> params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Counts the number of this type of entity matching the given query, with named parameters.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params {@link Parameters} of named parameters
* @return the number of entities counted.
* @see #count()
* @see #count(String, Object...)
* @see #count(String, Map)
*/
@GenerateBridge
public default CompletionStage<Long> count(String query, Parameters params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Counts the number of this type of entity matching the given query
*
* @param query a {@link Document} query
* @return he number of entities counted.
* @see #count()
* @see #count(String, Object...)
* @see #count(String, Map)
*/
@GenerateBridge
public default CompletionStage<Long> count(Document query) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Delete all entities of this type from the database.
*
* @return the number of entities deleted.
* @see #delete(String, Object...)
* @see #delete(String, Map)
* @see #delete(String, Parameters)
*/
@GenerateBridge
public default CompletionStage<Long> deleteAll() {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Delete all entities of this type matching the given query, with optional indexed parameters.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params optional sequence of indexed parameters
* @return the number of entities deleted.
* @see #deleteAll()
* @see #delete(String, Map)
* @see #delete(String, Parameters)
*/
@GenerateBridge
public default CompletionStage<Long> delete(String query, Object... params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Delete all entities of this type matching the given query, with named parameters.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params {@link Map} of named parameters
* @return the number of entities deleted.
* @see #deleteAll()
* @see #delete(String, Object...)
* @see #delete(String, Parameters)
*/
@GenerateBridge
public default CompletionStage<Long> delete(String query, Map<String, Object> params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Delete all entities of this type matching the given query, with named parameters.
*
* @param query a {@link io.quarkus.mongodb.panache query string}
* @param params {@link Parameters} of named parameters
* @return the number of entities deleted.
* @see #deleteAll()
* @see #delete(String, Object...)
* @see #delete(String, Map)
*/
@GenerateBridge
public default CompletionStage<Long> delete(String query, Parameters params) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Delete all entities of this type matching the given query
*
* @param query a {@link Document} query
* @return he number of entities counted.
* @see #count()
* @see #count(String, Object...)
* @see #count(String, Map)
*/
@GenerateBridge
public default CompletionStage<Long> delete(Document query) {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Persist all given entities.
*
* @param entities the entities to insert
* @see #persist(Object)
* @see #persist(Stream)
* @see #persist(Object,Object...)
*/
public default CompletionStage<Void> persist(Iterable<Entity> entities) {
return ReactiveMongoOperations.persist(entities);
}
/**
* Persist all given entities.
*
* @param entities the entities to insert
* @see #persist(Object)
* @see #persist(Iterable)
* @see #persist(Object,Object...)
*/
public default CompletionStage<Void> persist(Stream<Entity> entities) {
return ReactiveMongoOperations.persist(entities);
}
/**
* Persist all given entities.
*
* @param entities the entities to insert
* @see #persist(Object)
* @see #persist(Stream)
* @see #persist(Iterable)
*/
public default CompletionStage<Void> persist(Entity firstEntity, @SuppressWarnings("unchecked") Entity... entities) {
return ReactiveMongoOperations.persist(firstEntity, entities);
}
/**
* Update all given entities.
*
* @param entities the entities to update
* @see #update(Object)
* @see #update(Stream)
* @see #update(Object,Object...)
*/
public default CompletionStage<Void> update(Iterable<Entity> entities) {
return ReactiveMongoOperations.update(entities);
}
/**
* Update all given entities.
*
* @param entities the entities to update
* @see #update(Object)
* @see #update(Iterable)
* @see #update(Object,Object...)
*/
public default CompletionStage<Void> update(Stream<Entity> entities) {
return ReactiveMongoOperations.update(entities);
}
/**
* Update all given entities.
*
* @param entities the entities to update
* @see #update(Object)
* @see #update(Stream)
* @see #update(Iterable)
*/
public default CompletionStage<Void> update(Entity firstEntity, @SuppressWarnings("unchecked") Entity... entities) {
return ReactiveMongoOperations.update(firstEntity, entities);
}
/**
* Persist all given entities or update them if they already exist.
*
* @param entities the entities to update
* @see #persistOrUpdate(Object)
* @see #persistOrUpdate(Stream)
* @see #persistOrUpdate(Object,Object...)
*/
public default CompletionStage<Void> persistOrUpdate(Iterable<Entity> entities) {
return ReactiveMongoOperations.persistOrUpdate(entities);
}
/**
* Persist all given entities or update them if they already exist.
*
* @param entities the entities to update
* @see #persistOrUpdate(Object)
* @see #persistOrUpdate(Iterable)
* @see #persistOrUpdate(Object,Object...)
*/
public default CompletionStage<Void> persistOrUpdate(Stream<Entity> entities) {
return ReactiveMongoOperations.persistOrUpdate(entities);
}
/**
* Persist all given entities or update them if they already exist.
*
* @param entities the entities to update
* @see #update(Object)
* @see #update(Stream)
* @see #update(Iterable)
*/
public default CompletionStage<Void> persistOrUpdate(Entity firstEntity,
@SuppressWarnings("unchecked") Entity... entities) {
return ReactiveMongoOperations.persistOrUpdate(firstEntity, entities);
}
/**
* Allow to access the underlying Mongo Collection
*/
@GenerateBridge
public default ReactiveMongoCollection<Entity> mongoCollection() {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
/**
* Allow to access the underlying Mongo Database.
*/
@GenerateBridge
public default ReactiveMongoDatabase mongoDatabase() {
throw ReactiveMongoOperations.implementationInjectionMissing();
}
}

View File

@@ -0,0 +1,179 @@
package io.quarkus.mongodb.panache.axle;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletionStage;
import java.util.stream.Stream;
import org.reactivestreams.Publisher;
import io.quarkus.panache.common.Page;
/**
* Interface representing an entity query, which abstracts the use of paging, getting the number of results, and
* operating on {@link List} or {@link Stream}.
*
* Instances of this interface cannot mutate the query itself or its parameters: only paging information can be
* modified, and instances of this interface can be reused to obtain multiple pages of results.
*
* @param <Entity> The entity type being queried
*/
public interface ReactivePanacheQuery<Entity> {
// Builder
/**
* Sets the current page.
*
* @param page the new page
* @return this query, modified
* @see #page(int, int)
* @see #page()
*/
public <T extends Entity> ReactivePanacheQuery<T> page(Page page);
/**
* Sets the current page.
*
* @param pageIndex the page index
* @param pageSize the page size
* @return this query, modified
* @see #page(Page)
* @see #page()
*/
public <T extends Entity> ReactivePanacheQuery<T> page(int pageIndex, int pageSize);
/**
* Sets the current page to the next page
*
* @return this query, modified
* @see #previousPage()
*/
public <T extends Entity> ReactivePanacheQuery<T> nextPage();
/**
* Sets the current page to the previous page (or the first page if there is no previous page)
*
* @return this query, modified
* @see #nextPage()
*/
public <T extends Entity> ReactivePanacheQuery<T> previousPage();
/**
* Sets the current page to the first page
*
* @return this query, modified
* @see #lastPage()
*/
public <T extends Entity> ReactivePanacheQuery<T> firstPage();
/**
* Sets the current page to the last page. This will cause reading of the entity count.
*
* @return this query, modified
* @see #firstPage()
* @see #count()
*/
public <T extends Entity> CompletionStage<ReactivePanacheQuery<T>> lastPage();
/**
* Returns true if there is another page to read after the current one.
* This will cause reading of the entity count.
*
* @return true if there is another page to read
* @see #hasPreviousPage()
* @see #count()
*/
public CompletionStage<Boolean> hasNextPage();
/**
* Returns true if there is a page to read before the current one.
*
* @return true if there is a previous page to read
* @see #hasNextPage()
*/
public boolean hasPreviousPage();
/**
* Returns the total number of pages to be read using the current page size.
* This will cause reading of the entity count.
*
* @return the total number of pages to be read using the current page size.
*/
public CompletionStage<Integer> pageCount();
/**
* Returns the current page.
*
* @return the current page
* @see #page(Page)
* @see #page(int,int)
*/
public Page page();
// Results
/**
* Reads and caches the total number of entities this query operates on. This causes a database
* query with <code>SELECT COUNT(*)</code> and a query equivalent to the current query, minus
* ordering.
*
* @return the total number of entities this query operates on, cached.
*/
public CompletionStage<Long> count();
/**
* Returns the current page of results as a {@link List}.
*
* @return the current page of results as a {@link List}.
* @see #page(Page)
* @see #page()
*/
public <T extends Entity> CompletionStage<List<T>> list();
/**
* Returns the current page of results as a {@link Stream}.
*
* @return the current page of results as a {@link Stream}.
* @see #list()
* @see #page(Page)
* @see #page()
*/
public <T extends Entity> Publisher<T> stream();
/**
* Returns the first result of the current page index. This ignores the current page size to fetch
* a single result.
*
* @return the first result of the current page index, or null if there are no results.
* @see #singleResult()
*/
public <T extends Entity> CompletionStage<T> firstResult();
/**
* Returns the first result of the current page index. This ignores the current page size to fetch
* a single result.
*
* @return if found, an optional containing the entity, else <code>Optional.empty()</code>.
* @see #singleResultOptional()
*/
public <T extends Entity> CompletionStage<Optional<T>> firstResultOptional();
/**
* Executes this query for the current page and return a single result.
*
* @return the single result.
* @throws io.quarkus.panache.common.exception.PanacheQueryException if there are more than one result.
* @see #firstResult()
*/
public <T extends Entity> CompletionStage<T> singleResult();
/**
* Executes this query for the current page and return a single result.
*
* @return if found, an optional containing the entity, else <code>Optional.empty()</code>.
* @throws io.quarkus.panache.common.exception.PanacheQueryException if there are more than one result.
* @see #firstResultOptional()
*/
public <T extends Entity> CompletionStage<Optional<T>> singleResultOptional();
}

View File

@@ -0,0 +1,577 @@
package io.quarkus.mongodb.panache.axle.runtime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.bson.BsonDocument;
import org.bson.BsonDocumentWriter;
import org.bson.BsonValue;
import org.bson.Document;
import org.bson.codecs.Codec;
import org.bson.codecs.EncoderContext;
import org.eclipse.microprofile.config.ConfigProvider;
import org.eclipse.microprofile.context.ThreadContext;
import org.jboss.logging.Logger;
import org.reactivestreams.Publisher;
import com.mongodb.client.model.InsertOneModel;
import com.mongodb.client.model.ReplaceOneModel;
import com.mongodb.client.model.ReplaceOptions;
import com.mongodb.client.model.UpdateOptions;
import com.mongodb.client.model.WriteModel;
import io.quarkus.arc.Arc;
import io.quarkus.mongodb.ReactiveMongoClient;
import io.quarkus.mongodb.ReactiveMongoCollection;
import io.quarkus.mongodb.ReactiveMongoDatabase;
import io.quarkus.mongodb.panache.MongoEntity;
import io.quarkus.mongodb.panache.axle.ReactivePanacheQuery;
import io.quarkus.mongodb.panache.binder.NativeQueryBinder;
import io.quarkus.mongodb.panache.binder.PanacheQlQueryBinder;
import io.quarkus.panache.common.Parameters;
import io.quarkus.panache.common.Sort;
public class ReactiveMongoOperations {
private static final Logger LOGGER = Logger.getLogger(ReactiveMongoOperations.class);
public static final String ID = "_id";
public static final String MONGODB_DATABASE = "quarkus.mongodb.database";
//
// Instance methods
public static CompletionStage<Void> persist(Object entity) {
ReactiveMongoCollection collection = mongoCollection(entity);
return persist(collection, entity);
}
public static CompletionStage<Void> persist(Iterable<?> entities) {
// not all iterables are re-traversal, so we traverse it once for copying inside a list
List<Object> objects = new ArrayList<>();
for (Object entity : entities) {
objects.add(entity);
}
if (objects.size() > 0) {
// get the first entity to be able to retrieve the collection with it
Object firstEntity = objects.get(0);
ReactiveMongoCollection collection = mongoCollection(firstEntity);
return persist(collection, objects);
}
return nullFuture();
}
public static CompletionStage<Void> persist(Object firstEntity, Object... entities) {
ReactiveMongoCollection collection = mongoCollection(firstEntity);
if (entities == null || entities.length == 0) {
return persist(collection, firstEntity);
} else {
List<Object> entityList = new ArrayList<>();
entityList.add(firstEntity);
entityList.addAll(Arrays.asList(entities));
return persist(collection, entityList);
}
}
public static CompletionStage<Void> persist(Stream<?> entities) {
List<Object> objects = entities.collect(Collectors.toList());
if (objects.size() > 0) {
// get the first entity to be able to retrieve the collection with it
Object firstEntity = objects.get(0);
ReactiveMongoCollection collection = mongoCollection(firstEntity);
return persist(collection, objects);
}
return nullFuture();
}
public static CompletionStage<Void> update(Object entity) {
ReactiveMongoCollection collection = mongoCollection(entity);
return update(collection, entity);
}
public static CompletionStage<Void> update(Iterable<?> entities) {
// not all iterables are re-traversal, so we traverse it once for copying inside a list
List<Object> objects = new ArrayList<>();
for (Object entity : entities) {
objects.add(entity);
}
if (objects.size() > 0) {
// get the first entity to be able to retrieve the collection with it
Object firstEntity = objects.get(0);
ReactiveMongoCollection collection = mongoCollection(firstEntity);
return update(collection, objects);
}
return nullFuture();
}
public static CompletionStage<Void> update(Object firstEntity, Object... entities) {
ReactiveMongoCollection collection = mongoCollection(firstEntity);
if (entities == null || entities.length == 0) {
return update(collection, firstEntity);
} else {
List<Object> entityList = new ArrayList<>();
entityList.add(firstEntity);
entityList.addAll(Arrays.asList(entities));
return update(collection, entityList);
}
}
public static CompletionStage<Void> update(Stream<?> entities) {
List<Object> objects = entities.collect(Collectors.toList());
if (objects.size() > 0) {
// get the first entity to be able to retrieve the collection with it
Object firstEntity = objects.get(0);
ReactiveMongoCollection collection = mongoCollection(firstEntity);
return update(collection, objects);
}
return nullFuture();
}
public static CompletionStage<Void> persistOrUpdate(Object entity) {
ReactiveMongoCollection collection = mongoCollection(entity);
return persistOrUpdate(collection, entity);
}
public static CompletionStage<Void> persistOrUpdate(Iterable<?> entities) {
// not all iterables are re-traversal, so we traverse it once for copying inside a list
List<Object> objects = new ArrayList<>();
for (Object entity : entities) {
objects.add(entity);
}
if (objects.size() > 0) {
// get the first entity to be able to retrieve the collection with it
Object firstEntity = objects.get(0);
ReactiveMongoCollection collection = mongoCollection(firstEntity);
return persistOrUpdate(collection, objects);
}
return nullFuture();
}
public static CompletionStage<Void> persistOrUpdate(Object firstEntity, Object... entities) {
ReactiveMongoCollection collection = mongoCollection(firstEntity);
if (entities == null || entities.length == 0) {
return persistOrUpdate(collection, firstEntity);
} else {
List<Object> entityList = new ArrayList<>();
entityList.add(firstEntity);
entityList.addAll(Arrays.asList(entities));
return persistOrUpdate(collection, entityList);
}
}
public static CompletionStage<Void> persistOrUpdate(Stream<?> entities) {
List<Object> objects = entities.collect(Collectors.toList());
if (objects.size() > 0) {
// get the first entity to be able to retrieve the collection with it
Object firstEntity = objects.get(0);
ReactiveMongoCollection collection = mongoCollection(firstEntity);
return persistOrUpdate(collection, objects);
}
return nullFuture();
}
public static CompletionStage<Void> delete(Object entity) {
ReactiveMongoCollection collection = mongoCollection(entity);
BsonDocument document = getBsonDocument(collection, entity);
BsonValue id = document.get(ID);
BsonDocument query = new BsonDocument().append(ID, id);
return collection.deleteOne(query).thenApply(r -> null);
}
public static ReactiveMongoCollection mongoCollection(Class<?> entityClass) {
MongoEntity mongoEntity = entityClass.getAnnotation(MongoEntity.class);
ReactiveMongoDatabase database = mongoDatabase(mongoEntity);
if (mongoEntity != null && !mongoEntity.collection().isEmpty()) {
return database.getCollection(mongoEntity.collection(), entityClass);
}
return database.getCollection(entityClass.getSimpleName(), entityClass);
}
public static ReactiveMongoDatabase mongoDatabase(Class<?> entityClass) {
MongoEntity mongoEntity = entityClass.getAnnotation(MongoEntity.class);
return mongoDatabase(mongoEntity);
}
//
// Private stuff
public static CompletableFuture<Void> nullFuture() {
return completedFuture(null);
}
public static <U> CompletableFuture<U> completedFuture(U value) {
ThreadContext threadContext = Arc.container().instance(ThreadContext.class).get();
return threadContext.withContextCapture(CompletableFuture.completedFuture(value));
}
private static CompletionStage<Void> persist(ReactiveMongoCollection collection, Object entity) {
return collection.insertOne(entity);
}
private static CompletionStage<Void> persist(ReactiveMongoCollection collection, List<Object> entities) {
return collection.insertMany(entities);
}
private static CompletionStage<Void> update(ReactiveMongoCollection collection, Object entity) {
//we transform the entity as a document first
BsonDocument document = getBsonDocument(collection, entity);
//then we get its id field and create a new Document with only this one that will be our replace query
BsonValue id = document.get(ID);
BsonDocument query = new BsonDocument().append(ID, id);
return collection.replaceOne(query, entity).thenApply(u -> null);
}
private static CompletionStage<Void> update(ReactiveMongoCollection collection, List<Object> entities) {
CompletionStage<Void> ret = nullFuture();
for (Object entity : entities) {
ret.thenCompose(v -> update(collection, entity));
}
return ret.thenApply(v -> null);
}
private static CompletionStage<Void> persistOrUpdate(ReactiveMongoCollection collection, Object entity) {
//we transform the entity as a document first
BsonDocument document = getBsonDocument(collection, entity);
//then we get its id field and create a new Document with only this one that will be our replace query
BsonValue id = document.get(ID);
if (id == null) {
//insert with autogenerated ID
return collection.insertOne(entity);
} else {
//insert with user provided ID or update
BsonDocument query = new BsonDocument().append(ID, id);
return collection.replaceOne(query, entity, ReplaceOptions.createReplaceOptions(new UpdateOptions().upsert(true)))
.thenApply(u -> null);
}
}
private static CompletionStage<Void> persistOrUpdate(ReactiveMongoCollection collection, List<Object> entities) {
//this will be an ordered bulk: it's less performant than a unordered one but will fail at the first failed write
List<WriteModel> bulk = new ArrayList<>();
for (Object entity : entities) {
//we transform the entity as a document first
BsonDocument document = getBsonDocument(collection, entity);
//then we get its id field and create a new Document with only this one that will be our replace query
BsonValue id = document.get(ID);
if (id == null) {
//insert with autogenerated ID
bulk.add(new InsertOneModel(entity));
} else {
//insert with user provided ID or update
BsonDocument query = new BsonDocument().append(ID, id);
bulk.add(new ReplaceOneModel(query, entity,
ReplaceOptions.createReplaceOptions(new UpdateOptions().upsert(true))));
}
}
return collection.bulkWrite(bulk).thenApply(b -> null);
}
private static BsonDocument getBsonDocument(ReactiveMongoCollection collection, Object entity) {
BsonDocument document = new BsonDocument();
Codec codec = collection.getCodecRegistry().get(entity.getClass());
codec.encode(new BsonDocumentWriter(document), entity, EncoderContext.builder().build());
return document;
}
private static ReactiveMongoCollection mongoCollection(Object entity) {
Class<?> entityClass = entity.getClass();
return mongoCollection(entityClass);
}
private static ReactiveMongoDatabase mongoDatabase(MongoEntity entity) {
ReactiveMongoClient mongoClient = Arc.container().instance(ReactiveMongoClient.class).get();
if (entity != null && !entity.database().isEmpty()) {
return mongoClient.getDatabase(entity.database());
}
String databaseName = ConfigProvider.getConfig()
.getValue(MONGODB_DATABASE, String.class);
return mongoClient.getDatabase(databaseName);
}
//
// Queries
public static CompletionStage<Object> findById(Class<?> entityClass, Object id) {
CompletionStage<Optional> optionalEntity = findByIdOptional(entityClass, id);
return optionalEntity.thenApply(optional -> optional.orElse(null));
}
public static CompletionStage<Optional> findByIdOptional(Class<?> entityClass, Object id) {
ReactiveMongoCollection collection = mongoCollection(entityClass);
return collection.find(new Document(ID, id)).findFirst().run();
}
public static ReactivePanacheQuery<?> find(Class<?> entityClass, String query, Object... params) {
return find(entityClass, query, null, params);
}
@SuppressWarnings("rawtypes")
public static ReactivePanacheQuery<?> find(Class<?> entityClass, String query, Sort sort, Object... params) {
String bindQuery = bindQuery(entityClass, query, params);
Document docQuery = Document.parse(bindQuery);
Document docSort = sortToDocument(sort);
ReactiveMongoCollection collection = mongoCollection(entityClass);
return new ReactivePanacheQueryImpl(collection, entityClass, docQuery, docSort);
}
/**
* We should have a query like <code>{'firstname': ?1, 'lastname': ?2}</code> for native one
* and like <code>firstname = ?1</code> for PanacheQL one.
*/
static String bindQuery(Class<?> clazz, String query, Object[] params) {
String bindQuery = null;
//determine the type of the query
if (query.charAt(0) == '{') {
//this is a native query
bindQuery = NativeQueryBinder.bindQuery(query, params);
} else {
//this is a PanacheQL query
bindQuery = PanacheQlQueryBinder.bindQuery(clazz, query, params);
}
LOGGER.debug(bindQuery);
return bindQuery;
}
/**
* We should have a query like <code>{'firstname': :firstname, 'lastname': :lastname}</code> for native one
* and like <code>firstname = :firstname and lastname = :lastname</code> for PanacheQL one.
*/
static String bindQuery(Class<?> clazz, String query, Map<String, Object> params) {
String bindQuery = null;
//determine the type of the query
if (query.charAt(0) == '{') {
//this is a native query
bindQuery = NativeQueryBinder.bindQuery(query, params);
} else {
//this is a PanacheQL query
bindQuery = PanacheQlQueryBinder.bindQuery(clazz, query, params);
}
LOGGER.debug(bindQuery);
return bindQuery;
}
public static ReactivePanacheQuery<?> find(Class<?> entityClass, String query, Map<String, Object> params) {
return find(entityClass, query, null, params);
}
@SuppressWarnings("rawtypes")
public static ReactivePanacheQuery<?> find(Class<?> entityClass, String query, Sort sort, Map<String, Object> params) {
String bindQuery = bindQuery(entityClass, query, params);
Document docQuery = Document.parse(bindQuery);
Document docSort = sortToDocument(sort);
ReactiveMongoCollection collection = mongoCollection(entityClass);
return new ReactivePanacheQueryImpl(collection, entityClass, docQuery, docSort);
}
public static ReactivePanacheQuery<?> find(Class<?> entityClass, String query, Parameters params) {
return find(entityClass, query, null, params.map());
}
public static ReactivePanacheQuery<?> find(Class<?> entityClass, String query, Sort sort, Parameters params) {
return find(entityClass, query, sort, params.map());
}
@SuppressWarnings("rawtypes")
public static ReactivePanacheQuery<?> find(Class<?> entityClass, Document query, Sort sort) {
ReactiveMongoCollection collection = mongoCollection(entityClass);
Document sortDoc = sortToDocument(sort);
return new ReactivePanacheQueryImpl(collection, entityClass, query, sortDoc);
}
public static ReactivePanacheQuery<?> find(Class<?> entityClass, Document query, Document sort) {
ReactiveMongoCollection collection = mongoCollection(entityClass);
return new ReactivePanacheQueryImpl(collection, entityClass, query, sort);
}
public static ReactivePanacheQuery<?> find(Class<?> entityClass, Document query) {
return find(entityClass, query, (Document) null);
}
public static CompletionStage<List<?>> list(Class<?> entityClass, String query, Object... params) {
return (CompletionStage) find(entityClass, query, params).list();
}
public static CompletionStage<List<?>> list(Class<?> entityClass, String query, Sort sort, Object... params) {
return (CompletionStage) find(entityClass, query, sort, params).list();
}
public static CompletionStage<List<?>> list(Class<?> entityClass, String query, Map<String, Object> params) {
return (CompletionStage) find(entityClass, query, params).list();
}
public static CompletionStage<List<?>> list(Class<?> entityClass, String query, Sort sort, Map<String, Object> params) {
return (CompletionStage) find(entityClass, query, sort, params).list();
}
public static CompletionStage<List<?>> list(Class<?> entityClass, String query, Parameters params) {
return (CompletionStage) find(entityClass, query, params).list();
}
public static CompletionStage<List<?>> list(Class<?> entityClass, String query, Sort sort, Parameters params) {
return (CompletionStage) find(entityClass, query, sort, params).list();
}
//specific Mongo query
public static CompletionStage<List<?>> list(Class<?> entityClass, Document query) {
return (CompletionStage) find(entityClass, query).list();
}
//specific Mongo query
public static CompletionStage<List<?>> list(Class<?> entityClass, Document query, Document sort) {
return (CompletionStage) find(entityClass, query, sort).list();
}
public static Publisher<?> stream(Class<?> entityClass, String query, Object... params) {
return find(entityClass, query, params).stream();
}
public static Publisher<?> stream(Class<?> entityClass, String query, Sort sort, Object... params) {
return find(entityClass, query, sort, params).stream();
}
public static Publisher<?> stream(Class<?> entityClass, String query, Map<String, Object> params) {
return find(entityClass, query, params).stream();
}
public static Publisher<?> stream(Class<?> entityClass, String query, Sort sort, Map<String, Object> params) {
return find(entityClass, query, sort, params).stream();
}
public static Publisher<?> stream(Class<?> entityClass, String query, Parameters params) {
return find(entityClass, query, params).stream();
}
public static Publisher<?> stream(Class<?> entityClass, String query, Sort sort, Parameters params) {
return find(entityClass, query, sort, params).stream();
}
//specific Mongo query
public static Publisher<?> stream(Class<?> entityClass, Document query) {
return find(entityClass, query).stream();
}
//specific Mongo query
public static Publisher<?> stream(Class<?> entityClass, Document query, Document sort) {
return find(entityClass, query, sort).stream();
}
@SuppressWarnings("rawtypes")
public static ReactivePanacheQuery<?> findAll(Class<?> entityClass) {
ReactiveMongoCollection collection = mongoCollection(entityClass);
return new ReactivePanacheQueryImpl(collection, entityClass, null, null);
}
@SuppressWarnings("rawtypes")
public static ReactivePanacheQuery<?> findAll(Class<?> entityClass, Sort sort) {
ReactiveMongoCollection collection = mongoCollection(entityClass);
Document sortDoc = sortToDocument(sort);
return new ReactivePanacheQueryImpl(collection, entityClass, null, sortDoc);
}
private static Document sortToDocument(Sort sort) {
if (sort == null) {
return null;
}
Document sortDoc = new Document();
for (Sort.Column col : sort.getColumns()) {
sortDoc.append(col.getName(), col.getDirection() == Sort.Direction.Ascending ? 1 : -1);
}
return sortDoc;
}
public static CompletionStage<List<?>> listAll(Class<?> entityClass) {
return (CompletionStage) findAll(entityClass).list();
}
public static CompletionStage<List<?>> listAll(Class<?> entityClass, Sort sort) {
return (CompletionStage) findAll(entityClass, sort).list();
}
public static Publisher<?> streamAll(Class<?> entityClass) {
return findAll(entityClass).stream();
}
public static Publisher<?> streamAll(Class<?> entityClass, Sort sort) {
return findAll(entityClass, sort).stream();
}
public static CompletionStage<Long> count(Class<?> entityClass) {
ReactiveMongoCollection collection = mongoCollection(entityClass);
return collection.countDocuments();
}
public static CompletionStage<Long> count(Class<?> entityClass, String query, Object... params) {
String bindQuery = bindQuery(entityClass, query, params);
Document docQuery = Document.parse(bindQuery);
ReactiveMongoCollection collection = mongoCollection(entityClass);
return collection.countDocuments(docQuery);
}
public static CompletionStage<Long> count(Class<?> entityClass, String query, Map<String, Object> params) {
String bindQuery = bindQuery(entityClass, query, params);
Document docQuery = Document.parse(bindQuery);
ReactiveMongoCollection collection = mongoCollection(entityClass);
return collection.countDocuments(docQuery);
}
public static CompletionStage<Long> count(Class<?> entityClass, String query, Parameters params) {
return count(entityClass, query, params.map());
}
//specific Mongo query
public static CompletionStage<Long> count(Class<?> entityClass, Document query) {
ReactiveMongoCollection<?> collection = mongoCollection(entityClass);
return collection.countDocuments(query);
}
public static CompletionStage<Long> deleteAll(Class<?> entityClass) {
ReactiveMongoCollection<?> collection = mongoCollection(entityClass);
return collection.deleteMany(new Document()).thenApply(deleteResult -> deleteResult.getDeletedCount());
}
public static CompletionStage<Long> delete(Class<?> entityClass, String query, Object... params) {
String bindQuery = bindQuery(entityClass, query, params);
Document docQuery = Document.parse(bindQuery);
ReactiveMongoCollection<?> collection = mongoCollection(entityClass);
return collection.deleteMany(docQuery).thenApply(deleteResult -> deleteResult.getDeletedCount());
}
public static CompletionStage<Long> delete(Class<?> entityClass, String query, Map<String, Object> params) {
String bindQuery = bindQuery(entityClass, query, params);
Document docQuery = Document.parse(bindQuery);
ReactiveMongoCollection<?> collection = mongoCollection(entityClass);
return collection.deleteMany(docQuery).thenApply(deleteResult -> deleteResult.getDeletedCount());
}
public static CompletionStage<Long> delete(Class<?> entityClass, String query, Parameters params) {
return delete(entityClass, query, params.map());
}
//specific Mongo query
public static CompletionStage<Long> delete(Class<?> entityClass, Document query) {
ReactiveMongoCollection<?> collection = mongoCollection(entityClass);
return collection.deleteMany(query).thenApply(deleteResult -> deleteResult.getDeletedCount());
}
public static IllegalStateException implementationInjectionMissing() {
return new IllegalStateException(
"This method is normally automatically overridden in subclasses");
}
}

View File

@@ -0,0 +1,171 @@
package io.quarkus.mongodb.panache.axle.runtime;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletionStage;
import org.bson.Document;
import org.eclipse.microprofile.reactive.streams.operators.PublisherBuilder;
import org.reactivestreams.Publisher;
import io.quarkus.mongodb.FindOptions;
import io.quarkus.mongodb.ReactiveMongoCollection;
import io.quarkus.mongodb.panache.axle.ReactivePanacheQuery;
import io.quarkus.panache.common.Page;
import io.quarkus.panache.common.exception.PanacheQueryException;
public class ReactivePanacheQueryImpl<Entity> implements ReactivePanacheQuery<Entity> {
private ReactiveMongoCollection collection;
private Class<? extends Entity> entityClass;
private Document mongoQuery;
private Document sort;
/*
* We store the pageSize and apply it for each request because getFirstResult()
* sets the page size to 1
*/
private Page page;
private CompletionStage<Long> count;
ReactivePanacheQueryImpl(ReactiveMongoCollection<? extends Entity> collection, Class<? extends Entity> entityClass,
Document mongoQuery,
Document sort) {
this.collection = collection;
this.entityClass = entityClass;
this.mongoQuery = mongoQuery;
this.sort = sort;
page = new Page(0, Integer.MAX_VALUE);
}
// Builder
@Override
@SuppressWarnings("unchecked")
public <T extends Entity> ReactivePanacheQuery<T> page(Page page) {
this.page = page;
return (ReactivePanacheQuery<T>) this;
}
@Override
public <T extends Entity> ReactivePanacheQuery<T> page(int pageIndex, int pageSize) {
return page(Page.of(pageIndex, pageSize));
}
@Override
public <T extends Entity> ReactivePanacheQuery<T> nextPage() {
return page(page.next());
}
@Override
public <T extends Entity> ReactivePanacheQuery<T> previousPage() {
return page(page.previous());
}
@Override
public <T extends Entity> ReactivePanacheQuery<T> firstPage() {
return page(page.first());
}
@Override
public <T extends Entity> CompletionStage<ReactivePanacheQuery<T>> lastPage() {
return pageCount().thenApply(pageCount -> {
return page(page.index(pageCount - 1));
});
}
@Override
public CompletionStage<Boolean> hasNextPage() {
return pageCount().thenApply(pageCount -> {
return page.index < (pageCount - 1);
});
}
@Override
public boolean hasPreviousPage() {
return page.index > 0;
}
@Override
public CompletionStage<Integer> pageCount() {
return count().thenApply(count -> {
if (count == 0)
return 1; // a single page of zero results
return (int) Math.ceil((double) count / (double) page.size);
});
}
@Override
public Page page() {
return page;
}
// Results
@Override
@SuppressWarnings("unchecked")
public CompletionStage<Long> count() {
if (count == null) {
count = collection.countDocuments(mongoQuery);
}
return count;
}
@Override
@SuppressWarnings("unchecked")
public <T extends Entity> CompletionStage<List<T>> list() {
FindOptions options = new FindOptions();
options.sort(sort).skip(page.index).limit(page.size);
PublisherBuilder<T> results = mongoQuery == null ? collection.find(options) : collection.find(mongoQuery, options);
return results.toList().run();
}
@Override
@SuppressWarnings("unchecked")
public <T extends Entity> Publisher<T> stream() {
FindOptions options = new FindOptions();
options.sort(sort).skip(page.index).limit(page.size);
return mongoQuery == null ? collection.find(options).buildRs() : collection.find(mongoQuery, options).buildRs();
}
@Override
public <T extends Entity> CompletionStage<T> firstResult() {
CompletionStage<Optional<T>> optionalEntity = firstResultOptional();
return optionalEntity.thenApply(optional -> optional.orElse(null));
}
@Override
public <T extends Entity> CompletionStage<Optional<T>> firstResultOptional() {
FindOptions options = new FindOptions();
options.sort(sort).skip(page.index).limit(1);
return mongoQuery == null ? collection.find(options).findFirst().run()
: collection.find(mongoQuery, options).findFirst().run();
}
@Override
@SuppressWarnings("unchecked")
public <T extends Entity> CompletionStage<T> singleResult() {
FindOptions options = new FindOptions();
options.sort(sort).skip(page.index).limit(2);
PublisherBuilder<T> results = mongoQuery == null ? collection.find(options) : collection.find(mongoQuery, options);
return results.toList().run().thenApply(list -> {
if (list.size() == 0 || list.size() > 1) {
throw new PanacheQueryException("There should be only one result");
} else {
return list.get(0);
}
});
}
@Override
public <T extends Entity> CompletionStage<Optional<T>> singleResultOptional() {
FindOptions options = new FindOptions();
options.sort(sort).skip(page.index).limit(2);
PublisherBuilder<T> results = mongoQuery == null ? collection.find(options) : collection.find(mongoQuery, options);
return results.toList().run().thenApply(list -> {
if (list.size() == 2) {
throw new PanacheQueryException("There should be no more than one result");
}
return list.isEmpty() ? Optional.empty() : Optional.of(list.get(0));
});
}
}

View File

@@ -1,4 +1,4 @@
package io.quarkus.mongodb.panache.runtime;
package io.quarkus.mongodb.panache.binder;
import java.text.SimpleDateFormat;
import java.time.Instant;

View File

@@ -1,8 +1,8 @@
package io.quarkus.mongodb.panache.runtime;
package io.quarkus.mongodb.panache.binder;
import java.util.Map;
class NativeQueryBinder {
public class NativeQueryBinder {
public static String bindQuery(String query, Object[] params) {
String bindQuery = query;

View File

@@ -1,4 +1,4 @@
package io.quarkus.mongodb.panache.runtime;
package io.quarkus.mongodb.panache.binder;
import java.util.HashMap;
import java.util.Map;
@@ -6,6 +6,7 @@ import java.util.Map;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import io.quarkus.mongodb.panache.runtime.MongoPropertyUtil;
import io.quarkus.panacheql.internal.HqlLexer;
import io.quarkus.panacheql.internal.HqlParser;
import io.quarkus.panacheql.internal.HqlParserBaseVisitor;

View File

@@ -29,6 +29,8 @@ import com.mongodb.client.model.WriteModel;
import io.quarkus.arc.Arc;
import io.quarkus.mongodb.panache.MongoEntity;
import io.quarkus.mongodb.panache.PanacheQuery;
import io.quarkus.mongodb.panache.binder.NativeQueryBinder;
import io.quarkus.mongodb.panache.binder.PanacheQlQueryBinder;
import io.quarkus.panache.common.Parameters;
import io.quarkus.panache.common.Sort;

View File

@@ -23,7 +23,7 @@ public final class MongoPropertyUtil {
replacementCache = newReplacementCache;
}
static Map<String, String> getReplacementMap(Class<?> clazz) {
public static Map<String, String> getReplacementMap(Class<?> clazz) {
return replacementCache.computeIfAbsent(clazz.getName(), s -> buildWithReflection(clazz));
}

View File

@@ -23,6 +23,10 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-mongodb-panache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-client</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>

View File

@@ -0,0 +1,81 @@
package io.quarkus.it.mongodb.panache.axle.book;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import javax.json.bind.annotation.JsonbDateFormat;
import org.bson.codecs.pojo.annotations.BsonIgnore;
import org.bson.codecs.pojo.annotations.BsonProperty;
import io.quarkus.it.mongodb.panache.book.BookDetail;
import io.quarkus.mongodb.panache.MongoEntity;
import io.quarkus.mongodb.panache.axle.ReactivePanacheMongoEntity;
@MongoEntity(collection = "TheBookEntity")
public class ReactiveBookEntity extends ReactivePanacheMongoEntity {
@BsonProperty("bookTitle")
private String title;
private String author;
@BsonIgnore
private String transientDescription;
@JsonbDateFormat("yyyy-MM-dd")
private LocalDate creationDate;
private List<String> categories = new ArrayList<>();
private BookDetail details;
public String getTitle() {
return title;
}
public ReactiveBookEntity setTitle(String title) {
this.title = title;
return this;
}
public String getAuthor() {
return author;
}
public ReactiveBookEntity setAuthor(String author) {
this.author = author;
return this;
}
public List<String> getCategories() {
return categories;
}
public ReactiveBookEntity setCategories(List<String> categories) {
this.categories = categories;
return this;
}
public BookDetail getDetails() {
return details;
}
public ReactiveBookEntity setDetails(BookDetail details) {
this.details = details;
return this;
}
public String getTransientDescription() {
return transientDescription;
}
public void setTransientDescription(String transientDescription) {
this.transientDescription = transientDescription;
}
public LocalDate getCreationDate() {
return creationDate;
}
public void setCreationDate(LocalDate creationDate) {
this.creationDate = creationDate;
}
}

View File

@@ -0,0 +1,123 @@
package io.quarkus.it.mongodb.panache.axle.book;
import java.net.URI;
import java.time.LocalDate;
import java.util.List;
import java.util.concurrent.CompletionStage;
import javax.annotation.PostConstruct;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.bson.types.ObjectId;
import org.jboss.logging.Logger;
import org.jboss.resteasy.annotations.SseElementType;
import org.reactivestreams.Publisher;
import io.quarkus.panache.common.Parameters;
import io.quarkus.panache.common.Sort;
@Path("/axle/books/entity")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ReactiveBookEntityResource {
private static final Logger LOGGER = Logger.getLogger(ReactiveBookEntityResource.class);
@PostConstruct
void init() {
String databaseName = ReactiveBookEntity.mongoDatabase().getName();
String collectionName = ReactiveBookEntity.mongoCollection().getNamespace().getCollectionName();
LOGGER.infov("Using BookEntity[database={0}, collection={1}]", databaseName, collectionName);
}
@GET
public CompletionStage<List<ReactiveBookEntity>> getBooks(@QueryParam("sort") String sort) {
if (sort != null) {
return ReactiveBookEntity.listAll(Sort.ascending(sort));
}
return ReactiveBookEntity.listAll();
}
@GET
@Path("/stream")
@Produces(MediaType.SERVER_SENT_EVENTS)
@SseElementType(MediaType.APPLICATION_JSON)
public Publisher<ReactiveBookEntity> streamBooks(@QueryParam("sort") String sort) {
if (sort != null) {
return ReactiveBookEntity.streamAll(Sort.ascending(sort));
}
return ReactiveBookEntity.streamAll();
}
@POST
public CompletionStage<Response> addBook(ReactiveBookEntity book) {
return book.persist().thenApply(v -> {
//the ID is populated before sending it to the database
String id = book.id.toString();
return Response.created(URI.create("/books/entity" + id)).build();
});
}
@PUT
public CompletionStage<Response> updateBook(ReactiveBookEntity book) {
return book.update().thenApply(v -> Response.accepted().build());
}
// PATCH is not correct here but it allows to test persistOrUpdate without a specific subpath
@PATCH
public CompletionStage<Response> upsertBook(ReactiveBookEntity book) {
return book.persistOrUpdate().thenApply(v -> Response.accepted().build());
}
@DELETE
@Path("/{id}")
public CompletionStage<Void> deleteBook(@PathParam("id") String id) {
return ReactiveBookEntity.findById(new ObjectId(id)).thenCompose(book -> book.delete());
}
@GET
@Path("/{id}")
public CompletionStage<ReactiveBookEntity> getBook(@PathParam("id") String id) {
return ReactiveBookEntity.findById(new ObjectId(id));
}
@GET
@Path("/search/{author}")
public CompletionStage<List<ReactiveBookEntity>> getBooksByAuthor(@PathParam("author") String author) {
return ReactiveBookEntity.list("author", author);
}
@GET
@Path("/search")
public CompletionStage<ReactiveBookEntity> search(@QueryParam("author") String author, @QueryParam("title") String title,
@QueryParam("dateFrom") String dateFrom, @QueryParam("dateTo") String dateTo) {
if (author != null) {
return ReactiveBookEntity.find("{'author': ?1,'bookTitle': ?2}", author, title).firstResult();
}
return ReactiveBookEntity
.find("{'creationDate': {$gte: ?1}, 'creationDate': {$lte: ?2}}", LocalDate.parse(dateFrom),
LocalDate.parse(dateTo))
.firstResult();
}
@GET
@Path("/search2")
public CompletionStage<ReactiveBookEntity> search2(@QueryParam("author") String author, @QueryParam("title") String title,
@QueryParam("dateFrom") String dateFrom, @QueryParam("dateTo") String dateTo) {
if (author != null) {
return ReactiveBookEntity.find("{'author': :author,'bookTitle': :title}",
Parameters.with("author", author).and("title", title)).firstResult();
}
return ReactiveBookEntity.find("{'creationDate': {$gte: :dateFrom}, 'creationDate': {$lte: :dateTo}}",
Parameters.with("dateFrom", LocalDate.parse(dateFrom)).and("dateTo", LocalDate.parse(dateTo))).firstResult();
}
@DELETE
public CompletionStage<Void> deleteAll() {
return ReactiveBookEntity.deleteAll().thenApply(l -> null);
}
}

View File

@@ -0,0 +1,10 @@
package io.quarkus.it.mongodb.panache.axle.book;
import javax.enterprise.context.ApplicationScoped;
import io.quarkus.it.mongodb.panache.book.Book;
import io.quarkus.mongodb.panache.axle.ReactivePanacheMongoRepository;
@ApplicationScoped
public class ReactiveBookRepository implements ReactivePanacheMongoRepository<Book> {
}

View File

@@ -0,0 +1,126 @@
package io.quarkus.it.mongodb.panache.axle.book;
import java.net.URI;
import java.time.LocalDate;
import java.util.List;
import java.util.concurrent.CompletionStage;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.bson.types.ObjectId;
import org.jboss.logging.Logger;
import org.jboss.resteasy.annotations.SseElementType;
import org.reactivestreams.Publisher;
import io.quarkus.it.mongodb.panache.book.Book;
import io.quarkus.panache.common.Parameters;
import io.quarkus.panache.common.Sort;
@Path("/axle/books/repository")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ReactiveBookRepositoryResource {
private static final Logger LOGGER = Logger.getLogger(ReactiveBookRepositoryResource.class);
@Inject
ReactiveBookRepository reactiveBookRepository;
@PostConstruct
void init() {
String databaseName = reactiveBookRepository.mongoDatabase().getName();
String collectionName = reactiveBookRepository.mongoCollection().getNamespace().getCollectionName();
LOGGER.infov("Using BookRepository[database={0}, collection={1}]", databaseName, collectionName);
}
@GET
public CompletionStage<List<Book>> getBooks(@QueryParam("sort") String sort) {
if (sort != null) {
return reactiveBookRepository.listAll(Sort.ascending(sort));
}
return reactiveBookRepository.listAll();
}
@GET
@Path("/stream")
@Produces(MediaType.SERVER_SENT_EVENTS)
@SseElementType(MediaType.APPLICATION_JSON)
public Publisher<Book> streamBooks(@QueryParam("sort") String sort) {
if (sort != null) {
return reactiveBookRepository.streamAll(Sort.ascending(sort));
}
return reactiveBookRepository.streamAll();
}
@POST
public CompletionStage<Response> addBook(Book book) {
return reactiveBookRepository.persist(book).thenApply(v -> {
//the ID is populated before sending it to the database
String id = book.getId().toString();
return Response.created(URI.create("/books/entity" + id)).build();
});
}
@PUT
public CompletionStage<Response> updateBook(Book book) {
return reactiveBookRepository.update(book).thenApply(v -> Response.accepted().build());
}
// PATCH is not correct here but it allows to test persistOrUpdate without a specific subpath
@PATCH
public CompletionStage<Response> upsertBook(Book book) {
return reactiveBookRepository.persistOrUpdate(book).thenApply(v -> Response.accepted().build());
}
@DELETE
@Path("/{id}")
public CompletionStage<Void> deleteBook(@PathParam("id") String id) {
return reactiveBookRepository.findById(new ObjectId(id)).thenCompose(book -> reactiveBookRepository.delete(book));
}
@GET
@Path("/{id}")
public CompletionStage<Book> getBook(@PathParam("id") String id) {
return reactiveBookRepository.findById(new ObjectId(id));
}
@GET
@Path("/search/{author}")
public CompletionStage<List<Book>> getBooksByAuthor(@PathParam("author") String author) {
return reactiveBookRepository.list("author", author);
}
@GET
@Path("/search")
public CompletionStage<Book> search(@QueryParam("author") String author, @QueryParam("title") String title,
@QueryParam("dateFrom") String dateFrom, @QueryParam("dateTo") String dateTo) {
if (author != null) {
return reactiveBookRepository.find("{'author': ?1,'bookTitle': ?2}", author, title).firstResult();
}
return reactiveBookRepository
.find("{'creationDate': {$gte: ?1}, 'creationDate': {$lte: ?2}}", LocalDate.parse(dateFrom),
LocalDate.parse(dateTo))
.firstResult();
}
@GET
@Path("/search2")
public CompletionStage<Book> search2(@QueryParam("author") String author, @QueryParam("title") String title,
@QueryParam("dateFrom") String dateFrom, @QueryParam("dateTo") String dateTo) {
if (author != null) {
return reactiveBookRepository.find("{'author': :author,'bookTitle': :title}",
Parameters.with("author", author).and("title", title)).firstResult();
}
return reactiveBookRepository.find("{'creationDate': {$gte: :dateFrom}, 'creationDate': {$lte: :dateTo}}",
Parameters.with("dateFrom", LocalDate.parse(dateFrom)).and("dateTo", LocalDate.parse(dateTo))).firstResult();
}
@DELETE
public CompletionStage<Void> deleteAll() {
return reactiveBookRepository.deleteAll().thenApply(l -> null);
}
}

View File

@@ -0,0 +1,12 @@
package io.quarkus.it.mongodb.panache.axle.person;
import org.bson.codecs.pojo.annotations.BsonId;
import io.quarkus.mongodb.panache.axle.ReactivePanacheMongoEntityBase;
public class ReactivePersonEntity extends ReactivePanacheMongoEntityBase {
@BsonId
public Long id;
public String firstname;
public String lastname;
}

View File

@@ -0,0 +1,74 @@
package io.quarkus.it.mongodb.panache.axle.person;
import java.net.URI;
import java.util.List;
import java.util.concurrent.CompletionStage;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import io.quarkus.panache.common.Sort;
@Path("/axle/persons/entity")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ReactivePersonEntityResource {
@GET
public CompletionStage<List<ReactivePersonEntity>> getPersons(@QueryParam("sort") String sort) {
if (sort != null) {
return ReactivePersonEntity.listAll(Sort.ascending(sort));
}
return ReactivePersonEntity.listAll();
}
@POST
public CompletionStage<Response> addPerson(ReactivePersonEntity person) {
return person.persist().thenApply(v -> {
//the ID is populated before sending it to the database
String id = person.id.toString();
return Response.created(URI.create("/persons/entity" + id)).build();
});
}
@POST
@Path("/multiple")
public CompletionStage<Void> addPersons(List<ReactivePersonEntity> persons) {
return ReactivePersonEntity.persist(persons);
}
@PUT
public CompletionStage<Response> updatePerson(ReactivePersonEntity person) {
return person.update().thenApply(v -> Response.accepted().build());
}
// PATCH is not correct here but it allows to test persistOrUpdate without a specific subpath
@PATCH
public CompletionStage<Response> upsertPerson(ReactivePersonEntity person) {
return person.persistOrUpdate().thenApply(v -> Response.accepted().build());
}
@DELETE
@Path("/{id}")
public CompletionStage<Void> deletePerson(@PathParam("id") String id) {
return ReactivePersonEntity.findById(Long.parseLong(id)).thenCompose(person -> person.delete());
}
@GET
@Path("/{id}")
public CompletionStage<ReactivePersonEntity> getPerson(@PathParam("id") String id) {
return ReactivePersonEntity.findById(Long.parseLong(id));
}
@GET
@Path("/count")
public CompletionStage<Long> countAll() {
return ReactivePersonEntity.count();
}
@DELETE
public CompletionStage<Void> deleteAll() {
return ReactivePersonEntity.deleteAll().thenAccept(l -> {
});
}
}

View File

@@ -0,0 +1,10 @@
package io.quarkus.it.mongodb.panache.axle.person;
import javax.enterprise.context.ApplicationScoped;
import io.quarkus.it.mongodb.panache.person.Person;
import io.quarkus.mongodb.panache.axle.ReactivePanacheMongoRepositoryBase;
@ApplicationScoped
public class ReactivePersonRepository implements ReactivePanacheMongoRepositoryBase<Person, Long> {
}

View File

@@ -0,0 +1,81 @@
package io.quarkus.it.mongodb.panache.axle.person;
import java.net.URI;
import java.util.List;
import java.util.concurrent.CompletionStage;
import javax.inject.Inject;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import io.quarkus.it.mongodb.panache.person.Person;
import io.quarkus.panache.common.Sort;
@Path("/axle/persons/repository")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ReactivePersonRepositoryResource {
@Inject
ReactivePersonRepository reactivePersonRepository;
@GET
public CompletionStage<List<Person>> getPersons(@QueryParam("sort") String sort) {
if (sort != null) {
return reactivePersonRepository.listAll(Sort.ascending(sort));
}
return reactivePersonRepository.listAll();
}
@POST
public CompletionStage<Response> addPerson(Person person) {
return reactivePersonRepository.persist(person).thenApply(v -> {
//the ID is populated before sending it to the database
String id = person.id.toString();
return Response.created(URI.create("/persons/entity" + id)).build();
});
}
@POST
@Path("/multiple")
public CompletionStage<Void> addPersons(List<Person> persons) {
return reactivePersonRepository.persist(persons);
}
@PUT
public CompletionStage<Response> updatePerson(Person person) {
return reactivePersonRepository.update(person).thenApply(v -> Response.accepted().build());
}
// PATCH is not correct here but it allows to test persistOrUpdate without a specific subpath
@PATCH
public CompletionStage<Response> upsertPerson(Person person) {
return reactivePersonRepository.persistOrUpdate(person).thenApply(v -> Response.accepted().build());
}
@DELETE
@Path("/{id}")
public CompletionStage<Void> deletePerson(@PathParam("id") String id) {
return reactivePersonRepository.findById(Long.parseLong(id))
.thenCompose(person -> reactivePersonRepository.delete(person));
}
@GET
@Path("/{id}")
public CompletionStage<Person> getPerson(@PathParam("id") String id) {
return reactivePersonRepository.findById(Long.parseLong(id));
}
@GET
@Path("/count")
public CompletionStage<Long> countAll() {
return reactivePersonRepository.count();
}
@DELETE
public CompletionStage<Void> deleteAll() {
return reactivePersonRepository.deleteAll().thenAccept(l -> {
});
}
}

View File

@@ -108,4 +108,9 @@ public class BookEntityResource {
Parameters.with("dateFrom", LocalDate.parse(dateFrom)).and("dateTo", LocalDate.parse(dateTo))).firstResult();
}
@DELETE
public void deleteAll() {
BookEntity.deleteAll();
}
}

View File

@@ -111,4 +111,8 @@ public class BookRepositoryResource {
Parameters.with("dateFrom", LocalDate.parse(dateFrom)).and("dateTo", LocalDate.parse(dateTo))).firstResult();
}
@DELETE
public void deleteAll() {
bookRepository.deleteAll();
}
}

View File

@@ -3,8 +3,6 @@ package io.quarkus.it.mongodb.panache;
import static io.restassured.RestAssured.get;
import static org.hamcrest.Matchers.is;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@@ -187,6 +185,13 @@ class MongodbPanacheResourceTest {
//test some special characters
list = get(endpoint + "/search/Victor'\\ Hugo").as(LIST_OF_BOOK_TYPE_REF);
Assertions.assertEquals(0, list.size());
//delete all
response = RestAssured
.given()
.delete(endpoint)
.andReturn();
Assertions.assertEquals(204, response.statusCode());
}
private void callPersonEndpoint(String endpoint) {
@@ -303,10 +308,6 @@ class MongodbPanacheResourceTest {
return cal.getTime();
}
private Date fromYear(int year) {
return Date.from(LocalDate.of(year, 1, 1).atStartOfDay().toInstant(ZoneOffset.UTC));
}
@Test
public void testBug5274() {
get("/bugs/5274").then().body(is("OK"));

View File

@@ -3,6 +3,6 @@ package io.quarkus.it.mongodb.panache;
import io.quarkus.test.junit.NativeImageTest;
@NativeImageTest
class NativeBookResourceIT extends MongodbPanacheResourceTest {
class NativeMongodbPanacheResourceIT extends MongodbPanacheResourceTest {
}

View File

@@ -0,0 +1,8 @@
package io.quarkus.it.mongodb.panache.axle;
import io.quarkus.test.junit.NativeImageTest;
@NativeImageTest
class NativeMongodbPanacheResourceIT extends ReactiveMongodbPanacheResourceTest {
}

View File

@@ -0,0 +1,339 @@
package io.quarkus.it.mongodb.panache.axle;
import static io.restassured.RestAssured.get;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.sse.SseEventSource;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import io.quarkus.it.mongodb.panache.BookDTO;
import io.quarkus.it.mongodb.panache.MongoTestResource;
import io.quarkus.it.mongodb.panache.book.BookDetail;
import io.quarkus.it.mongodb.panache.person.Person;
import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.junit.QuarkusTest;
import io.restassured.RestAssured;
import io.restassured.common.mapper.TypeRef;
import io.restassured.config.ObjectMapperConfig;
import io.restassured.parsing.Parser;
import io.restassured.response.Response;
@QuarkusTest
@QuarkusTestResource(MongoTestResource.class)
class ReactiveMongodbPanacheResourceTest {
private static final Logger LOGGER = LoggerFactory.getLogger(ReactiveMongodbPanacheResourceTest.class);
private static final TypeRef<List<BookDTO>> LIST_OF_BOOK_TYPE_REF = new TypeRef<List<BookDTO>>() {
};
private static final TypeRef<List<Person>> LIST_OF_PERSON_TYPE_REF = new TypeRef<List<Person>>() {
};
@Test
public void testReactiveBookEntity() throws InterruptedException {
callReactiveBookEndpoint("/axle/books/entity");
}
@Test
public void testReactiveBookRepository() throws InterruptedException {
callReactiveBookEndpoint("/axle/books/repository");
}
@Test
public void testReactivePersonEntity() {
callReactivePersonEndpoint("/axle/persons/entity");
}
@Test
public void testReactivePersonRepository() {
callReactivePersonEndpoint("/axle/persons/repository");
}
private void callReactiveBookEndpoint(String endpoint) throws InterruptedException {
RestAssured.defaultParser = Parser.JSON;
ObjectMapper objectMapper = new ObjectMapper()
.registerModule(new Jdk8Module())
.registerModule(new JavaTimeModule())
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
RestAssured.config
.objectMapperConfig(new ObjectMapperConfig().jackson2ObjectMapperFactory((type, s) -> objectMapper));
List<BookDTO> list = get(endpoint).as(LIST_OF_BOOK_TYPE_REF);
assertEquals(0, list.size());
BookDTO book1 = new BookDTO().setAuthor("Victor Hugo").setTitle("Les Misérables")
.setCreationDate(yearToDate(1886))
.setCategories(Arrays.asList("long", "very long"))
.setDetails(new BookDetail().setRating(3).setSummary("A very long book"));
Response response = RestAssured
.given()
.header("Content-Type", "application/json")
.body(book1)
.post(endpoint)
.andReturn();
assertEquals(201, response.statusCode());
Assertions.assertTrue(response.header("Location").length() > 20);//Assert that id has been populated
BookDTO book2 = new BookDTO().setAuthor("Victor Hugo").setTitle("Notre-Dame de Paris")
.setCreationDate(yearToDate(1831))
.setCategories(Arrays.asList("long", "quasimodo"))
.setDetails(new BookDetail().setRating(4).setSummary("quasimodo and esmeralda"));
response = RestAssured
.given()
.header("Content-Type", "application/json")
.body(book2)
.post(endpoint)
.andReturn();
assertEquals(201, response.statusCode());
list = get(endpoint).as(LIST_OF_BOOK_TYPE_REF);
assertEquals(2, list.size());
BookDTO book3 = new BookDTO().setAuthor("Charles Baudelaire").setTitle("Les fleurs du mal")
.setCreationDate(yearToDate(1857))
.setCategories(Collections.singletonList("poem"))
.setDetails(new BookDetail().setRating(2).setSummary("Les Fleurs du mal is a volume of poetry."));
response = RestAssured
.given()
.header("Content-Type", "application/json")
.body(book3)
.post(endpoint)
.andReturn();
assertEquals(201, response.statusCode());
BookDTO book4 = new BookDTO().setAuthor("Charles Baudelaire").setTitle("Le Spleen de Paris")
.setCreationDate(yearToDate(1869))
.setCategories(Collections.singletonList("poem"))
.setDetails(new BookDetail().setRating(2)
.setSummary("Le Spleen de Paris is a collection of 50 short prose poems."));
response = RestAssured
.given()
.header("Content-Type", "application/json")
.body(book4)
.patch(endpoint)
.andReturn();
assertEquals(202, response.statusCode());
list = get(endpoint).as(LIST_OF_BOOK_TYPE_REF);
assertEquals(4, list.size());
//with sort
list = get(endpoint + "?sort=author").as(LIST_OF_BOOK_TYPE_REF);
assertEquals(4, list.size());
// magic query find("author", author)
list = get(endpoint + "/search/Victor Hugo").as(LIST_OF_BOOK_TYPE_REF);
assertEquals(2, list.size());
// magic query find("{'author':?1,'title':?1}", author, title)
BookDTO book = get(endpoint + "/search?author=Victor Hugo&title=Notre-Dame de Paris").as(BookDTO.class);
assertNotNull(book);
// date
book = get(endpoint + "/search?dateFrom=1885-01-01&dateTo=1887-01-01").as(BookDTO.class);
assertNotNull(book);
book = get(endpoint + "/search2?dateFrom=1885-01-01&dateTo=1887-01-01").as(BookDTO.class);
assertNotNull(book);
// magic query find("{'author'::author,'title'::title}", Parameters.with("author", author).and("title", title))
book = get(endpoint + "/search2?author=Victor Hugo&title=Notre-Dame de Paris").as(BookDTO.class);
assertNotNull(book);
assertNotNull(book.getId());
assertNotNull(book.getDetails());
//update a book
book.setTitle("Notre-Dame de Paris 2").setTransientDescription("should not be persisted");
response = RestAssured
.given()
.header("Content-Type", "application/json")
.body(book)
.put(endpoint)
.andReturn();
assertEquals(202, response.statusCode());
//check that the title has been updated and the transient description ignored
book = get(endpoint + "/" + book.getId().toString()).as(BookDTO.class);
assertEquals("Notre-Dame de Paris 2", book.getTitle());
Assertions.assertNull(book.getTransientDescription());
//delete a book
response = RestAssured
.given()
.delete(endpoint + "/" + book.getId().toString())
.andReturn();
assertEquals(204, response.statusCode());
list = get(endpoint).as(LIST_OF_BOOK_TYPE_REF);
assertEquals(3, list.size());
//test some special characters
list = get(endpoint + "/search/Victor'\\ Hugo").as(LIST_OF_BOOK_TYPE_REF);
assertEquals(0, list.size());
//test SSE : there is no JSON serialization for SSE so the test is not very elegant ...
Client client = ClientBuilder.newClient();
WebTarget target = client.target("http://localhost:8081" + endpoint + "/stream");
try (SseEventSource source = SseEventSource.target(target).build()) {
final IntegerAdder nbEvent = new IntegerAdder();
source.register((inboundSseEvent) -> {
try {
BookDTO theBook = objectMapper.readValue(inboundSseEvent.readData(), BookDTO.class);
assertNotNull(theBook);
} catch (IOException e) {
throw new RuntimeException(e);
}
nbEvent.increment();
});
source.open();
Thread.sleep(100);//wait a little for the events to comes in
assertEquals(3, nbEvent.count());
}
//delete all
response = RestAssured
.given()
.delete(endpoint)
.andReturn();
Assertions.assertEquals(204, response.statusCode());
}
private void callReactivePersonEndpoint(String endpoint) {
RestAssured.defaultParser = Parser.JSON;
RestAssured.config
.objectMapperConfig(new ObjectMapperConfig().jackson2ObjectMapperFactory((type, s) -> new ObjectMapper()
.registerModule(new Jdk8Module())
.registerModule(new JavaTimeModule())
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)));
List<Person> list = get(endpoint).as(LIST_OF_PERSON_TYPE_REF);
assertEquals(0, list.size());
Person person1 = new Person();
person1.id = 1L;
person1.firstname = "John";
person1.lastname = "Doe";
Response response = RestAssured
.given()
.header("Content-Type", "application/json")
.body(person1)
.post(endpoint)
.andReturn();
assertEquals(201, response.statusCode());
Person person2 = new Person();
person2.id = 2L;
person2.firstname = "Jane";
person2.lastname = "Doe";
Person person3 = new Person();
person3.id = 3L;
person3.firstname = "Victor";
person3.lastname = "Hugo";
List<Person> persons = new ArrayList<>();
persons.add(person2);
persons.add(person3);
response = RestAssured
.given()
.header("Content-Type", "application/json")
.body(persons)
.post(endpoint + "/multiple")
.andReturn();
assertEquals(204, response.statusCode());
Person person4 = new Person();
person4.id = 4L;
person4.firstname = "Charles";
person4.lastname = "Baudelaire";
response = RestAssured
.given()
.header("Content-Type", "application/json")
.body(person4)
.patch(endpoint)
.andReturn();
assertEquals(202, response.statusCode());
list = get(endpoint).as(LIST_OF_PERSON_TYPE_REF);
assertEquals(4, list.size());
//with sort
list = get(endpoint + "?sort=firstname").as(LIST_OF_PERSON_TYPE_REF);
assertEquals(4, list.size());
//count
Long count = get(endpoint + "/count").as(Long.class);
assertEquals(4, count);
//update a person
person3.lastname = "Webster";
response = RestAssured
.given()
.header("Content-Type", "application/json")
.body(person3)
.put(endpoint)
.andReturn();
assertEquals(202, response.statusCode());
//check that the title has been updated
person3 = get(endpoint + "/" + person3.id.toString()).as(Person.class);
assertEquals(3L, person3.id);
assertEquals("Webster", person3.lastname);
//delete a person
response = RestAssured
.given()
.delete(endpoint + "/" + person3.id.toString())
.andReturn();
assertEquals(204, response.statusCode());
count = get(endpoint + "/count").as(Long.class);
assertEquals(3, count);
//delete all
response = RestAssured
.given()
.delete(endpoint)
.andReturn();
assertEquals(204, response.statusCode());
count = get(endpoint + "/count").as(Long.class);
assertEquals(0, count);
}
private Date yearToDate(int year) {
Calendar cal = new GregorianCalendar();
cal.set(year, 1, 1);
return cal.getTime();
}
private class IntegerAdder {
int cpt = 0;
public void increment() {
cpt++;
}
public int count() {
return cpt;
}
}
}