Skip to main content

Spring Data FalkorDB

This page describes how to integrate FalkorDB with Spring Data using spring-data-falkordb.

Overview

Spring Data FalkorDB provides JPA-style object-graph mapping for FalkorDB, enabling developers to use familiar Spring Data patterns and annotations to work with graph databases. This library makes it easy to build high-performance graph-based applications using Spring's data access framework.

Key Features

  • JPA-style Annotations: Use familiar @Node, @Relationship, @Id, @Property annotations
  • Repository Abstractions: Implement FalkorDBRepository<T, ID> for automatic CRUD operations
  • Derived Query Methods: Full support for Spring Data query methods like findByName, findByAgeGreaterThan, etc.
  • Custom Queries: Write Cypher queries with @Query annotation and named parameters
  • Auto-Configuration: Enable repositories with @EnableFalkorDBRepositories
  • Object-Graph Mapping: Automatic conversion between Java objects and FalkorDB graph structures
  • Transaction Support: Built on Spring's robust transaction management
  • High Performance: Leverages FalkorDB's speed with the official JFalkorDB Java client

Getting Started

Installation

Add the following dependencies to your project:

Maven

<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-falkordb</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>

<dependency>
<groupId>com.falkordb</groupId>
<artifactId>jfalkordb</artifactId>
<version>0.5.1</version>
</dependency>

Gradle

dependencies {
implementation 'org.springframework.data:spring-data-falkordb:1.0.0-SNAPSHOT'
implementation 'com.falkordb:jfalkordb:0.5.1'
}

Entity Mapping

Define your graph entities using Spring Data annotations:

@Node(labels = {"Person", "Individual"})
public class Person {

@Id
@GeneratedValue
private Long id;

@Property("full_name") // Maps to "full_name" property in FalkorDB
private String name;

private String email;
private int age;

@Relationship(type = "KNOWS", direction = Relationship.Direction.OUTGOING)
private List<Person> friends;

@Relationship(type = "WORKS_FOR", direction = Relationship.Direction.OUTGOING)
private Company company;

// Constructors, getters, and setters...
}

@Node("Company")
public class Company {

@Id
@GeneratedValue
private Long id;

private String name;
private String industry;

@Property("employee_count")
private int employeeCount;

@Relationship(type = "EMPLOYS", direction = Relationship.Direction.INCOMING)
private List<Person> employees;

// Constructors, getters, and setters...
}

Repository Interface

Create repository interfaces extending FalkorDBRepository:

public interface PersonRepository extends FalkorDBRepository<Person, Long> {

// Derived query methods (automatically implemented)
Optional<Person> findByName(String name);
List<Person> findByAgeGreaterThan(int age);
List<Person> findByEmail(String email);
List<Person> findByNameAndAgeGreaterThan(String name, int age);
List<Person> findByNameOrEmail(String name, String email);
Page<Person> findByAgeGreaterThan(int age, Pageable pageable);

// Count and existence queries
long countByAge(int age);
boolean existsByEmail(String email);

// Custom Cypher queries with named parameters
@Query("MATCH (p:Person)-[:KNOWS]->(f:Person) WHERE p.name = $name RETURN f")
List<Person> findFriends(@Param("name") String name);

@Query("MATCH (p:Person) WHERE p.age > $minAge AND p.age < $maxAge RETURN p")
List<Person> findByAgeRange(@Param("minAge") int minAge, @Param("maxAge") int maxAge);
}

Configuration

Configure the FalkorDB connection in your Spring application:

@Configuration
@EnableFalkorDBRepositories
public class FalkorDBConfig {

@Bean
public FalkorDBClient falkorDBClient() {
Driver driver = FalkorDB.driver("localhost", 6379);
return new DefaultFalkorDBClient(driver, "social");
}

@Bean
public FalkorDBTemplate falkorDBTemplate(FalkorDBClient client,
FalkorDBMappingContext mappingContext,
FalkorDBEntityConverter converter) {
return new FalkorDBTemplate(client, mappingContext, converter);
}
}

Service Usage

Use repositories and templates in your service classes:

@Service
@Transactional
public class PersonService {

@Autowired
private PersonRepository personRepository;

@Autowired
private FalkorDBTemplate falkorDBTemplate;

public Person createPerson(String name, String email) {
Person person = new Person(name, email);
return personRepository.save(person);
}

public List<Person> findYoungAdults() {
return personRepository.findByAgeBetween(18, 30);
}

public List<Person> findConnectedPeople(int minAge) {
String cypher = """
MATCH (p:Person)-[:KNOWS]-(friend:Person)
WHERE p.age > $minAge
RETURN p, friend
""";
Map<String, Object> params = Collections.singletonMap("minAge", minAge);
return falkorDBTemplate.query(cypher, params, Person.class);
}
}

Supported Annotations

@Node

Marks a class as a graph node entity:

@Node("Person")                          // Single label
@Node(labels = {"Person", "Individual"}) // Multiple labels
@Node(primaryLabel = "Person") // Explicit primary label

@Id

Marks the entity identifier:

@Id
private String customId; // Assigned ID

@Id
@GeneratedValue
private Long id; // FalkorDB internal ID

@Id
@GeneratedValue(UUIDStringGenerator.class)
private String uuid; // Custom generator

@Property

Maps fields to graph properties:

@Property("full_name")
private String name; // Maps to "full_name" property

private String email; // Maps to "email" property (default)

@Interned

Marks string properties as low-cardinality, applying FalkorDB's intern() function to optimize storage:

@Interned
private String status; // Uses intern() - ideal for limited values like "ACTIVE", "INACTIVE"

@Interned
private String country; // Uses intern() - ideal for country codes "US", "UK", "CA"

@Interned
private String category; // Uses intern() - ideal for categories like "SPORTS", "NEWS"

The @Interned annotation is useful for string properties that have a limited set of possible values (low cardinality). When a property is marked with @Interned, FalkorDB's intern() function is automatically applied when writing to the database, which keeps only a single copy of frequently repeated string values, optimizing storage and query performance.

Use cases:

  • Status codes (ACTIVE, INACTIVE, PENDING)
  • Country/region codes
  • Categories and types
  • Enum-like string values
  • Any string with a limited vocabulary

@Relationship

Maps relationships between entities:

@Relationship(type = "KNOWS", direction = Relationship.Direction.OUTGOING)
private List<Person> friends;

@Relationship(type = "WORKS_FOR", direction = Relationship.Direction.OUTGOING)
private Company company;

@Relationship(type = "EMPLOYS", direction = Relationship.Direction.INCOMING)
private List<Person> employees;

Repository Query Methods

Spring Data FalkorDB supports two types of queries:

1. Derived Query Methods (Automatically Implemented)

Define methods following Spring Data naming conventions, and the implementation is generated automatically:

Query Keywords

  • findBy...: Find entities matching criteria
  • countBy...: Count entities matching criteria
  • existsBy...: Check if entities exist matching criteria
  • deleteBy...: Delete entities matching criteria
  • findFirstBy... / findTopNBy...: Limit results

Supported Comparison Operations

// Equality
findByName(String name)
findByNameNot(String name)

// Comparison
findByAgeGreaterThan(int age)
findByAgeGreaterThanEqual(int age)
findByAgeLessThan(int age)
findByAgeLessThanEqual(int age)

// String operations
findByNameContaining(String substring) // *substring*
findByNameStartingWith(String prefix) // prefix*
findByNameEndingWith(String suffix) // *suffix
findByNameLike(String pattern) // Custom pattern
findByNameNotContaining(String substring)

// Null checks
findByEmailIsNull()
findByEmailIsNotNull()

// Boolean
findByActiveTrue()
findByActiveFalse()

// Collections
findByAgeIn(Collection<Integer> ages)
findByAgeNotIn(Collection<Integer> ages)

// Logical operations
findByNameAndAge(String name, int age) // AND condition
findByNameOrEmail(String name, String email) // OR condition

// Sorting and pagination
findByAgeGreaterThan(int age, Sort sort)
findByAgeGreaterThan(int age, Pageable pageable)

// Limiting results
findFirstByOrderByCreatedAtDesc()
findTop10ByOrderByAgeDesc()

2. Custom Cypher Queries with @Query

Write custom Cypher queries for complex operations:

public interface PersonRepository extends FalkorDBRepository<Person, Long> {

// Using named parameters
@Query("MATCH (p:Person)-[:KNOWS]->(f:Person) "
+ "WHERE p.name = $name RETURN f")
List<Person> findFriends(@Param("name") String name);

// Using indexed parameters
@Query("MATCH (p:Person) WHERE p.age > $0 RETURN p")
List<Person> findOlderThan(int age);

// Count query
@Query(value = "MATCH (p:Person)-[:WORKS_FOR]->(c:Company) "
+ "WHERE c.name = $company RETURN count(p)",
count = true)
long countEmployees(@Param("company") String company);

// Exists query
@Query(value = "MATCH (p:Person {email: $email}) RETURN count(p) > 0",
exists = true)
boolean emailExists(@Param("email") String email);

// Write query (creates/updates data)
@Query(value = "MATCH (p:Person {id: $id}) "
+ "SET p.lastLogin = $time",
write = true)
void updateLastLogin(@Param("id") Long id, @Param("time") LocalDateTime time);
}

Query Method Examples

// Simple equality
List<Person> people = repository.findByName("John");

// Comparison
List<Person> adults = repository.findByAgeGreaterThanEqual(18);

// String matching
List<Person> smiths = repository.findByNameEndingWith("Smith");

// Logical AND/OR
List<Person> results = repository.findByNameAndAgeGreaterThan("John", 25);
List<Person> results = repository.findByNameOrEmail("John", "john@example.com");

// Null checks
List<Person> noEmail = repository.findByEmailIsNull();

// Collections
List<Person> youngPeople = repository.findByAgeIn(Arrays.asList(18, 19, 20));

// Counting and existence
long count = repository.countByAge(25);
boolean exists = repository.existsByEmail("test@example.com");

// Sorting
List<Person> sorted = repository.findByAgeGreaterThan(20, Sort.by("name").ascending());

// Pagination
Page<Person> page = repository.findByAgeGreaterThan(18, PageRequest.of(0, 10));

// Limiting
Optional<Person> youngest = repository.findFirstByOrderByAgeAsc();
List<Person> oldest = repository.findTop5ByOrderByAgeDesc();

// Delete
repository.deleteByAge(0); // Delete all with age = 0

Twitter Integration Example

The library includes a comprehensive Twitter-like integration test that demonstrates real-world usage patterns. This example creates a social graph with users, tweets, follows, and hashtags.

Entity Examples

TwitterUser Entity

@Node(labels = { "User", "TwitterUser" })
public class TwitterUser {
@Id @GeneratedValue
private Long id;

@Property("username") private String username;
@Property("display_name") private String displayName;
@Property("email") private String email;
@Property("bio") private String bio;
@Property("follower_count") private Integer followerCount;
@Property("verified") private Boolean verified;
@Property("created_at") private LocalDateTime createdAt;

@Relationship(value = "FOLLOWS", direction = OUTGOING)
private List<TwitterUser> following;

@Relationship(value = "POSTED", direction = OUTGOING)
private List<Tweet> tweets;
}

Tweet Entity

@Node(labels = { "Tweet" })
public class Tweet {
@Id @GeneratedValue
private Long id;

@Property("text") private String text;
@Property("created_at") private LocalDateTime createdAt;
@Property("like_count") private Integer likeCount;
@Property("retweet_count") private Integer retweetCount;

@Relationship(value = "POSTED", direction = INCOMING)
private TwitterUser author;

@Relationship(value = "HAS_HASHTAG", direction = OUTGOING)
private List<Hashtag> hashtags;
}

Advanced Configuration

Connection Pool Settings

@Bean
public FalkorDBClient falkorDBClient() {
Driver driver = FalkorDB.driver("localhost", 6379);
// Configure connection pool if needed
return new DefaultFalkorDBClient(driver, "myapp");
}

Custom Converters

@Configuration
public class FalkorDBConfig {

@Bean
public FalkorDBCustomConversions customConversions() {
return new FalkorDBCustomConversions(Arrays.asList(
new LocalDateTimeToStringConverter(),
new StringToLocalDateTimeConverter()
));
}
}

Transaction Configuration

@Configuration
@EnableTransactionManagement
public class FalkorDBTransactionConfig {

@Bean
public FalkorDBTransactionManager transactionManager(FalkorDBClient client) {
return new FalkorDBTransactionManager(client);
}
}

Reference