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,@Propertyannotations - 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 
@Queryannotation 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 criteriacountBy...: Count entities matching criteriaexistsBy...: Check if entities exist matching criteriadeleteBy...: Delete entities matching criteriafindFirstBy.../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);
    }
}