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

Frequently Asked Questions 5
What annotations does Spring Data FalkorDB support?

It supports JPA-style annotations including @Node, @Relationship, @Id, @Property, and @Query for custom Cypher queries. Use @EnableFalkorDBRepositories to activate auto-configuration.

How do I define a repository for my graph entities?

Create an interface extending FalkorDBRepository<T, ID> where T is your entity class annotated with @Node and ID is the identifier type. Spring Data automatically provides CRUD operations.

Does Spring Data FalkorDB support derived query methods?

Yes, it fully supports Spring Data derived query methods like findByName, findByAgeGreaterThan, findByNameContaining, etc. The framework automatically generates the appropriate Cypher queries.

What dependencies do I need in my Maven project?

Add spring-data-falkordb (groupId: org.springframework.data) and jfalkordb (groupId: com.falkordb) to your pom.xml. Check the repository for the latest versions.

Does it support transactions?

Yes, Spring Data FalkorDB includes built-in transaction support via FalkorDBTransactionManager. Enable it with @EnableTransactionManagement and use @Transactional annotations on your service methods.