vertx-mongo-values

Library based on mongo-values and vertx-values that allows you to work with mongodb and vertx using functions and values

License

License

GroupId

GroupId

com.github.imrafaelmerino
ArtifactId

ArtifactId

vertx-mongo-values
Last Version

Last Version

0.2
Release Date

Release Date

Type

Type

jar
Description

Description

vertx-mongo-values
Library based on mongo-values and vertx-values that allows you to work with mongodb and vertx using functions and values
Project URL

Project URL

https://github.com/imrafaelmerino/vertx-mongo-values
Source Code Management

Source Code Management

https://github.com/imrafaelmerino/vertx-mongo-values.git

Download vertx-mongo-values

How to add to project

<!-- https://jarcasting.com/artifacts/com.github.imrafaelmerino/vertx-mongo-values/ -->
<dependency>
    <groupId>com.github.imrafaelmerino</groupId>
    <artifactId>vertx-mongo-values</artifactId>
    <version>0.2</version>
</dependency>
// https://jarcasting.com/artifacts/com.github.imrafaelmerino/vertx-mongo-values/
implementation 'com.github.imrafaelmerino:vertx-mongo-values:0.2'
// https://jarcasting.com/artifacts/com.github.imrafaelmerino/vertx-mongo-values/
implementation ("com.github.imrafaelmerino:vertx-mongo-values:0.2")
'com.github.imrafaelmerino:vertx-mongo-values:jar:0.2'
<dependency org="com.github.imrafaelmerino" name="vertx-mongo-values" rev="0.2">
  <artifact name="vertx-mongo-values" type="jar" />
</dependency>
@Grapes(
@Grab(group='com.github.imrafaelmerino', module='vertx-mongo-values', version='0.2')
)
libraryDependencies += "com.github.imrafaelmerino" % "vertx-mongo-values" % "0.2"
[com.github.imrafaelmerino/vertx-mongo-values "0.2"]

Dependencies

compile (4)

Group / Artifact Type Version
com.github.imrafaelmerino : mongo-values jar 0.5
com.github.imrafaelmerino : vertx-values jar 0.7
org.mongodb : mongodb-driver-reactivestreams jar 1.13.1
org.mongodb : mongodb-driver-sync jar 4.0.0-beta1

test (4)

Group / Artifact Type Version
org.junit.jupiter : junit-jupiter-engine jar 5.6.2
io.vertx : vertx-junit5 jar 4.0.0.Beta1
org.slf4j : slf4j-api jar 1.7.29
org.slf4j : slf4j-nop jar 1.7.29

Project Modules

There are no modules declared in this project.

vertx-mongodb-effect

Build Status codecov

Quality Gate Status Maintainability Rating

Javadocs Maven

Introduction

vertx-mongodb-effect allows us to work with MongoDB following a purely functional and reactive style. It requires to be familiar with vertx-effect. Both vertx-effect and vertx-mongo-effect use the immutable and persistent Json from json-values. Jsons travel across the event bus, from verticle to verticle, back and forth, without being neither copied nor converted to BSON.

Supported types

json-values supports the standard Json types: string, number, null, object, array; There are five number specializations: int, long, double, decimal, and BigInteger. json-values adds support for instants and binary data. It serializes Instants into its string representation according to ISO-8601, and the binary type into a string encoded in base 64.

vertx-mongodb-effect uses mongo-values. It abstracts the processes of encoding to BSON and decoding from BSON. Please find below the BSON types supported and their equivalent types in json-values.

Map<BsonType, Class<?>> map = new HashMap<>();
map.put(BsonType.NULL, JsNull.class);
map.put(BsonType.ARRAY, JsArray.class);
map.put(BsonType.BINARY, JsBinary.class);
map.put(BsonType.BOOLEAN, JsBool.class);
map.put(BsonType.DATE_TIME, JsInstant.class);
map.put(BsonType.DOCUMENT, JsObj.class);
map.put(BsonType.DOUBLE, JsDouble.class);
map.put(BsonType.INT32, JsInt.class);
map.put(BsonType.INT64, JsLong.class);
map.put(BsonType.DECIMAL128, JsBigDec.class);
map.put(BsonType.STRING, JsStr.class);

When defining the mongodb settings, you have to specify the codec registry JsValuesRegistry from mongo-values:

import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import mongovalues.JsValuesRegistry;

MongoClientSettings  settings =
             MongoClientSettings.builder()
                                .applyConnectionString(connString)
                                .codecRegistry(JsValuesRegistry.INSTANCE)
                                .build();

Supported operations

Every method of the MongoDB driver has an associated lambda.

Since vertx-mongodb-effect uses the driver API directly, it can benefit from all its features and methods. It's an advantage over the official vertx-mongodb-client.

Please find below the types and constructors of the most essentials operations:

Count :: λc<JsObj, Long>

import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.CountOptions;

public Count(Supplier<MongoCollection<JsObj>> collectionSupplier,
             CountOptions options
            )

DeleteMany :: λc<JsObj, O>

import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.DeleteOptions;
import com.mongodb.client.result.DeleteResult;

public DeleteMany(Supplier<MongoCollection<JsObj>> collectionSupplier,
                  Function<DeleteResult, O> resultConverter,
                  DeleteOptions options 
                 )
                      

DeleteOne :: λc<JsObj, O>

import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.DeleteOptions;
import com.mongodb.client.result.DeleteResult;

public DeleteOne(Supplier<MongoCollection<JsObj>> collectionSupplier,
                 Function<DeleteResult, O> resultConverter,
                 DeleteOptions options 
                )
                      

FindAll :: λc<FindMessage, JsArray>

import com.mongodb.client.MongoCollection;
import com.mongodb.client.FindIterable;

public FindAll(Supplier<MongoCollection<JsObj>> collectionSupplier,
               Function<FindIterable<JsObj>, JsArray> converter 
              )

FindOne :: λc<FindMessage, JsObj>

import com.mongodb.client.MongoCollection;
import com.mongodb.client.FindIterable;

public FindOne(Supplier<MongoCollection<JsObj>> collectionSupplier,
               Function<FindIterable<JsObj>, JsObj> converter 
              )                 

FindOneAndDelete :: λc<JsObj, JsObj>

import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.FindOneAndDeleteOptions;

public FindOneAndDelete(Supplier<MongoCollection<JsObj>> collectionSupplier,
                        FindOneAndDeleteOptions options
                       ) 

FindOneAndReplace :: λc<UpdateMessage, JsObj>

import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.FindOneAndReplaceOptions;

public FindOneAndReplace(Supplier<MongoCollection<JsObj>> collectionSupplier,
                         FindOneAndReplaceOptions options
                        )   

FindOneAndUpdate :: λc<UpdateMessage, JsObj>

import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.FindOneAndUpdateOptions;

public FindOneAndUpdate(Supplier<MongoCollection<JsObj>> collectionSupplier,
                        FindOneAndUpdateOptions options
                       )

InsertMany :: λc<JsArray, R>

import com.mongodb.client.MongoCollection;
import com.mongodb.client.result.InsertManyResult;
import com.mongodb.client.model.InsertManyOptions;

public InsertMany(Supplier<MongoCollection<JsObj>> collectionSupplier,
                  Function<InsertManyResult, R> resultConverter,
                  InsertManyOptions options
                 )   

InsertOne :: λc<JsObj, R>

import com.mongodb.client.MongoCollection;
import com.mongodb.client.result.InsertOneResult;
import com.mongodb.client.model.InsertOneOptions;

public InsertOne(Supplier<MongoCollection<JsObj>> collectionSupplier,
                 Function<InsertOneResult, R> resultConverter,
                 InsertOneOptions options
                )    

ReplaceOne :: λc<UpdateMessage, O>

import com.mongodb.client.MongoCollection;
import com.mongodb.client.result.UpdateResult;
import com.mongodb.client.model.ReplaceOptions;

public ReplaceOne(Supplier<MongoCollection<JsObj>> collectionSupplier,
                  Function<UpdateResult, O> resultConverter,
                  ReplaceOptions options
                 )   

UpdateMany :: λc<UpdateMessage, O>

import com.mongodb.client.MongoCollection;
import com.mongodb.client.result.UpdateResult;
import com.mongodb.client.model.UpdateOptions

public UpdateMany(Supplier<MongoCollection<JsObj>> collectionSupplier,
                  Function<UpdateResult, O> resultConverter,
                  UpdateOptions options
                 )

UpdateOne :: λc<UpdateMessage, O>

import com.mongodb.client.MongoCollection;
import com.mongodb.client.result.UpdateResult;
import com.mongodb.client.model.UpdateOptions

public UpdateOne(Supplier<MongoCollection<JsObj>> collectionSupplier,
                 Function<UpdateResult, O> resultConverter,
                 UpdateOptions options
                )

Defining modules

Like with vertx-effect, modules deploys verticles and expose lambdas to communicate with them. The typical scenario is to create a module per collection. We can deploy or spawn verticles.

The following modules are just a couple of examples.

We create a module where all the lambdas make read operations and spawn verticles to reach a significant level of parallelization:

import vertx.mongodb.effect.MongoModule;
import vertx.effect.λc;

public class ReadModule extends MongoModule {

    public MyCollectionModule(final Supplier<MongoCollection<JsObj>> collection) {
        super(collection);
    }

    public static λc<FindMessage, Optional<JsObj>> findOne;
    public static λc<FindMessage, JsArray> findAll;
    public static λc<JsObj, Long> count;
    public static λc<JsArray, JsArray> aggregate;

    @Override
    protected void deploy() {}  

    @Override
    protected void initialize() {
        λc<FindMessage, JsObj> findOneLambda = vertxRef.spawn("find_one",
                                                             new FindOne(collection)
                                                            );
        this.findOne = (context,message) -> findOneLambda.apply(context,message)
                                                         .map(Optional::ofNullable);
        this.findAll = vertxRef.spawn("find_all",
                                      new FindAll(collection)
                                     );
        this.count = vertxRef.spawn("count",
                                    new Count(collection)
                                   );

        this.aggregate = vertxRef.spawn("aggregate",
                                        new Aggregate<>(collection,
                                                        Converters.aggregateResult2JsArray
                                                       )
                                       );
    }
}

We create a module where all the lambdas make delete, insert and update operations, and deploy only one instance per verticle.

import vertx.mongodb.effect.MongoModule;
import vertx.effect.λc;

public class MyCollectionModule extends MongoModule {

    public MyCollectionModule(final Supplier<MongoCollection<JsObj>> collection) {
        super(collection);
    }

    public static λc<JsObj, String> insertOne;
    public static λc<JsObj, JsObj> deleteOne;
    public static λc<UpdateMessage, JsObj> replaceOne;
    public static λc<UpdateMessage, JsObj> updateOne;

    @Override
    protected void deploy() {
        this.deploy(INSERT_ONE_ADDRESS,
                    new InsertOne<>(collection,
                                    Converters.insertOneResult2HexId
                                   ),
                   );
        this.deploy(DELETE_ONE_ADDRESS,
                    new DeleteOne<>(collection,
                                    Converters.deleteResult2JsObj
                                   )
                   );

        this.deploy(REPLACE_ONE_ADDRESS,
                    new ReplaceOne<>(collection,
                                     Converters.updateResult2JsObj
                                    )
                   );
        this.deploy(UPDATE_ONE_ADDRESS,
                    new UpdateOne<>(collection,
                                    Converters.updateResult2JsObj
                                   )
                   );
    }  

    @Override
    protected void initialize() {
        this.insertOne = this.trace(INSERT_ONE_ADDRESS);
        this.deleteOne = this.trace(DELETE_ONE_ADDRESS);
        this.replaceOne = this.trace(REPLACE_ONE_ADDRESS);
        this.updateOne = this.trace(UPDATE_ONE_ADDRESS);
    }

    private static final String DELETE_ONE_ADDRESS = "delete_one";   
    private static final String UPDATE_ONE_ADDRESS = "update_one";
    private static final String REPLACE_ONE_ADDRESS = "replace_one";
    private static final String INSERT_ONE_ADDRESS = "insert_one";
    private static final String INSERT_MANY_ADDRESS = "insert_all";
    private static final String DELETE_MANY_ADDRESS = "delete_all";

}

Deploying modules

The verticles RegisterMongoEffectCodecs and RegisterJsValuesCodecs need to be deployed to register the vertx message codecs. Remember that you can't send any message to the event bus. If a message is not supported by Vertx you have to create a MessageCodec.

import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import vertx.mongodb.effect.MongoVertxClient;
import vertx.effect.VertxRef;

// define every timeout if you wanna be reactive
int connectTimeoutMS =  ???;
int socketTimeoutMS = ???;
int serverSelectionTimeoutMS = ???;

String connectionUrl = 
     String.format("mongodb://localhost:27017/?connectTimeoutMS=%s&socketTimeoutMS=%s&serverSelectionTimeoutMS=%s",
                   connectTimeoutMS,
                   socketTimeoutMS,
                   serverSelectionTimeoutMS 
                  );
 
ConnectionString connString = new ConnectionString(connectionUrl);

MongoClientSettings  settings =
             MongoClientSettings.builder()
                                .applyConnectionString(connString)
                                .codecRegistry(JsValuesRegistry.INSTANCE)
                                .build();

// one vertx client per database connection 
MongoVertxClient mongoClient = new MongoVertxClient(settings);

String database = ???;
String collection = ???; 
MyCollectionModule collectionModule = 
          new MyCollectionModule(mongoClient.getCollection(database,
                                                           collection
                                                          )
                                );

VertxRef vertxRef = new VertxRef(vertx);

Quadruple.sequential(vertxRef.deployVerticle(new RegisterJsValuesCodecs()),
                     vertxRef.deployVerticle(new RegisterMongoEffectCodecs()),
                     vertxRef.deployVerticle(mongoClient),
                     vertxRef.deployVerticle(collectionModule)
                     ) 
         .get();

Once everything is up and running, enjoy your lambdas!

BiFunction<Integer,String,Val<Optional<JsObj>>> findByCode = (attempts,code) ->
          MyCollectionModule.findOne
                            .apply(FindMessage.ofFilter(JsObj.of("code",
                                                                 JsStr.of(code)
                                                                )
                                                        ) 
                                   )                    
                            .retryIf(e -> Failures.MONGO_TIMEOUT_PRISM.getOptional
                                                                      .apply(e)
                                                                      .isPresent(),
                                     attempts
                                     )
                            .recoverWith(e -> Cons.success(Optional.empty()));

Publishing events

Since vertx-effect publishes the most critical events into the address vertx-effect-events, it' possible to register consumers to explode that information. You can disable this feature with the Java system property -Dpublish.events=false. Thanks to λc, it's possible to correlate different events that belongs to the same transaction. Go to the vertx-effect doc for further details.

Requirements

Installation

<dependency>
   <groupId>com.github.imrafaelmerino</groupId>
   <artifactId>vertx-mongodb-effect</artifactId>
   <version>0.5</version>
</dependency>

Release process

Every time a tagged commit is pushed into master, a Travis CI build will be triggered automatically and start the release process, deploying to Maven repositories and GitHub Releases. See the Travis conf file .travis.yml for further details. On the other hand, the master branch is read-only, and all the commits should be pushed to master through pull requests.

Versions

Version
0.2
0.1