Objectis


logo

An object-oriented data access API for the Redis cache

Objectis is a Java library that allows you to quickly manage data in Redis in an object-oriented way. Through Objectis, you can easily save, retrieve, list, delete and filter objects instead of Strings or raw data within your Redis cache.


The latest version of Objectis (0.1.8) is still in beta. Use this library at your own risk. You can report issues here.

Contents

Download and import

You can easily import Objectis in your project using Maven or Gradle:

Maven:

<dependency>
  <groupId>com.raylabz</groupId>
  <artifactId>objectis</artifactId>
  <version>0.1.8</version>
</dependency>

Gradle:

implementation 'com.raylabz:objectis:0.1.8'

Alternatively, you can download Objectis as a .jar file:

Download Jar

Guide

Initialization

To use Objectis, you first need to call Objectis.init(). The function can take several parameters:

Objectis.init(); //Initialize for localhost on default port (6379)
Objectis.init("192.168.10.1", 6379); //Initialize for specific IP and port.
Objectis.init("192.168.10.1", 6379, 30, true); //Initialize for specific IP and port, with a timeout and using SSL.
Objectis.init(jedis); //Initialize using a pre-existing Jedis object.

Using objects and classes

Objectis can utilize your existing classes and save their instance in a Redis cache. To enable this, your classes have to follow several rules:

  • Be annotated with the @ObjectisObject annotation.
  • Must extend the Serializable interface.
  • Have an attribute called id of type String, or extend a class that has this field.
  • Have an empty (no-parameter) constructor (access modifier does not matter).

The following code shows an example on how to create and properly annotate a class:

@ObjectisObject
public class Person implements Serializable {

    private String id;
    private String firstName;
    private String lastName;
    private int age;

    public Person(String firstName, String lastName, int age) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }

    private Person() {
    }

    //Setters, getters and other methods...

}

Registering classes

Before you can use your class, you also need to register it with Objectis. Each class needs to be registered only once. This process is necessary as Objectis will check your classes for the above requirements and will index its fields. You can register your class after initializing Objectis, by using the register() method, providing your class as a parameter:

Objectis.register(Person.class);
The register() method will throw a runtime exception (ClassRegistrationException) when the conditions stated above are not met by the class being registered.

Basic operations

Create

You can create objects in Redis using Objectis by using Objectis.create() and providing an instance of your custom class:

Objectis.create(person);

Alternatively, you may also create an item using a custom ID, by providing your object and the ID. This will ignore the ID field of your object and save it using the ID you provide to the method.

Objectis.create(person, id);
Creating an item with the same ID as another existing item of the same class will replace the existing item.
Create all

You can also store multiple objects in a single call, using the Objectis.createAll() method, providing a list of items to create:

Objectis.createAll(person, listOfItems);
Get

You can retrieve objects from Redis by using Objectis.get() and providing the class of the object and an ID:

Person person = Objectis.get(Person.class, id);

The get() method will return null when the object does not exist. If Objectis fails to fetch the object from Redis for any reason, the method will throw an OperationFailedException.

Get many

You can retrieve multiple objects of the same type from Redis by using Objectis.getMany() and providing the class of the object a list of IDs for the objects to retrieve.

List<Person> person = Objectis.getMany(Person.class, myIDs);

Or by using a Java varargs array:

List<Person> person = Objectis.getMany(Person.class, "id1", "id2", "id3");
Exists

You can check if an object exists on Redis by providing its class and an ID:

if (Objectis.exists(Person.class, id)) {
    //TODO - Document exists...
}
else {
    //TODO - Document does not exist...
}
Update

You can update objects by using Objectis.update() and providing your object:

Objectis.update(person);
List

Objectis supports fetching objects of a given type using the Objectis.list() method.

List<Person> peopleList = Objectis.list(Person.class);
Delete

You can delete an object from Redis by using the Objectis.delete() method.

Objectis.delete(person);

Alternatively, you may also use the object class and an object's ID:

Objectis.delete(Person.class, id);
Delete all

To delete multiple items from Redis, you can use Objectis.deleteAll(), and provide a list of items to delete:

Objectis.deleteAll(listOfItems);

Alternatively, you can also provide the class of the items being deleted and list of IDs for items to delete:

Objectis.deleteAll(Person.class, ids);

Filtering

Objectis allows you to easily query and filter results based on your class fields using the filter() method. This method expects a class as a parameter and will return an ObjectisFilterable object which can be used to filter your objects:

final ObjectisFilterable<Person> filter = Objectis.filter(Person.class);

Items can be filtered using several methods. It is preferable to chain these methods in order to make the code easier to read. The following example shows how to obtain an object of type Person, for which the field firstName is equal to "John" and the age is greater than 10. The results are ordered by the field "age" and a total of 5 results are retrieved.

final ObjectisFilterable<Person> filter = Objectis.filter(Person.class)
        .whereEqualTo("firstName", "John")
        .whereGreaterThan("age", 10)
        .orderBy("age")
        .limit(5);

You can get the result of a filter by using the fetch() method. This returns an ObjectisQueryResult:

ObjectisQueryResult<Person> result = Objectis.filter(Person.class)
        .whereEqualTo("firstName", "John")
        .whereGreaterThan("age", 10)
        .orderBy("age")
        .limit(5)
        .fetch();

Using this result, you can get the items using getItems():

Collection<Person> items = result.getItems();

You can also retrieve the ID of the last document/item retrieved:

String lastDocumentID = result.getLastDocumentID();

In most cases, it may be easier to directly fetch and get the items in the same statement:

Collection<Person> items = Objectis.filter(Person.class)
        .whereEqualTo("name", "N2")
        .fetch()
        .getItems();
Filters reference

The following is a table of filters, ordering, limits etc. which can be applied to a query via the filter() method:

Method Use Params
whereEqualTo Returns objects that have a field with a value equal to the given value. Field name, value
whereLessThan Returns objects that have a field with a value less than the given value. Field name, value
whereLessThanOrEqualTo Returns objects that have a field with a value less than or equal to the given value. Field name, value
whereGreaterThan Returns objects that have a field with a value greater than the given value. Field name, value
whereGreaterThanOrEqualTo Returns objects that have a field with a value greater than or equal to the given value. Field name, value
whereArrayContains Returns objects that have a field (array) containing the given value. Field name, value
whereArrayContainsAny Returns objects that have a field (array) containing any of a given list of values. Field name, values
orderBy Returns objects ordered by a specific field. Field name, Direction of order
limit Limits the number of results returned by the query. Limit (number)
offset Offsets the query's start position by a given amount. Offset(number)
fetch Fetches the results of the query. -
Fields must implement the Comparable interface in order to use the following filter methods:
  • whereLessThan
  • whereLessThanOrEqualTo
  • whereGreaterThan
  • whereGreaterThanOrEqualTo
This is applied by default on basic data types and their boxed counterparts.
Considerations on filtering

Filtering data on Redis is a computationally expensive process, especially when large numbers of items need to be processed and filtered. Unfortunately, due to the nature of caching in general, it is not possible to run queries as efficiently as in other data storage formats such as SQL databases or even NoSQL data stores. Even though Objectis is optimized to handle large numbers of items through multithreading where appropriate, there are hard limits to the scalability of this approach.

For these reasons, we encourage the use of Collections. Collections are a feature of Objectis which allows us to create custom collections of items that exist within the cache. A collection can include only a subset of the items of a particular class and therefore make it easier and more efficient to handle scenarios where operations need to be performed on sets of data. Where necessary, collections can be further filtered to obtain fine-grained results.


Collections

Objectis collections are custom collections of objects you can create, which can contain items of a particular class. Each collection has a unique name that identifies the subset of items that it stores. The goal of collections is to group items of a particular class together to make it easier to find similar items and to reduce processing times.

An example would be the need to store a special collection of Person objects whose age is above 18 - this collection could be named adults. Storing items in this collection allows us to quickly refer to all Persons who are adults and run operations only on a subset of items of this type, instead of all objects of type Person. Therefore, collections of items can be operated on and filtered much more efficiently compared to normal lists.

Reference a collection

To reference a collection by class and name, you can use Objectis.collection(), providing the type of items this collection stores and the name of the collection. For new collections, you do not need to explicitly create the collection and you can start adding items directly. Calling Objectis.collection() will return an ObjectisCollection object, which you can later use to run operations on the data inside the collection:

ObjectisCollection<Person> adults = Objectis.collection(Person.class, "adults");
The name of each collection must be unique.
Add to collection

You can add an item to a collection using the add() method:

adults.add(person);

The potential of collections is based on conditionally adding items to the collection based on each scenario. In our example, we would add a person to the adults collection, only if their age was >=18:

if (person.getAge() >= 18) {
   adults.add(person);
}
Add all to collection

You can also batch add a list of items to the collection using the addAll() method, providing a list of items:

adults.addAll(listOfPersons);
Adding items to a collection only adds a reference to the original item. Therefore, changes made to any item within a collection are going to be visible globally.
List items of collection

To retrieve a list of items inside the collection, you can use list():

List<Person> adults = Objectis.collection(Person.class, "adults").list();
Delete from collection

To delete an item from the collection you can use the delete() method, providing either an object or the item's ID as a String:

adults.delete(person);
adults.delete(personID);
Delete all

You can also delete multiple items using deleteAll(). In this method, you can provide either a list of items to delete or an array of IDs as Strings:

adults.deleteAll(listOfPersons);
adults.deleteAll("id1", "id2", "id3");
Deleting an item from a collection only removes the item from the collection. To delete the item itself, use Objectis.delete().
Contains

You can check if a collection contains an item using either an object or the item's ID as a string, by calling contains():

if (adults.contains(person)) {
   //TODO - Collection contains the item
}
else {
   //TODO - Collection does NOT contain the item.
}

or

if (adults.contains(itemID)) {
   //TODO - Collection contains the item
}
else {
   //TODO - Collection does NOT contain the item.
}
Collection filters

You may also want to filter items within the collection. Filtering in collections works in the same way as mentioned in the Filtering section. To filter collections, use the filter() method and the subsequent filtering methods provided by the filterable object returned:

List<Person> items = adults.filter()
        .whereEqualTo("firstName", "John")
        .limit(5)
        .fetch()
        .getItems();

Keys

Object keys

In some cases, you may need to manage your data using the Redis CLI client or do it manually from another client. In such cases you need to be able to form the keys manually, without Objectis' support.

Objectis creates objects keys based on the class of your object and its ID. For example, when an object is of class Person, and has an ID "myID", Objectis will form the following key and store your object there:

Person/myID

The Redis command to retrieve this object would be:

GET Person/MyID

If your class exists within a package, the key will be formed based on the fully evaluated name of the class (including the package). For example, if your Person class exists inside the package com.mypackage.model, Objectis will form the following key:

com.mypackage.model.Person/myID
Your custom client or CLI will retrieve data in raw bytes, which you will then need to manually de-serialize if you are not using Objectis.
Class list keys

To track the objects of each class, Objectis uses unordered sets containing their IDs. The keys to these sets are created based on the class name. For example, the fully evaluated class name can be used to obtain a set of all saved instances of that class:

com.mypackage.model.Person

Redis command to retrieve all instance of class Person:

SMEMBERS com.mypackage.model.Person
Collection keys

For collections, Objectis uses unordered sets containing item IDs to track the objects in each collection. The key of each collection is formed using the class name and the collection name (e.g. "evens"), separated by a colon:

com.mypackage.model.Person:evens

Using the key in the CLI to obtain the IDs of the members in the collection:

SMEMBERS com.mypackage.model.Person:evens


Documentation

View the documentation.


License

Objectis is released under the Apache License.


Source code

You can find the source code at the project's repository here.


Bug reporting

Please report bugs here.