Skip to content

Commit

Permalink
Add node properties
Browse files Browse the repository at this point in the history
  • Loading branch information
oxisto committed Dec 10, 2024
1 parent 617f57e commit cb1dfa5
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -253,10 +253,10 @@ abstract class Node :
var isImplicit = false

/** Required field for object graph mapping. It contains the node id. */
@Id @GeneratedValue var id: Long? = null
@Id @GeneratedValue var legacyId: Long? = null

/** Will replace [id] */
var graphId: Uuid? = null
/** Will replace [legacyId] */
var id: Uuid? = null

/** Index of the argument if this node is used in a function call or parameter list. */
var argumentIndex = 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import org.neo4j.ogm.annotation.Relationship
*/
class FieldDeclaration : VariableDeclaration() {
/** Specifies, whether this field declaration is also a definition, i.e. has an initializer. */
private var isDefinition = false
var isDefinition = false

/** If this is only a declaration, this provides a link to the definition of the field. */
@Relationship(value = "DEFINES")
Expand Down
100 changes: 82 additions & 18 deletions cpg-neo4j/src/main/kotlin/de/fraunhofer/aisec/cpg/v2/Persistence.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,19 @@
package de.fraunhofer.aisec.cpg.v2

import de.fraunhofer.aisec.cpg.TranslationResult
import de.fraunhofer.aisec.cpg.graph.Name
import de.fraunhofer.aisec.cpg.graph.Node
import de.fraunhofer.aisec.cpg.graph.edges.Edge
import de.fraunhofer.aisec.cpg.graph.edges.allEdges
import de.fraunhofer.aisec.cpg.graph.edges.edges
import de.fraunhofer.aisec.cpg.graph.nodes
import de.fraunhofer.aisec.cpg.helpers.neo4j.NameConverter
import de.fraunhofer.aisec.cpg.helpers.neo4j.SimpleNameConverter
import kotlin.collections.joinToString
import kotlin.reflect.KClass
import kotlin.reflect.KProperty1
import kotlin.reflect.full.createType
import kotlin.reflect.full.memberProperties
import kotlin.reflect.full.withNullability
import kotlin.uuid.Uuid
import org.neo4j.driver.GraphDatabase
import org.neo4j.driver.TransactionContext
Expand All @@ -47,6 +53,9 @@ val neo4jSession by lazy {

val labelCache: MutableMap<KClass<out Node>, Set<String>> = mutableMapOf()

val schemaPropertiesCache: MutableMap<KClass<out Node>, Map<String, KProperty1<out Node, *>>> =

Check warning on line 56 in cpg-neo4j/src/main/kotlin/de/fraunhofer/aisec/cpg/v2/Persistence.kt

View check run for this annotation

Codecov / codecov/patch

cpg-neo4j/src/main/kotlin/de/fraunhofer/aisec/cpg/v2/Persistence.kt#L56

Added line #L56 was not covered by tests
mutableMapOf()

fun TranslationResult.persist() {
neo4jSession.executeWrite { tx ->
with(tx) {
Expand All @@ -65,31 +74,59 @@ fun TranslationResult.persist() {

context(TransactionContext)
fun Node.persist() {
if (this.graphId == null) {
this.graphId = Uuid.random()
if (this.id == null) {
this.id = Uuid.random()
}

val result =
this@TransactionContext.run(
"MERGE (n:${this.labels.joinToString("&")} { name: \$name, id: \$id } ) RETURN n.id",
mapOf(
"name" to this.name.localName,
"id" to this.graphId.toString(),
)
val properties = this.properties()

this@TransactionContext.run(
"MERGE (n:${this.labels.joinToString("&")} ${properties.writePlaceHolder()} ) RETURN n.id",
this.properties()
)
println("Created node with id $graphId")
.consume()
println("Created node with id $id")
}

fun Map<String, Any?>.writePlaceHolder(): String {
return this.map { "${it.key}: \$${it.key}" }.joinToString(", ", prefix = "{ ", postfix = " }")
}

/**
* Returns the node's properties. This DOES NOT include relationships, but only properties directly
* attached to the node.
*/
fun Node.properties(): Map<String, Any?> {
val properties = mutableMapOf<String, Any?>()
for (entry in schemaProperties) {
val value = entry.value.call(this)

if (value == null) {
continue
}

// TODO: generalize conversions
if (value is Name && entry.key == "name") {
properties += NameConverter().toGraphProperties(value)
} else if (value is Name) {
properties.put(entry.key, SimpleNameConverter().toGraphProperty(value))

Check warning on line 112 in cpg-neo4j/src/main/kotlin/de/fraunhofer/aisec/cpg/v2/Persistence.kt

View check run for this annotation

Codecov / codecov/patch

cpg-neo4j/src/main/kotlin/de/fraunhofer/aisec/cpg/v2/Persistence.kt#L112

Added line #L112 was not covered by tests
} else if (value is Uuid) {
properties.put(entry.key, value.toString())
} else {
properties.put(entry.key, value)
}
}

return properties
}

context(TransactionContext)
fun Edge<*>.persist() {
val result =
this@TransactionContext.run(
"MATCH (start { id: \$startId }), (end { id: \$endId } ) MERGE (start)-[r:${label}]->(end)",
mapOf(
"startId" to this.start.graphId.toString(),
"endId" to this.end.graphId.toString()
)
this@TransactionContext.run(
"MATCH (start { id: \$startId }), (end { id: \$endId } ) MERGE (start)-[r:${label} { }]->(end)",
mapOf("startId" to this.start.id.toString(), "endId" to this.end.id.toString())
)
.consume()
}

val Node.labels: Set<String>
Expand All @@ -99,3 +136,30 @@ val Node.labels: Set<String>
// Check, if we already computed the labels for this node's class
return labelCache.computeIfAbsent(klazz) { setOf<String>("Node", klazz.simpleName!!) }
}

val primitiveTypes =

Check warning on line 140 in cpg-neo4j/src/main/kotlin/de/fraunhofer/aisec/cpg/v2/Persistence.kt

View check run for this annotation

Codecov / codecov/patch

cpg-neo4j/src/main/kotlin/de/fraunhofer/aisec/cpg/v2/Persistence.kt#L140

Added line #L140 was not covered by tests
setOf(
String::class.createType(),
Int::class.createType(),
Long::class.createType(),
Boolean::class.createType(),
Name::class.createType(),
Uuid::class.createType(),
)

val Node.schemaProperties: Map<String, KProperty1<out Node, *>>
get() {
val klazz = this::class

// Check, if we already computed the labels for this node's class
return schemaPropertiesCache.computeIfAbsent(klazz) {
val schema = mutableMapOf<String, KProperty1<out Node, *>>()
val properties = it.memberProperties
for (property in properties) {
if (property.returnType.withNullability(false) in primitiveTypes) {
schema.put(property.name, property)
}
}
schema
}
}

0 comments on commit cb1dfa5

Please sign in to comment.