Skip to content

Instantly share code, notes, and snippets.

@dacr
Created June 21, 2024 21:09

Revisions

  1. dacr created this gist Jun 21, 2024.
    106 changes: 106 additions & 0 deletions neo4j-cypher-raw-data-types.sc
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,106 @@
    // summary : neo4j cypher queries - supported storable data types
    // keywords : scala, neo4j, cypher, @testable
    // publish : gist
    // authors : David Crosson
    // license : Apache NON-AI License Version 2.0 (https://raw.githubusercontent.com/non-ai-licenses/non-ai-licenses/main/NON-AI-APACHE2)
    // id : f7116c87-b923-401a-9d9a-dde815befd86
    // created-on : 2024-06-20T08:29:22+02:00
    // managed-by : https://github.com/dacr/code-examples-manager
    // run-with : scala-cli $file

    // ---------------------
    //> using scala "3.4.2"
    //> using dep "org.neo4j.test:neo4j-harness:5.20.0"
    //> using dep "org.neo4j.driver:neo4j-java-driver:5.21.0"
    // ---------------------

    import org.neo4j.driver.{AuthTokens, GraphDatabase, Result, Value}

    import scala.util.Using
    import scala.util.chaining.*
    import scala.jdk.CollectionConverters.*

    val fixture =
    """
    |CREATE (:CoreType {name: 'INTEGER', example: 42}) // INTEGER IS Long 64bits
    |CREATE (:CoreType {name: 'FLOAT', example: 1.7976931348623157E308}) // FLOAT IS Double !!
    |CREATE (:CoreType {name: 'BOOLEAN', example: true})
    |CREATE (:CoreType {name: 'STRING', example: 'hello'})
    |CREATE (:CoreType {name: 'LIST', example: [1,2,3]})
    |//CREATE (:CoreType {name: 'ByteArray', example: }) // NOT SUPPORTED IN CYPHER STANDARD
    |//CREATE (:CoreType {name: 'MAP', example: {age:42, name:"joe"}}) // NOT SUPPORTED & USELESS => GRAPH DB
    |
    |CREATE (:TemporalType {name: 'DATE', example: date('2024-06-20')})
    |CREATE (:TemporalType {name: 'LOCAL TIME', example: time('10:02:32.192')})
    |CREATE (:TemporalType {name: 'LOCAL DATETIME', example: datetime('2024-06-20T08:50:06')})
    |CREATE (:TemporalType {name: 'ZONED DATETIME', example: datetime('2024-06-20T08:50:06+02:00')})
    |CREATE (:TemporalType {name: 'ZONED TIME', example: time('10:02:32.192Z')})
    |CREATE (:TemporalType {name: 'DURATION', example: duration('P14DT16H12M')})
    |
    |CREATE (:SpatialType {name: 'POINT (2D Cartesian)', example: point({x: 2.3, y: 4.5}), srid: 7203})
    |CREATE (:SpatialType {name: 'POINT (2D WGS-84)', example: point({latitude: 48.866667, longitude: 2.333333}) , srid: 4326})
    |CREATE (:SpatialType {name: 'POINT (3D Cartesian)', example: point({x: 2.3, y: 4.5, z: 8.1}), srid: 9157})
    |CREATE (:SpatialType {name: 'POINT (3D WGS-84)', example: point({latitude: 48.866667, longitude: 2.333333, height: 35.0}), srid: 4979})
    |""".stripMargin

    val builder =
    org.neo4j.harness.Neo4jBuilders
    .newInProcessBuilder()
    .withFixture(fixture)

    Using(builder.build()) { embedded =>
    Using(GraphDatabase.driver(embedded.boltURI(), AuthTokens.none())) { driver =>
    Using(driver.session()) { session =>
    // ------------------------------------------------
    val byteArray = "Hello world".getBytes
    val createResponse = session.run("CREATE (:CoreType {name: 'ByteArray', example: $value})", Map( "value"-> byteArray ).asJava)
    // ------------------------------------------------
    val readQuery = "MATCH (n {name: $name}) RETURN n.example as value"
    def get(name: String): Value = {
    val response: Result = session.run(readQuery, Map("name" -> name).asJava)
    val value: Value = response.single().get("value")
    value
    }
    def info(name: String, comment: Option[String] = None): Unit = {
    val value = get(name)
    println(s"---------------------")
    println(s"name:$name - type=${value.`type`().name()} - ${comment.getOrElse("")}")
    println(s"java class : ${value.getClass.getCanonicalName}")
    value.`type`().name() match
    case "BOOLEAN" => println(s"value:${value.asBoolean()}")
    case "LIST OF ANY?" => println(s"value:${value.asList().asScala.mkString(",")}")
    case "FLOAT" => println(s"value:${value.asDouble()}")
    case "INTEGER" => println(s"value:${value.asLong()}")
    case "STRING" => println(s"value:${value.asString()}")
    case "DATE" => println(s"value:${value.asLocalDate()}")
    case "DATE_TIME" => println(s"value:${value.asZonedDateTime()}")
    case "TIME" => println(s"value:${value.asOffsetTime()}")
    case "DURATION" => println(s"value:${value.asIsoDuration()}")
    case "POINT" => println(s"value:${value.asPoint()}")
    case "BYTES" => println(s"value:${value.asByteArray().mkString("-")}")
    case value => println(s"NOT YET SUPPORTED ${value.getClass.getCanonicalName}")
    }
    info("INTEGER", Some("is a Long value in fact (8 bytes)"))
    info("FLOAT", Some("is a Double value in fact (8 bytes)"))
    info("BOOLEAN")
    info("STRING")
    info("LIST")
    info("DATE")
    info("LOCAL TIME")
    info("LOCAL DATETIME")
    info("ZONED TIME")
    info("ZONED DATETIME")
    info("DURATION")
    info("POINT (2D Cartesian)")
    info("POINT (3D Cartesian)")
    info("POINT (2D WGS-84)")
    info("POINT (3D WGS-84)", Some("use height and not altitude"))
    info("ByteArray", Some("for small objects, not part of CYPHER standard"))
    // ------------------------------------------------
    println("*****************************************************************")
    println("Constructed types cannot be stored as properties (with the exception of homogenous lists).")
    println("https://neo4j.com/docs/cypher-manual/current/values-and-types/property-structural-constructed/#constructed-types")
    println("*****************************************************************")
    }.tap(r => println(r))
    }.tap(r => println(r))
    }.tap(r => println(r))