DEV Community

Simer Plaha
Simer Plaha

Posted on • Updated on

Functions as Queries @ SwayDB

Conditional updates & deletes are performed by submitting any Java, Scala, Kotlin or any native JVM code/function. There is no query language.

Being just JVM functions/code we can

  • Be more expressive in writing our query logic as we are not restricted by rules of a query language.
  • Use external Java libraries within our queries like Joda-Time for time management, Jackson or Circe for JSON parsing etc.
  • Write test-cases using familiar testing libraries like JUnit, kotlin.test, ScalaTest etc.
  • Type-safe - catch errors during compile time.
  • Fast - No runtime parsing & compiling performance cost. See these benchmarks that show 482,000+ writes per second. Function updates follow the same write path as any basic write and do not incur any extra cost.

Example

A simple example which creates a Map<String, Double> that stores productName as key and price as value. You can use custom classes (Product.java for values) here but for simplicity String and Double is used.

Update logic: If a product has less than 2 days left to expiration, give a generous 50% discount!

//Our function that implements the update logic. 
//You can also use PureFunction.OnValue or PureFunction.OnKey.
PureFunction.OnKeyValue<String, Double, Return.Map<Double>> discount =  
  (String product, Double price, Optional<Deadline> deadline) -> {
    //if there are less than 2 days to expiry then apply discount.  
    if (deadline.isPresent() && deadline.get().timeLeft().minusDays(2).isNegative()) {  
      double discountedPrice = price * 0.50; //50% discount.
      if (discountedPrice <= 10) { //If the price is below $10
        //return Return.expire(Duration.ZERO); //expire it
        return Return.remove(); //or remove the product
      } else {  
        return Return.update(discountedPrice); //else update with the discounted price
      }  
    } else {
      return Return.nothing(); //else do nothing.
    }
  };  

//create our map with functions enabled.    
Map<String, Double, PureFunction<String, Double, Return.Map<Double>>> products =  
  MapConfig.functionsOn(stringSerializer(), doubleSerializer())  
    .registerFunction(discount) //register the discount function
    .get();  

//insert two products that expire after a day. 
products.put("MacBook Pro", 2799.00, Duration.ofDays(1));  
products.put("Tesla", 69275.0, Duration.ofDays(1));  

//apply the discount function.
products.applyFunction("MacBook Pro", "Tesla", discount);  
Enter fullscreen mode Exit fullscreen mode

We can print the content of the Map with the following

products.stream().forEach(System.out::println);    
Enter fullscreen mode Exit fullscreen mode

Before the discount is applied will print

KeyVal(MacBook Pro, 2799.0)
KeyVal(Tesla, 69275.0)

After the discount is applied prints

KeyVal(MacBook Pro, 1399.5)
KeyVal(Tesla, 34637.5)

See the above example on GitHub - DiscountApp.java.

What is function ID?

Each PureFunction type also implement a String id value which gets stored in the database. Currently the id is defaulted to this.getClass().getName(); which can be overridden by your function.

Note & TODOs

  • The function itself is not serialised. Only the function's id gets serialised and stored in the database. TODO - Serialisable functions.
  • .mightContainFunction can be used to check if a function is in use. TODO - Automatic function removal.
  • TODO - stronger types when registering and applying function.

Useful links

Top comments (0)