Skip to content

Domain Specific Semantic Query Client for Neo4j Graph Database for Kotlin + Gradle projects.

License

Notifications You must be signed in to change notification settings

vihangpatil/neo4j-store

Repository files navigation

Neo4j Store

Build Status Kotlin version badge GitHub license

Neo4j Graph Database client for Koltin + Gradle project.
Layered architecture exposing multiple levels of API. Recommended top-level DSL API enables you to perform DB operations in declarative type-safe semantics specific to your domain. Based on the verbs used in your Entity-Relation definitions (see Define Entities and Relations), custom DSL is auto-generated with semantic verbs as method names.

Quick Start

In this example, we have …​

  1. Two entities - (User) and (Role)

  2. Directed relation from User to Role - (User)-[HAS_ROLE]→(Role).

First you have to Define Entities and Relations.
Based on those definitions, custom DSL methods will be auto-generated for you.

Then you can do operations on Entities such as …​

// create
create { User(id = "foo", name = "Jane Doe") }

// read
val user = get( User withId "foo" )

// update
update { User(id = "foo", name = "John Doe") }

// delete
delete { User withId "foo" }

And operations on Entity Relations such as

// create
link { (User withId "foo") hasRole (Role withId "admin") }

// read
val user = get( User withRole (Role withId "admin"))
val role = get( Role ofUser (User withId "foo"))

// delete
unlink { (User withId "foo") hasRole (Role withId "admin") }
ℹ️
  • Here, methods such as withId, hasRole, withRole and ofUser will be provided by auto-generated code by Kotlin Symbol Processor (KSP).

  • So, advanced users can also write methods which are missing in auto-generated code.

  • But, generic methods such as create, get, update, delete, link and unlink are part of dsl and not auto-generated.

Define Entities and Relations

For e.g., to define…​

  1. entities - (User) and (Role)

  2. relation (User)-[HAS_ROLE]→(Role).

package dev.vihang.neo4jstore.examples.dslclient

import dev.vihang.neo4jstore.schema.model.HasId
import dev.vihang.neo4jstore.dsl.model.annotation.Entity
import dev.vihang.neo4jstore.dsl.model.annotation.Relation

@Entity                             // (4)
@Relation(                          // (5)
        name = "HAS_ROLE",                                          // (6)
        to = "dev.vihang.neo4jstore.examples.dslclient.Role",       // (7)
        forwardRelation = "hasRole", // [User] hasRole [Role]       // (8)
        reverseRelation = "ofUser",  // [Role] ofUser [User]        // (8)
        forwardQuery = "withRole",   // get(User withRole [Role])   // (9)
        reverseQuery = "ofUser")     // get(Role ofUser [User])     // (9)
data class User(                    // (1)
        override val id: String,    // (2)
        val name: String
) : HasId {                         // (2)
    // Needed for DSL
    companion object                // (3)
}

@Entity                             // (4)
data class Role(                    // (1)
        override val id: String,    // (2)
        val description: String
) : HasId {                         // (2)
    // Needed for DSL
    companion object                // (3)
}

To define an Entity (steps 1 to 4) and Relation (steps 5 onwards):

  1. Create Entity class as simple Koltin data class. Name of the class will be used as label in Neo4j nodes.

  2. Make entity classes implement interface HasId and hence add a field override val id: String.

  3. Add companion object for entity classes. It is used to add extension functions such as withId for the DSL such as get( User withId "foo" ).

  4. Add @Entity annotation to the entity class.

  5. Only on from entity of a directed relation, add @Relation annotation.
    In this example, the relation is from User entity.

  6. name of the relation has to be capital snake case (i.e., separated by underscore). It will be used as label in Neo4j relations.

  7. to of the relation is the to entity class’s qualified name, i.e., including package name.

  8. forwardRelation and reverseRelation are relation verbs in camel case to define (create/link or delete/unlink) relation.
    E.g. User hasRole Role and Role ofUser User.
    Actual DSL code will be:

    • link { (User withId "foo") hasRole (Role withId "admin") }

    • link { (Role withId "admin") ofUser (User withId "foo")}

  9. forwardQuery and reverseQuery are query verbs, which are used in the query based on relations.
    E.g. get( User withRole Role) and get( Role ofUser User )
    Actual DSL code will be:

    • get( User withRole (Role withId "admin"))

    • get( Role ofUser (User withId "foo")).

ℹ️
  • withId is reserved and cannot be used as "query" verbs.

  • "relation" and "query" verbs can be same.
    So, ofUser is both - reverseRelation and reverseQuery in the example above.

💡
  • For relation verbs, try using verbs which make sense as a declaration or statement.

  • For query verbs,, try using verbs which make sense as a query.

  • And keep that query and statement as a documentation comment alongside.

ℹ️

This quick start guide is only for layer 3 API.
For the details of API for layer 2 and 1, check documentation under docs.

Source

This project was a module written by me named neo4j-store in a larger Open-Source mono-repo project - ostelco-core with Apache License 2.0. The module has been ported out to be project of its own so that it can be used independently. The new project is made generic and does not take any code with business logic from its origin project.