### Manual Testcontainers Setup for Drivine Source: https://github.com/liberation-data/drivine4j/blob/main/README.md This Kotlin configuration demonstrates manual setup of Testcontainers for Drivine. It defines a `DataSourceMap` by extracting connection details from `DrivineTestContainer` and manually parsing the connection URL. ```kotlin @Configuration @EnableDrivine class TestConfig { @Bean fun dataSourceMap(): DataSourceMap { val props = ConnectionProperties( host = extractHost(DrivineTestContainer.getConnectionUrl()), port = extractPort(DrivineTestContainer.getConnectionUrl()), userName = DrivineTestContainer.getConnectionUsername(), password = DrivineTestContainer.getConnectionPassword(), type = DatabaseType.NEO4J, databaseName = "neo4j" ) return DataSourceMap(mapOf("neo" to props)) } private fun extractHost(boltUrl: String): String = boltUrl.substringAfter("bolt://").substringBefore(":") private fun extractPort(boltUrl: String): Int = boltUrl.substringAfter("bolt://").substringAfter(":").toIntOrNull() ?: 7687 } ``` -------------------------------- ### Run Tests with Testcontainers (Gradle) Source: https://github.com/liberation-data/drivine4j/blob/main/TEST_CONTAINERS.md This command executes all tests in the project using the default Testcontainers setup for Neo4j. No external Neo4j instance is required as a container will be automatically provisioned. ```bash ./gradlew test ``` -------------------------------- ### Transaction Management with Drivine4j and Spring Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Shows how to manage transactions using Spring's `@Transactional` and Drivine's `@DrivineTransactional` annotations. This example demonstrates registering a user and updating a profile within transactional boundaries. ```kotlin @Component class UserService @Autowired constructor( private val personRepo: PersonRepository, private val emailService: EmailService ) { @Transactional // Spring's @Transactional works fun registerUser(person: Person) { val created = personRepo.create(person) emailService.sendWelcome(created.email) // Auto-commits on success, rolls back on exception } @DrivineTransactional // Or use Drivine's annotation fun updateUserProfile(uuid: String, updates: Partial) { personRepo.update(uuid, updates) } } ``` -------------------------------- ### Maven Configuration for Drivine4j with Kotlin Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Configures the Maven build to include the kotlin-maven-plugin with KSP support and the necessary Drivine4j code generator. This setup enables compile-time code generation for Drivine4j features. ```xml org.jetbrains.kotlin kotlin-maven-plugin 2.2.0 ksp -Xcontext-parameters com.dyescape kotlin-maven-symbol-processing 1.6 org.drivine drivine4j-codegen 0.0.1-SNAPSHOT ``` -------------------------------- ### Java Query DSL Examples Source: https://context7.com/liberation-data/drivine4j/llms.txt Demonstrates how to use the fluent Java API for type-safe queries. It covers basic filtering, multiple WHERE conditions (AND), OR conditions, ordering, deleting records, and polymorphic filtering using instanceOf. Requires GraphObjectManager and query DSL classes. ```java import org.drivine.query.dsl.JavaQueryBuilderKt; import org.drivine.manager.GraphObjectManager; import java.util.Arrays; import java.util.List; public class JavaIssueRepository { private final GraphObjectManager graphObjectManager; public JavaIssueRepository(GraphObjectManager graphObjectManager) { this.graphObjectManager = graphObjectManager; } // Basic filtering public List getOpenIssues() { return JavaQueryBuilderKt .query(graphObjectManager, RaisedAndAssignedIssue.class) .filterWith(RaisedAndAssignedIssueQueryDsl.class) .where(dsl -> dsl.getIssue().getState().eq("open")) .loadAll(); } // Multiple WHERE conditions (AND) public List getOpenUnlockedIssues() { return JavaQueryBuilderKt .query(graphObjectManager, RaisedAndAssignedIssue.class) .filterWith(RaisedAndAssignedIssueQueryDsl.class) .where(dsl -> dsl.getIssue().getState().eq("open")) .where(dsl -> dsl.getIssue().getLocked().eq(false)) .loadAll(); } // OR conditions public List getOpenOrReopenedIssues() { return JavaQueryBuilderKt .query(graphObjectManager, RaisedAndAssignedIssue.class) .filterWith(RaisedAndAssignedIssueQueryDsl.class) .whereAny(dsl -> Arrays.asList( dsl.getIssue().getState().eq("open"), dsl.getIssue().getState().eq("reopened") )) .loadAll(); } // Ordering and first result public RaisedAndAssignedIssue getLatestIssue() { return JavaQueryBuilderKt .query(graphObjectManager, RaisedAndAssignedIssue.class) .filterWith(RaisedAndAssignedIssueQueryDsl.class) .where(dsl -> dsl.getIssue().getState().eq("open")) .orderBy(dsl -> dsl.getIssue().getId().desc()) .loadFirst(); } // Delete with DSL public int deleteClosedIssues() { return JavaQueryBuilderKt .query(graphObjectManager, RaisedAndAssignedIssue.class) .filterWith(RaisedAndAssignedIssueQueryDsl.class) .where(dsl -> dsl.getIssue().getState().eq("closed")) .deleteAll(); } // instanceOf for polymorphic filtering public List getAnonymousUsers() { return JavaQueryBuilderKt .query(graphObjectManager, GuideUserWithWebUser.class) .filterWith(GuideUserWithWebUserQueryDsl.class) .where(dsl -> dsl.getWebUser().instanceOf(AnonymousWebUser.class)) .loadAll(); } } ``` -------------------------------- ### Configure Test Setup with @EnableDrivineTestConfig Source: https://context7.com/liberation-data/drivine4j/llms.txt Sets up automated testing for Drivine4j applications. It automatically configures Testcontainers for isolated Neo4j instances or a local Neo4j instance based on the USE_LOCAL_NEO4J environment variable. This configuration is essential for running integration tests. ```kotlin import org.drivine.autoconfigure.EnableDrivine import org.drivine.autoconfigure.EnableDrivineTestConfig import org.springframework.boot.test.context.SpringBootTest import org.springframework.test.annotation.Rollback import org.springframework.transaction.annotation.Transactional import org.springframework.beans.factory.annotation.Autowired import org.drivine.graph.GraphObjectManager import org.drivine.persistence.PersistenceManager import org.drivine.persistence.QuerySpecification import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import java.util.UUID import kotlin.test.assertEquals @Configuration @EnableDrivine @EnableDrivineTestConfig // Auto-configures Testcontainers or local Neo4j class TestConfig @SpringBootTest(classes = [TestConfig::class]) @Transactional @Rollback(true) class IssueRepositoryTest @Autowired constructor( private val graphObjectManager: GraphObjectManager, private val persistenceManager: PersistenceManager ) { @BeforeEach fun setupTestData() { persistenceManager.execute( QuerySpecification .withStatement("MATCH (n) WHERE n.createdBy = 'test' DETACH DELETE n") ) persistenceManager.execute( QuerySpecification.withStatement(""" CREATE (i:Issue { uuid: '${UUID.randomUUID()}', id: 123, title: 'Test Issue', state: 'open', locked: false, createdBy: 'test' }) """.trimIndent()) ) } @Test fun `should find open issues`() { val results = graphObjectManager.loadAll { where { query.issue.state eq "open" } } assertEquals(1, results.size) assertEquals("open", results[0].issue.state) } } ``` -------------------------------- ### Drivine4j Domain Model Example Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Defines a simple data class 'Person' to represent a domain object. This class will be used for mapping data between the application and the graph database. ```kotlin data class Person( val uuid: String, val firstName: String, val lastName: String, val email: String?, val age: Int ) ``` -------------------------------- ### Spring Boot Test for Repository Functionality Source: https://github.com/liberation-data/drivine4j/blob/main/README.md This is a Spring Boot integration test class for a `PersonRepository`. It uses `@SpringBootTest` and `@TestInstance` for test setup and demonstrates testing a specific repository method, `findByCity`, by asserting that the results are not empty. ```kotlin @SpringBootTest @TestInstance(TestInstance.Lifecycle.PER_CLASS) class PersonRepositoryTest @Autowired constructor( private val repository: PersonRepository ) { @Test fun `should find person by city`() { val results = repository.findByCity("New York") assertThat(results).isNotEmpty } } ``` -------------------------------- ### Kotlin GraphView Definition with Java Reference Source: https://github.com/liberation-data/drivine4j/blob/main/README.md An example of defining a `@GraphView` in Kotlin, which is recommended for DSL generation. This example shows how a Kotlin `@GraphView` can reference a Java class (`Person`) and define relationships like `WORKS_FOR`. ```kotlin // Define GraphViews in Kotlin to get DSL generation @GraphView data class PersonContext( @Root val person: Person, // References Java class! @GraphRelationship(type = "WORKS_FOR") val worksFor: List ) ``` -------------------------------- ### Chain Transformations in Drivine4j Queries Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Illustrates chaining transformations and filters in a Drivine4j query. This example maps Neo4j results to a `Person` object, filters by age, and then transforms the `Person` objects into full name strings. ```kotlin val fullNames: List = manager.query( QuerySpecification .withStatement("MATCH (p:Person) RETURN properties(p)") .transform(Person::class.java) // Map to Person .filter { it.age > 25 } // Filter .map { "${it.firstName} ${it.lastName}" } // Transform to String ) ``` -------------------------------- ### Generated Cypher Query Example Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Illustrates the Cypher query generated by Drivine4j based on the `HolidayingPerson` graph view definition. This demonstrates how Drivine4j translates annotated Kotlin models into efficient graph queries. ```cypher MATCH (person:Person {firstName: $firstName}) WITH person, [(person)-[:BOOKED_HOLIDAY]->(holiday:Holiday) | holiday {.*}] AS holidays RETURN { person: properties(person), holidays: holidays } ``` -------------------------------- ### Enable Drivine Test Configuration Source: https://github.com/liberation-data/drivine4j/blob/main/README.md This configuration class enables Drivine's testing features, including automatic Testcontainers setup. It requires `@EnableDrivine` and `@EnableDrivineTestConfig` annotations and allows control over using local Neo4j or Testcontainers via an environment variable. ```kotlin @Configuration @EnableDrivine @EnableDrivineTestConfig class TestConfig ``` -------------------------------- ### Handle Multiple Query Results in Drivine4j Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Illustrates different methods for retrieving query results from `PersistenceManager`. It covers getting a single mandatory result, an optional single result, all results, and executing queries without returning results. ```kotlin // Expect exactly one result (throws if 0 or >1) val person: Person = manager.getOne(spec) // Expect 0 or 1 result (returns null if not found) val maybePerson: Person? = manager.maybeGetOne(spec) // Return all results val people: List = manager.query(spec) // Execute without returning results (for mutations) manager.execute(spec) ``` -------------------------------- ### Database-Side Sorting of Nested Collections with APOC Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Explains and provides examples for sorting nested relationship collections directly within the database using APOC Extended's `apoc.coll.sortMaps()` function. This is crucial for performance when dealing with large, nested datasets. ```kotlin // Sort assignees by name within each issue val results = graphObjectManager.loadAll { where { issue.state eq "open" } orderBy { issue.id.desc() // Root ordering (uses index) assignedTo.name.asc() // Collection sorting (uses APOC) } } // Each issue's assignedTo list is sorted by name ascending ``` ```kotlin // Sort nested worksFor organizations within raisedBy val results = graphObjectManager.loadAll { orderBy { raisedBy.worksFor.name.desc() // Sort organizations by name descending } } ``` -------------------------------- ### Basic Ordering with Drivine4j DSL Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Demonstrates how to apply basic ordering to query results using the DSL. It supports ascending and descending order for properties. ```kotlin val results = graphObjectManager.loadAll { where { person.bio.isNotNull() } orderBy { person.name.asc() } } ``` -------------------------------- ### Building and Testing Drivine4j with Gradle Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Provides common Gradle commands for interacting with the drivine4j project. This includes running tests, building the library, and publishing artifacts to the local Maven repository. ```bash # Run tests ./gradlew test # Build library ./gradlew build # Publish to local Maven (~/.m2/repository) ./gradlew publishToMavenLocal ``` -------------------------------- ### Run Drivine4j Tests with Local or Testcontainers Neo4j Source: https://context7.com/liberation-data/drivine4j/llms.txt Provides command-line instructions for executing Drivine4j tests. You can choose to run tests against a local Neo4j instance for faster iteration and inspection by setting the USE_LOCAL_NEO4J environment variable, or against isolated instances managed by Testcontainers for CI environments. ```bash # Run tests with local Neo4j (fast, inspectable) export USE_LOCAL_NEO4J=true ./gradlew test # Run tests with Testcontainers (isolated, CI) ./gradlew test # USE_LOCAL_NEO4J defaults to false ``` -------------------------------- ### Java NodeFragment Definition Source: https://github.com/liberation-data/drivine4j/blob/main/README.md An example of defining a `@NodeFragment` in Java. This class represents a node in the graph and can be used with `GraphObjectManager`. It demonstrates basic property definitions like UUID, String, and String. ```java // Java fragments work great! @NodeFragment(labels = {"Person"}) public class Person { @NodeId public UUID uuid; public String name; public String bio; } ``` -------------------------------- ### Kotlin: Save Object with Relationship Changes Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Illustrates saving an object where relationship collections are modified, such as removing all employment history. This example uses `CascadeType.NONE` to ensure only the relationships are affected, not the target nodes. ```kotlin val person = graphObjectManager.load(uuid, PersonCareer::class.java)!! // Remove all employment history val updated = person.copy(employmentHistory = emptyList()) graphObjectManager.save(updated, CascadeType.NONE) ``` -------------------------------- ### Fluent Query Building Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Demonstrates how to build queries fluently using QuerySpecification, including client-side filtering and mapping. ```APIDOC ## Fluent Query Building ### Description Build complex Neo4j queries using a fluent API with client-side filtering and transformations. ### Method `query()` ### Endpoint N/A (In-memory operation) ### Parameters #### Query Specification - **`withStatement(cypherQuery)`** (String) - Required - The Cypher query statement. - **`transform(TargetClass::class.java)`** (Class) - Required - The target class to transform results into. - **`filter { predicate }`** (Lambda) - Optional - Client-side filtering predicate. - **`map { transformation }`** (Lambda) - Optional - Client-side transformation of results. - **`limit(n)`** (Int) - Optional - Limits the number of results. ### Request Example ```kotlin val activeAdults = manager.query( QuerySpecification .withStatement("MATCH (p:Person) RETURN properties(p)") .transform(Person::class.java) .filter { it.age >= 18 } // Client-side filtering .filter { it.email != null } .map { it.firstName } // Transform to String .limit(10) ) ``` ### Response #### Success Response (200) - **List** - A list of transformed results. ``` -------------------------------- ### Generated Cypher for Recursive Relationships Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Illustrates the Cypher query generated by Drivine for recursive graph views. This example shows how nested pattern comprehensions are used to expand relationships to a specified depth. ```cypher MATCH (location:Location) WITH location { name: location.name, type: location.type, uuid: location.uuid } AS location, [(location)-[:HAS_LOCATION]->(sub_d1:Location) | sub_d1 { location: { name: sub_d1.name, type: sub_d1.type, uuid: sub_d1.uuid }, subLocations: [(sub_d1)-[:HAS_LOCATION]->(sub_d2:Location) | sub_d2 { location: { name: sub_d2.name, ... }, subLocations: [(sub_d2)-[:HAS_LOCATION]->(sub_d3:Location) | sub_d3 { location: { ... }, subLocations: [] } ] } ] } ] AS subLocations RETURN { location: location, subLocations: subLocations } AS result ``` -------------------------------- ### Build Fluent Queries with Drivine4j Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Demonstrates building a fluent query specification in Kotlin. It includes chaining methods for statement definition, transformation, client-side filtering, mapping, and limiting results. ```kotlin val activeAdults = manager.query( QuerySpecification .withStatement("MATCH (p:Person) RETURN properties(p)") .transform(Person::class.java) .filter { it.age >= 18 } // Client-side filtering .filter { it.email != null } .map { it.firstName } // Transform to String .limit(10) ) ``` -------------------------------- ### Kotlin: Delete Nodes with WHERE Clause Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Provides examples of deleting nodes based on a specified condition using a WHERE clause. This applies to both regular nodes and the root nodes of GraphViews, using the appropriate fragment alias. ```kotlin // Delete only if condition is met graphObjectManager.delete(uuid, "n.state = 'closed'") // For GraphViews, use the root fragment alias graphObjectManager.delete(uuid, "issue.state = 'closed'") ``` -------------------------------- ### Load External Cypher Queries Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Explains how to place `.cypher` files in `src/main/resources/queries/` and load them using `QueryLoader`. This promotes modularity by separating queries from application code. ```cypher // queries/findActiveUsers.cypher MATCH (p:Person) WHERE p.isActive = true RETURN properties(p) ``` -------------------------------- ### Load All Graph Objects with Drivine4j Source: https://context7.com/liberation-data/drivine4j/llms.txt Demonstrates loading all instances of a graph object (GraphView or NodeFragment) from the database using `GraphObjectManager.loadAll`. Supports reified types in Kotlin and class references for Java compatibility, along with basic WHERE clause filtering. ```kotlin import org.drivine.manager.GraphObjectManager import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Component @Component class PersonService @Autowired constructor( private val graphObjectManager: GraphObjectManager ) { // Load all using reified type (Kotlin) fun getAllPeople(): List { return graphObjectManager.loadAll() } // Load all using class reference (Java-compatible) fun getAllPeopleJava(): List { return graphObjectManager.loadAll(PersonCareer::class.java) } // Load all with simple WHERE clause filter fun getActivePeople(): List { return graphObjectManager.loadAll( PersonContext::class.java, "person.bio IS NOT NULL" ) } } ``` -------------------------------- ### External Query Files Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Explains how to load and use Cypher queries defined in external `.cypher` files. ```APIDOC ## External Query Files ### Description Store Cypher queries in external files for better organization and reusability. ### Method N/A (Configuration and Repository usage) ### Endpoint N/A ### Parameters N/A ### Request Example ```kotlin // Place .cypher files in src/main/resources/queries/ // queries/findActiveUsers.cypher // MATCH (p:Person) // WHERE p.isActive = true // RETURN properties(p) @Configuration class QueryConfig @Autowired constructor( private val loader: QueryLoader ) { @Bean fun findActiveUsers() = CypherStatement(loader.load("findActiveUsers")) } @Component class PersonRepository @Autowired constructor( @Qualifier("neoManager") val manager: PersistenceManager, val findActiveUsers: CypherStatement ) { fun getActive(): List { return manager.query( QuerySpecification .withStatement(findActiveUsers.statement) .transform(Person::class.java) ) } } ``` ### Response N/A ``` -------------------------------- ### Define Graph View Model in Kotlin Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Example of defining a data class in Kotlin to represent a graph view, using Drivine4j annotations to map relationships and root nodes. This model is used with the GraphObjectManager for type-safe querying. ```kotlin @GraphView data class HolidayingPerson( @Root val person: Person, @GraphRelationship(type = "BOOKED_HOLIDAY") val holidays: List ) ``` -------------------------------- ### Configure Datasource for Testing (YAML) Source: https://github.com/liberation-data/drivine4j/blob/main/README.md This YAML configuration snippet defines the datasource properties for testing. It specifies connection details like host, port, username, password, type, and database name, intended to be used with Drivine's testing utilities. ```yaml database: datasources: neo: host: localhost port: 7687 username: neo4j password: password type: NEO4J database-name: neo4j ``` -------------------------------- ### Basic Filtering with Drivine4j DSL Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Demonstrates how to load entities based on a simple condition using the type-safe DSL. It allows direct property access for filtering, improving code readability and safety. ```kotlin val leads = graphObjectManager.loadAll { where { person.bio contains "Lead" // Direct property access! } } ``` -------------------------------- ### Kotlin: Type-Safe DSL Delete Operations Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Illustrates the use of a type-safe DSL for deleting nodes, offering compile-time checking. Examples include deleting based on single or multiple conditions, relationship properties, and deleting all nodes without a filter. ```kotlin // Delete closed issues graphObjectManager.deleteAll { where { issue.state eq "closed" } } // Delete with multiple conditions graphObjectManager.deleteAll { where { issue.state eq "open" issue.locked eq true } } // Delete by relationship property graphObjectManager.deleteAll { where { assignedTo.name eq "Former Employee" } } // Delete all (no filter) graphObjectManager.deleteAll { } ``` -------------------------------- ### Type-Safe Query DSL - OR Conditions with anyOf Source: https://context7.com/liberation-data/drivine4j/llms.txt Demonstrates how to combine conditions using OR logic with the `anyOf` block in Drivine4j's Type-Safe Query DSL. This allows for flexible querying by specifying multiple alternative criteria. ```kotlin import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Component import org.drivine.manager.GraphObjectManager @Component class AdvancedIssueRepository @Autowired constructor( private val graphObjectManager: GraphObjectManager ) { // Simple OR condition fun getOpenOrReopenedIssues(): List { return graphObjectManager.loadAll { where { anyOf { query.issue.state eq "open" query.issue.state eq "reopened" } } } } // Combine AND and OR conditions fun getUnlockedOpenOrHighPriority(): List { return graphObjectManager.loadAll { where { query.issue.locked eq false // AND anyOf { // OR query.issue.state eq "open" query.issue.id gte 1000 } } } } // OR across different relationships fun getIssuesByAssigneeOrRaiser(name: String): List { return graphObjectManager.loadAll { where { anyOf { query.assignedTo.name eq name query.raisedBy.person.name eq name } } } } // Complex multi-level query fun dashboardQuery(org: String): List { return graphObjectManager.loadAll { where { query.issue.state eq "open" query.raisedBy.worksFor.name eq org anyOf { query.assignedTo.name.contains("Developer") query.issue.title.contains("feature") } } } } } ``` -------------------------------- ### Add Drivine4j Dependency using Gradle (Kotlin DSL) Source: https://context7.com/liberation-data/drivine4j/llms.txt This snippet shows how to add the Drivine4j library and its code generator to a Kotlin project using Gradle. It includes the necessary KSP plugin and Kotlin compiler options for context parameters. ```kotlin plugins { id("com.google.devtools.ksp") version "2.2.20-2.0.4" kotlin("jvm") version "2.2.0" } kotlin { compilerOptions { freeCompilerArgs.addAll("-Xcontext-parameters") } } dependencies { implementation("org.drivine:drivine4j:0.0.28") ksp("org.drivine:drivine4j-codegen:0.0.28") } ``` -------------------------------- ### Partial Updates Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Shows how to perform partial updates on entities using the `partial` builder. ```APIDOC ## Partial Updates ### Description Perform partial updates on existing entities by specifying only the fields to be modified. ### Method `update()` ### Endpoint N/A (Repository method) ### Parameters #### Path Parameters - **`personId`** (String) - Required - The ID of the person to update. #### Request Body - **`updates`** (Partial) - Required - A `Partial` object specifying the fields and their new values. ### Request Example ```kotlin val updates = partial { set(Person::email, "newemail@example.com") set(Person::age, 30) } personRepo.update(personId, updates) ``` ### Response #### Success Response (200) - **Void** - Indicates successful update. #### Response Example N/A ``` -------------------------------- ### Java Query DSL Basic Usage Source: https://github.com/liberation-data/drivine4j/blob/main/README.md This Java code snippet demonstrates the basic usage of Drivine4j's Java Query DSL. It shows how to initiate a query, specify a filter class, apply a `where` clause using a lambda, and load all results. ```java import org.drivine.query.dsl.JavaQueryBuilderKt; List results = JavaQueryBuilderKt .query(graphObjectManager, RaisedAndAssignedIssue.class) .filterWith(RaisedAndAssignedIssueQueryDsl.class) .where(dsl -> dsl.getIssue().getState().eq("open")) .loadAll(); ``` -------------------------------- ### Binding Objects with Jackson Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Demonstrates how to use `bindObject` to serialize Kotlin objects into Neo4j-compatible types using Jackson. ```APIDOC ## Binding Objects with Jackson ### Description Serializes Kotlin objects into Neo4j-compatible types using Jackson for use in Cypher queries. ### Method `bindObject(propertyName, object)` ### Endpoint N/A (QuerySpecification method) ### Parameters #### Query Specification - **`propertyName`** (String) - Required - The name of the property in the Cypher statement to bind the object to. - **`object`** (Any) - Required - The Kotlin object to serialize. ### Request Example ```kotlin // Automatically converts Enums to String, UUID to String, Instant to ZonedDateTime val task = Task(id = "1", priority = Priority.HIGH, status = Status.OPEN, dueDate = Instant.now()) manager.execute( QuerySpecification .withStatement("CREATE (t:Task) SET t = $props") .bindObject("props", task) ) ``` ### Response N/A (Modifies the QuerySpecification) ``` -------------------------------- ### Drivine4j Application Configuration Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Sets up the application configuration for Drivine4j, defining the data source connection properties for a Neo4j database. This includes creating a DataSourceMap bean. ```kotlin @Configuration @ComponentScan("org.drivine") class AppConfig { @Bean fun dataSourceMap(): DataSourceMap { val props = ConnectionProperties( host = "localhost", port = 7687, username = "neo4j", password = "password", database = "neo4j" ) return DataSourceMap(mapOf("neo" to props)) } } ``` -------------------------------- ### Using Drivine4j DSL from Java Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Demonstrates how to use the type-safe DSL generated from Kotlin `@GraphView` classes within Java code. It shows querying for `PersonContext` objects, filtering by name using the DSL, and loading all results. ```java // Use from Java with the fluent DSL API List results = JavaQueryBuilderKt .query(graphObjectManager, PersonContext.class) .filterWith(PersonContextQueryDsl.class) .where(dsl -> dsl.getPerson().getName().contains("Alice")) .loadAll(); ``` -------------------------------- ### Configure Multiple DataSources in Kotlin Source: https://github.com/liberation-data/drivine4j/blob/main/README.md This configuration class sets up multiple data sources for different purposes, such as analytics and user management. It utilizes Spring's `@Configuration` and `@Bean` annotations to define `DataSourceMap` and `ConnectionProperties` for each database. ```kotlin @Configuration class MultiDbConfig { @Bean fun dataSourceMap(): DataSourceMap { return DataSourceMap(mapOf( "analytics" to ConnectionProperties( host = "analytics.neo4j.com", database = "analytics" ), "users" to ConnectionProperties( host = "users.neo4j.com", database = "users" ) )) } } @Component class AnalyticsRepository @Autowired constructor( @Qualifier("analytics") val manager: PersistenceManager ) { /* ... */ } @Component class UserRepository @Autowired constructor( @Qualifier("users") val manager: PersistenceManager ) { /* ... */ } ``` -------------------------------- ### Load All Graph View Instances Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Loads all instances of a specified GraphView. This is typically done within a service class using the GraphObjectManager. ```kotlin @Component class PersonService @Autowired constructor( private val graphObjectManager: GraphObjectManager ) { fun getAllPeople(): List { return graphObjectManager.loadAll(PersonCareer::class.java) } } ``` -------------------------------- ### Manual Client-Side Sorting of Collections Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Presents manual approaches for client-side sorting when complex logic or no annotations are desired. This includes simple sorting methods within data classes and using cached lazy properties. ```kotlin // Simple method (zero annotations): @GraphView data class IssueWithAssignees( @Root val issue: Issue, @GraphRelationship(type = "ASSIGNED_TO") val assignees: List ) { fun sortedAssignees() = assignees.sortedBy { it.person.name } } ``` ```kotlin // Cached lazy property: @GraphView data class IssueWithAssignees( @Root val issue: Issue, @GraphRelationship(type = "ASSIGNED_TO") val assignees: List ) { @get:JsonIgnore val sortedAssignees: List by lazy { assignees.sortedBy { it.person.name } } } ``` -------------------------------- ### QuerySpecification Builder Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Details the builder pattern for constructing `QuerySpecification` objects. ```APIDOC ## QuerySpecification Builder ### Description Allows for the construction of `QuerySpecification` objects using a fluent builder pattern. ### Methods - **`withStatement(cypherQuery)`**: Starts the query specification with a Cypher statement. - **`bind(params)`**: Binds parameters to the Cypher query. - **`transform(TargetClass::class.java)`**: Specifies the target class for mapping query results. - **`filter { predicate }`**: Applies client-side filtering to the results. - **`map { transformation }`**: Applies client-side transformations to the results. - **`limit(n)`**: Limits the number of returned results. - **`skip(n)`**: Skips the first `n` results. ### Endpoint N/A (Builder pattern) ### Parameters - **`cypherQuery`** (String) - Required - The Cypher query string. - **`params`** (Map) - Optional - Parameters to bind to the query. - **`TargetClass::class.java`** (Class) - Required - The class to transform results into. - **`predicate`** (Lambda) - Optional - A lambda function for filtering. - **`transformation`** (Lambda) - Optional - A lambda function for transforming results. - **`n`** (Int) - Optional - The number of results to limit or skip. ### Request Example ```kotlin QuerySpecification .withStatement("MATCH (p:Person) RETURN p") .bind(mapOf("name" to "Alice")) .transform(Person::class.java) .filter { it.age > 18 } .limit(5) ``` ### Response - **`QuerySpecification`**: The constructed query specification object. ``` -------------------------------- ### Drivine4j Cypher RETURN Clause Best Practices Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Illustrates correct and incorrect ways to use the RETURN clause in Cypher queries with Drivine4j's PersistenceManager. Emphasizes returning a single map or scalar for proper mapping with `.transform()`. ```cypher -- WRONG: Multiple columns -- hard to map, NULL values cause errors RETURN a.name, a.age, b.title -- CORRECT: Return a single map RETURN { name: a.name, age: a.age, title: b.title } AS result -- CORRECT: Return a single property map RETURN properties(p) -- CORRECT: Return a scalar value RETURN count(p) AS total ``` ```cypher MATCH (p:Proposition)-[:HAS_MENTION]->(m:Mention) WITH m.type AS entityType, m.name AS name, count(p) AS mentionCount ORDER BY mentionCount DESC LIMIT 30 RETURN { entityType: entityType, name: name, mentionCount: mentionCount } AS result ``` -------------------------------- ### Configure Drivine with @EnableDrivine in Spring Boot Source: https://context7.com/liberation-data/drivine4j/llms.txt This Kotlin configuration class enables Drivine infrastructure beans and sets up Neo4j database connections using Spring Boot. It defines a DataSourceMap with connection properties for a Neo4j instance. ```kotlin import org.drivine.autoconfigure.EnableDrivine import org.drivine.connection.ConnectionProperties import org.drivine.connection.DataSourceMap import org.drivine.connection.DatabaseType import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration @Configuration @EnableDrivine class AppConfig { @Bean fun dataSourceMap(): DataSourceMap { val neo4jProps = ConnectionProperties( type = DatabaseType.NEO4J, host = "localhost", port = 7687, userName = "neo4j", password = "password", databaseName = "neo4j" ) return DataSourceMap(mapOf("neo" to neo4jProps)) } } ``` -------------------------------- ### Multiple Query Results Handling Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Details the different methods for handling query results, including single, optional single, and multiple results. ```APIDOC ## Multiple Query Results Handling ### Description Provides methods to retrieve query results, handling cases for exactly one, zero or one, and multiple results. ### Method - `getOne(spec)` - `maybeGetOne(spec)` - `query(spec)` - `execute(spec)` ### Endpoint N/A (PersistenceManager methods) ### Parameters #### Query Specification - **`spec`** (QuerySpecification) - Required - The specification for the query to execute. ### Request Example ```kotlin // Expect exactly one result (throws if 0 or >1) val person: Person = manager.getOne(spec) // Expect 0 or 1 result (returns null if not found) val maybePerson: Person? = manager.maybeGetOne(spec) // Return all results val people: List = manager.query(spec) // Execute without returning results (for mutations) manager.execute(spec) ``` ### Response #### Success Response (200) - **`getOne`**: Returns a single result of type T. - **`maybeGetOne`**: Returns a single result of type T or null. - **`query`**: Returns a List of results of type T. - **`execute`**: Returns void. ``` -------------------------------- ### Transaction Management Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Illustrates how to manage transactions using Spring's `@Transactional` or Drivine's `@DrivineTransactional` annotation. ```APIDOC ## Transaction Management ### Description Manage database transactions using declarative annotations for automatic commit or rollback. ### Method N/A (Annotation-based) ### Endpoint N/A ### Parameters N/A ### Request Example ```kotlin @Component class UserService @Autowired constructor( private val personRepo: PersonRepository, private val emailService: EmailService ) { @Transactional // Spring's @Transactional works fun registerUser(person: Person) { val created = personRepo.create(person) emailService.sendWelcome(created.email) // Auto-commits on success, rolls back on exception } @DrivineTransactional // Or use Drivine's annotation fun updateUserProfile(uuid: String, updates: Partial) { personRepo.update(uuid, updates) } } ``` ### Response N/A (Transaction outcome is implicit) ``` -------------------------------- ### QuerySpecification Builder Methods Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Details the fluent builder methods available on `QuerySpecification` for constructing Neo4j queries. This includes setting the statement, binding parameters, transforming results, filtering, mapping, and controlling result limits. ```kotlin QuerySpecification .withStatement(cypherQuery) // Start with Cypher query .bind(params) // Bind parameters .transform(TargetClass::class.java) // Map to target type .filter { predicate } // Client-side filtering .map { transformation } // Transform results .limit(n) // Limit results .skip(n) // Skip first n results ``` -------------------------------- ### Java DSL Operations for Query Building Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Demonstrates various operations available in the Java DSL for constructing graph queries. These include equality, comparison, string matching, null checks, collection membership, type filtering, logical AND/OR operations, and ordering. ```java // Equality .eq("value") .neq("value") // Comparison .gt(n) .gte(n) .lt(n) .lte(n) // Strings .contains("x") .startsWith("x") .endsWith("x") // Null .isNull() .isNotNull() // Collections .isIn(Arrays.asList(...)) // Type filter .instanceOf(SubtypeClass.class) // AND (Chain multiple .where() calls) // OR .whereAny(dsl -> Arrays.asList(...)) // Order .orderBy(dsl -> dsl.getProp().asc()) ``` -------------------------------- ### Ordering Results in Java Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Demonstrates how to specify the sorting order for query results using the 'orderBy()' method in Drivine4j. This allows results to be returned in ascending or descending order based on specified fields. ```java List results = JavaQueryBuilderKt .query(graphObjectManager, RaisedAndAssignedIssue.class) .filterWith(RaisedAndAssignedIssueQueryDsl.class) .where(dsl -> dsl.getIssue().getState().eq("open")) .orderBy(dsl -> dsl.getIssue().getId().desc()) .loadAll(); ``` -------------------------------- ### Drivine4j Repository Pattern Implementation Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Implements a 'PersonRepository' using Drivine4j's PersistenceManager. It demonstrates transactional methods for finding, creating, and updating 'Person' entities with Cypher queries. ```kotlin @Component class PersonRepository @Autowired constructor( @Qualifier("neoManager") val manager: PersistenceManager ) { @Transactional fun findByCity(city: String): List { return manager.query( QuerySpecification .withStatement("MATCH (p:Person {city: $city}) RETURN properties(p)") .bind(mapOf("city" to city)) .transform(Person::class.java) ) } @Transactional fun findById(id: String): Person? { return manager.maybeGetOne( QuerySpecification .withStatement("MATCH (p:Person {uuid: $id}) RETURN properties(p)") .bind(mapOf("id" to id)) .transform(Person::class.java) ) } @Transactional fun create(person: Person): Person { return manager.getOne( QuerySpecification .withStatement("CREATE (p:Person) SET p = $props RETURN properties(p)") .bindObject("props", person) .transform(Person::class.java) ) } @Transactional fun update(uuid: String, patch: Partial): Person { val props = patch.toMap() return manager.getOne( QuerySpecification .withStatement( "MATCH (p:Person {uuid: $uuid}) SET p += $props RETURN properties(p)" ) .bind(mapOf("uuid" to uuid, "props" to props)) .transform(Person::class.java) ) } } ``` -------------------------------- ### Loading Only the First Result in Java Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Explains how to retrieve only the first matching record from a query using the 'loadFirst()' method in Drivine4j. This method returns null if no records match the criteria. ```java RaisedAndAssignedIssue result = JavaQueryBuilderKt .query(graphObjectManager, RaisedAndAssignedIssue.class) .filterWith(RaisedAndAssignedIssueQueryDsl.class) .where(dsl -> dsl.getIssue().getState().eq("open")) .orderBy(dsl -> dsl.getIssue().getId().desc()) .loadFirst(); // Returns null if no matches ``` -------------------------------- ### Configure Drivine4j Code Generation (Gradle Kotlin DSL) Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Sets up the Gradle build for Drivine4j code generation using the KSP (Kotlin Symbol Processing) plugin. This is required for using the type-safe DSL with GraphObjectManager and enables Kotlin compiler options for context parameters. ```kotlin plugins { id("com.google.devtools.ksp") version "2.2.20-2.0.4" kotlin("jvm") version "2.2.0" } kotlin { compilerOptions { // Required for context parameters DSL freeCompilerArgs.addAll("-Xcontext-parameters") } } dependencies { implementation("org.drivine:drivine4j:0.0.1-SNAPSHOT") ksp("org.drivine:drivine4j-codegen:0.0.1-SNAPSHOT") } ``` -------------------------------- ### Create Partial Updates for Neo4j Objects Source: https://github.com/liberation-data/drivine4j/blob/main/README.md Demonstrates creating a partial update object in Kotlin using Drivine's `partial` function. This allows for selective updates of properties on a Neo4j entity. ```kotlin val updates = partial { set(Person::email, "newemail@example.com") set(Person::age, 30) } personRepo.update(personId, updates) ``` -------------------------------- ### Type-Safe Query DSL - Ordering Source: https://context7.com/liberation-data/drivine4j/llms.txt Illustrates how to apply ORDER BY clauses for ascending or descending sorting in Drivine4j's Type-Safe Query DSL. This allows for results to be ordered by specific properties, either on the root fragment or related entities. ```kotlin import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Component import org.drivine.manager.GraphObjectManager @Component class SortedIssueRepository @Autowired constructor( private val graphObjectManager: GraphObjectManager ) { // Order by root fragment property fun getIssuesOrderedById(): List { return graphObjectManager.loadAll { where { query.issue.state eq "open" } orderBy { query.issue.id.desc() } } } // Order by relationship property fun getPeopleOrderedByName(): List { return graphObjectManager.loadAll { orderBy { query.person.name.asc() } } } } ```