@@ -14,7 +14,9 @@ Spring Data FalkorDB provides JPA-style object-graph mapping for [FalkorDB](http
1414
1515- ** 🏷️ JPA-style Annotations** : Use familiar ` @Node ` , ` @Relationship ` , ` @Id ` , ` @Property ` annotations
1616- ** 🔧 Repository Abstractions** : Implement ` FalkorDBRepository<T, ID> ` for automatic CRUD operations
17- - ** 🔍 Query Method Generation** : Support for ` findByName ` , ` findByAgeGreaterThan ` , etc.
17+ - ** 🔍 Derived Query Methods** : Full support for Spring Data query methods like ` findByName ` , ` findByAgeGreaterThan ` , etc.
18+ - ** 📝 Custom Queries** : Write Cypher queries with ` @Query ` annotation and named parameters
19+ - ** ⚙️ Auto-Configuration** : Enable repositories with ` @EnableFalkorDBRepositories `
1820- ** 🔗 Object-Graph Mapping** : Automatic conversion between Java objects and FalkorDB graph structures
1921- ** 💳 Transaction Support** : Built on Spring's robust transaction management
2022- ** ⚡ High Performance** : Leverages FalkorDB's speed with the official JFalkorDB Java client
@@ -105,17 +107,24 @@ Create repository interfaces extending `FalkorDBRepository`:
105107``` java
106108public interface PersonRepository extends FalkorDBRepository<Person , Long > {
107109
110+ // Derived query methods (automatically implemented)
108111 Optional<Person > findByName (String name );
109-
110112 List<Person > findByAgeGreaterThan (int age );
111-
112113 List<Person > findByEmail (String email );
113-
114+ List<Person > findByNameAndAgeGreaterThan (String name , int age );
115+ List<Person > findByNameOrEmail (String name , String email );
114116 Page<Person > findByAgeGreaterThan (int age , Pageable pageable );
115117
118+ // Count and existence queries
116119 long countByAge (int age );
117-
118120 boolean existsByEmail (String email );
121+
122+ // Custom Cypher queries with named parameters
123+ @Query (" MATCH (p:Person)-[:KNOWS]->(f:Person) WHERE p.name = $name RETURN f" )
124+ List<Person > findFriends (@Param (" name" ) String name );
125+
126+ @Query (" MATCH (p:Person) WHERE p.age > $minAge AND p.age < $maxAge RETURN p" )
127+ List<Person > findByAgeRange (@Param (" minAge" ) int minAge , @Param (" maxAge" ) int maxAge );
119128}
120129```
121130
@@ -232,41 +241,138 @@ private List<Person> employees;
232241
233242## 🔍 Repository Query Methods
234243
235- Spring Data FalkorDB supports JPA-style query methods with these patterns:
244+ Spring Data FalkorDB supports two types of queries:
245+
246+ ### 1. Derived Query Methods (Automatically Implemented)
247+
248+ Define methods following Spring Data naming conventions, and the implementation is generated automatically:
236249
237- ### Query Keywords
250+ #### Query Keywords
238251
239252- ** ` findBy... ` ** : Find entities matching criteria
240253- ** ` countBy... ` ** : Count entities matching criteria
241254- ** ` existsBy... ` ** : Check if entities exist matching criteria
242255- ** ` deleteBy... ` ** : Delete entities matching criteria
256+ - ** ` findFirstBy... ` ** / ** ` findTopNBy... ` ** : Limit results
243257
244- ### Supported Operations
258+ #### Supported Comparison Operations
245259
246260``` java
247- // Exact match
261+ // Equality
248262findByName(String name)
263+ findByNameNot(String name)
249264
250- // Comparison operations
265+ // Comparison
251266findByAgeGreaterThan(int age)
252267findByAgeGreaterThanEqual(int age)
253268findByAgeLessThan(int age)
254-
255- // Range queries
256- findByAgeBetween(int start, int end)
269+ findByAgeLessThanEqual(int age)
257270
258271// String operations
259- findByNameContaining(String substring)
260- findByNameStartingWith(String prefix)
261- findByNameIgnoreCase(String name)
272+ findByNameContaining(String substring) // *substring*
273+ findByNameStartingWith(String prefix) // prefix*
274+ findByNameEndingWith(String suffix) // *suffix
275+ findByNameLike(String pattern) // Custom pattern
276+ findByNameNotContaining(String substring)
277+
278+ // Null checks
279+ findByEmailIsNull()
280+ findByEmailIsNotNull()
281+
282+ // Boolean
283+ findByActiveTrue()
284+ findByActiveFalse()
285+
286+ // Collections
287+ findByAgeIn(Collection<Integer > ages)
288+ findByAgeNotIn(Collection<Integer > ages)
289+
290+ // Logical operations
291+ findByNameAndAge(String name, int age) // AND condition
292+ findByNameOrEmail(String name, String email) // OR condition
262293
263294// Sorting and pagination
264- findAllByOrderByNameAsc( )
295+ findByAgeGreaterThan( int age, Sort sort )
265296findByAgeGreaterThan(int age, Pageable pageable)
266297
267- // Logical operations
268- findByNameAndAge(String name, int age)
269- findByNameOrEmail(String name, String email)
298+ // Limiting results
299+ findFirstByOrderByCreatedAtDesc()
300+ findTop10ByOrderByAgeDesc()
301+ ```
302+
303+ ### 2. Custom Cypher Queries with @Query
304+
305+ Write custom Cypher queries for complex operations:
306+
307+ ``` java
308+ public interface PersonRepository extends FalkorDBRepository<Person , Long > {
309+
310+ // Using named parameters
311+ @Query (" MATCH (p:Person)-[:KNOWS]->(f:Person) "
312+ + " WHERE p.name = $name RETURN f" )
313+ List<Person > findFriends (@Param (" name" ) String name );
314+
315+ // Using indexed parameters
316+ @Query (" MATCH (p:Person) WHERE p.age > $0 RETURN p" )
317+ List<Person > findOlderThan (int age );
318+
319+ // Count query
320+ @Query (value = " MATCH (p:Person)-[:WORKS_FOR]->(c:Company) "
321+ + " WHERE c.name = $company RETURN count(p)" ,
322+ count = true )
323+ long countEmployees (@Param (" company" ) String company );
324+
325+ // Exists query
326+ @Query (value = " MATCH (p:Person {email: $email}) RETURN count(p) > 0" ,
327+ exists = true )
328+ boolean emailExists (@Param (" email" ) String email );
329+
330+ // Write query (creates/updates data)
331+ @Query (value = " MATCH (p:Person {id: $id}) "
332+ + " SET p.lastLogin = $time" ,
333+ write = true )
334+ void updateLastLogin (@Param (" id" ) Long id , @Param (" time" ) LocalDateTime time );
335+ }
336+ ```
337+
338+ ### Query Method Examples
339+
340+ ``` java
341+ // Simple equality
342+ List<Person > people = repository. findByName(" John" );
343+
344+ // Comparison
345+ List<Person > adults = repository. findByAgeGreaterThanEqual(18 );
346+
347+ // String matching
348+ List<Person > smiths = repository. findByNameEndingWith(" Smith" );
349+
350+ // Logical AND/OR
351+ List<Person > results = repository. findByNameAndAgeGreaterThan(" John" , 25 );
352+ List<Person > results
= repository
. findByNameOrEmail(
" John" ,
" [email protected] " );
353+
354+ // Null checks
355+ List<Person > noEmail = repository. findByEmailIsNull();
356+
357+ // Collections
358+ List<Person > youngPeople = repository. findByAgeIn(Arrays . asList(18 , 19 , 20 ));
359+
360+ // Counting and existence
361+ long count = repository. countByAge(25 );
362+ boolean exists
= repository
. existsByEmail(
" [email protected] " );
363+
364+ // Sorting
365+ List<Person > sorted = repository. findByAgeGreaterThan(20 , Sort . by(" name" ). ascending());
366+
367+ // Pagination
368+ Page<Person > page = repository. findByAgeGreaterThan(18 , PageRequest . of(0 , 10 ));
369+
370+ // Limiting
371+ Optional<Person > youngest = repository. findFirstByOrderByAgeAsc();
372+ List<Person > oldest = repository. findTop5ByOrderByAgeDesc();
373+
374+ // Delete
375+ repository. deleteByAge(0 ); // Delete all with age = 0
270376```
271377
272378## 🧪 Twitter Integration Test
@@ -584,12 +690,28 @@ To verify everything is working correctly:
584690
585691### ✅ ** Fully Implemented & Tested**
586692- ✅ Core annotations (` @Node ` , ` @Id ` , ` @Property ` , ` @GeneratedValue ` )
693+ - ✅ ` @EnableFalkorDBRepositories ` for repository auto-configuration
587694- ✅ ` FalkorDBClient ` integration with JFalkorDB driver
588695- ✅ ` FalkorDBTemplate ` for custom Cypher queries
589696- ✅ Basic entity mapping (Java objects ↔ FalkorDB nodes)
590697- ✅ Entity persistence (save/retrieve operations)
591- - ✅ Raw Cypher query execution with parameters
698+ - ✅ ** Derived query methods** - Full support for method name-based queries
699+ - ✅ All comparison operators (=, <>, >, <, >=, <=)
700+ - ✅ String operations (Like, StartingWith, EndingWith, Containing)
701+ - ✅ Null checks (IsNull, IsNotNull)
702+ - ✅ Boolean checks (True, False)
703+ - ✅ Collection operations (In, NotIn)
704+ - ✅ Logical operations (And, Or)
705+ - ✅ Count, exists, and delete queries
706+ - ✅ Top/First limiting queries
707+ - ✅ Sorting support
708+ - ✅ ** Custom @Query annotation** with Cypher queries
709+ - ✅ Named parameters with ` @Param `
710+ - ✅ Indexed parameters ($0, $1, etc.)
711+ - ✅ Count and exists projections
712+ - ✅ Write operations support
592713- ✅ Spring Data repository interfaces
714+ - ✅ Raw Cypher query execution with parameters
593715- ✅ Integration test suite (Twitter social graph with 4 test scenarios)
594716- ✅ Complete Twitter entity definitions (TwitterUser, Tweet, Hashtag)
595717- ✅ Comprehensive relationship mapping definitions
@@ -598,10 +720,8 @@ To verify everything is working correctly:
598720- ✅ Complex analytics and traversal queries
599721
600722### 🚧 ** In Progress**
601- - 🔄 ` @Relationship ` annotation automatic handling
602- - 🔄 Complete mapping context implementation
723+ - 🔄 ` @Relationship ` annotation automatic relationship persistence
603724- 🔄 Entity converter with automatic relationship traversal
604- - 🔄 Query method name parsing (` findByName ` , etc.)
605725- 🔄 Full transaction support integration
606726
607727### 📋 ** Planned**
0 commit comments