Awesome
🍀 Redis OM Spring Skeleton App
Redis OM Spring provides powerful repository and custom object-mapping abstractions built on top of the powerful Spring Data Redis (SDR) framework. This app is a simple started application using Redis OM Spring to map, manipulate and query some simple Spring Data models backed by RedisJSON documents.
Check out our workshop video for this project on YouTube.
Overview
The app provides a simple model Person
:
id
: An autogeneratedString
using ULIDsfirstName
: AString
representing their first or given name.lastName
: AString
representing theirlast or surname.age
: AnInteger
representing their age in years.personalStatement
: AString
representing a text personal statement containing facts or other biographical information.homeLoc
: Aorg.springframework.data.geo.Point
representing the geo coordinates.address
: An entity of typeAddress
representing the Person's postal address.skills
: ASet<String>
, representing a collection of Strings representing skills the person has.
Mapping Entities to JSON
The Person
model is annotated with @Document
, this enables the models to be saved as RedisJSON documents.
The individuals fields that will be used to create the entity search index are annotated with @Indexed
or
@Searchable
(for full-text search capable text fields).
@Document
public class Person {
@Id @Indexed
private String id;
@Indexed
private String firstName;
@Indexed
private String lastName;
@Indexed
private Integer age;
@Searchable
private String personalStatement;
@Indexed
private Point homeLoc;
@Indexed
private Address address;
@Indexed
private Set<String> skills;
}
For example, saving the above object using a repository (see below) you'll get a JSON document under the Redis key com.redis.om.skeleton.models.Person:01FXD97WTHNYFQEA56YQ27BNQ6
:
{
"id": "01FXD97WTHNYFQEA56YQ27BNQ6",
"firstName": "Elizabeth",
"lastName": "Olsen",
"age": 32,
"personalStatement": "You Guys Know I Can Move Things With My Mind, Right?",
"homeLoc": "40.6976701,-74.2598641",
"address": {
"houseNumber": "20",
"street": "W 34th St",
"city": "New York",
"state": "NY",
"postalCode": "10001",
"country": "US"
},
"skills": [
"loyalty",
"magic"
]
}
The resulting JSON has a generated value for the id
field using a ULIDs. Note that
the homeLoc
field is mapped to the lon-lat format expected by Redis, and the set of skills
is mapped into a JSON array.
The SpringBoot App
Use the @EnableRedisDocumentRepositories
annotation to scan for @Document
annotated Spring models,
Inject repositories beans implementing RedisDocumentRepository
which you can use for CRUD operations and custom queries (all by declaring Spring Data Query Interfaces):
@SpringBootApplication
@EnableRedisDocumentRepositories(basePackages = "com.redis.om.skeleton.*")
public class SkeletonApplication {
}
🧰 The Repository
Redis OM Spring data repository's goal, like other Spring Data repositories, is to significantly reduce the amount of boilerplate code required to implement data access. Simply create a Java interface
that extends RedisDocumentRepository
that takes the domain class to manage as well as the ID type of the domain class as type arguments. RedisDocumentRepository
extends Spring Data's PagingAndSortingRepository
.
Declare query methods on the interface. You can both, expose CRUD methods or create declarations for complex queries that Redis OM Spring will fullfil at runtime:
public interface PeopleRepository extends RedisDocumentRepository<Person, String> {
// Find people by age range
Iterable<Person> findByAgeBetween(int minAge, int maxAge);
// Draws a circular geofilter around a spot and returns all people in that radius
Iterable<Person> findByHomeLoc(Point point, Distance distance);
// Find people by their first and last name
Iterable<Person> findByFirstNameAndLastName(String firstName, String lastName);
// Performs full text search on a person's personal Statement
Iterable<Person> searchByPersonalStatement(String text);
// ...
}
The repository proxy has two ways to derive a store-specific query from the method name:
- By deriving the query from the method name directly.
- By using a manually defined query using the
@Query
or@Aggregation
annotations.
CRUD Operations
You can inject a Repository and perform the basic CRUD operations as well as any custom queries you've defined:
@Autowired
PeopleRepository repo;
@PostMapping("new")
Person create(@RequestBody Person newPerson) {
return repo.save(newPerson);
}
🚤 Querying with Entity Streams
Redis OM Spring Entity Streams provides a Java 8 Streams interface to Query Redis JSON documents using RediSearch. Entity Streams allow you to process data in a typesafe declarative way similar to SQL statements. Streams can be used to express a query as a chain of operations.
Entity Streams in Redis OM Spring provide the same semantics as Java 8 streams. Streams can be made of Redis Mapped entities (@Document
) or one or more properties of an Entity. Entity Streams progressively build the query until a terminal operation is invoked (such as collect
). Whenever a Terminal operation is applied to a Stream, the Stream cannot accept additional operations to its pipeline and it also means that the Stream is started.
Let's start with a simple example, a Spring @Service
which includes EntityStream
to query for instances of the mapped class Person
:
package com.redis.om.skeleton.services;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.redis.om.skeleton.models.Person;
import com.redis.om.skeleton.models.Person$;
import com.redis.om.spring.search.stream.EntityStream;
@Service
public class PeopleService {
@Autowired
EntityStream entityStream;
// Find all people
public Iterable<Person> findAllPeople(int minAge, int maxAge) {
return entityStream //
.of(Person.class) //
.collect(Collectors.toList());
}
}
The EntityStream
is injected into the PeopleService
using @Autowired
. We can then get a stream for Person
objects by using entityStream.of(Person.class)
. At this point the stream represents the equivalent of a SELECT * FROM Person
on a relational database. The call to collect
will then execute the underlying query and return a collection of all Person
objects in Redis.
👭 Entity Meta-model
To produce more elaborate queries, you're provided with a generated meta-model, which is a class with the same name as your model but ending with a dollar sign. In the
example below, our entity model is Person
therefore we get a meta-model named Person$
. With the meta-model you have access to the operations related to the
underlying search engine field. For example, in the example we have an age
property which is an integer. Therefore our metamodel has an AGE
property which has
numeric operations we can use with the stream's filter
method such as between
.
// Find people by age range
public Iterable<Person> findByAgeBetween(int minAge, int maxAge) {
return entityStream //
.of(Person.class) //
.filter(Person$.AGE.between(minAge, maxAge)) //
.sorted(Person$.AGE, SortOrder.ASC) //
.collect(Collectors.toList());
}
In this example we also make use of the Streams sorted
method to declare that our stream will be sorted by the Person$.AGE
in ASC
ending order.
💾 Get the Source Code
Clone the repository from GitHub:
$ git clone https://github.com/redis-developer/redis-om-spring-skeleton-app
$ cd redis-om-spring-skeleton-app
🚀 Launch Redis
🥞Redis Stack on Docker Locally
Redis OM Spring relies on the power of the [RediSearch][redisearch-url] and [RedisJSON][redis-json-url] modules. We have provided a docker compose YAML file for you to quickly get started. To launch the docker compose application, on the command line (or via Docker Desktop), clone this repository and run (from the root folder):
docker compose up
This launches Redis Stack; Redis Stack Server on port 6379, and Redis Insight 8001.
🌥️Redis Cloud
If you're using Redis Enterprise Cloud, you'll need the hostname, port number, and password for your database. Use these to set the application.properties
configuration like this:
spring.data.redis.host=<host>
spring.data.redis.port=<port>
spring.data.redis.password=<password>
spring.data.redis.username=<username>
For example if your Redis Enterprise Cloud database is at port 9139
on host enterprise.redis.com
and your password is 5uper53cret
then you'd set REDIS_OM_URL
as follows:
spring.data.redis.host=enterprise.redis.com
spring.data.redis.port=9139
spring.data.redis.password=5uper53cret
spring.data.redis.username=default
📝 Prerequisites
- Java 17 or higher
- Spring Boot 3.0.2
- A Redis database with the RediSearch module version TODO or higher installed. We've provided a
docker-compose.yml
with Redis Stack for this. You can also sign up for a free 30Mb database with Redis Enterprise Cloud - be sure to check the box to configure a Redis Stack instance, follow this guide. - curl, or Postman - to send HTTP requests to the application. We'll provide examples using curl in this document.
- Optional: RedisInsight, a free data visualization and database management tool for Redis. When downloading RedisInsight, be sure to select version 2.x.
🏃Run the App
./mvnw spring-boot:run
🧭Interact with the API
You can interact with the API directly with CURL or through the Swagger interface.