org.apache.tinkerpop.gremlin.ogm.GraphMapper.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlin-gremlin-ogm Show documentation
Show all versions of kotlin-gremlin-ogm Show documentation
The Object Graph Mapping Library for Kotlin and Gremlin
@file:Suppress("unused")
package org.apache.tinkerpop.gremlin.ogm
import org.apache.tinkerpop.gremlin.ogm.elements.Edge
import org.apache.tinkerpop.gremlin.ogm.elements.Vertex
import org.apache.tinkerpop.gremlin.ogm.exceptions.UnregisteredClass
import org.apache.tinkerpop.gremlin.ogm.extensions.toMultiMap
import org.apache.tinkerpop.gremlin.ogm.extensions.toOptionalMap
import org.apache.tinkerpop.gremlin.ogm.extensions.toSingleMap
import org.apache.tinkerpop.gremlin.ogm.mappers.EdgeDeserializer
import org.apache.tinkerpop.gremlin.ogm.mappers.EdgeSerializer
import org.apache.tinkerpop.gremlin.ogm.mappers.VertexDeserializer
import org.apache.tinkerpop.gremlin.ogm.mappers.VertexSerializer
import org.apache.tinkerpop.gremlin.ogm.paths.Path
import org.apache.tinkerpop.gremlin.ogm.paths.bound.*
import org.apache.tinkerpop.gremlin.ogm.paths.steps.StepTraverser
import org.apache.tinkerpop.gremlin.ogm.reflection.GraphDescription
import org.apache.tinkerpop.gremlin.ogm.traversals.*
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource
import org.slf4j.LoggerFactory
import kotlin.reflect.KClass
import kotlin.reflect.full.isSubclassOf
/**
* The main object a client's application will interact with.
* This interface provides the ability to map objects from a clients domain to the graph and back.
* The default implementation of all methods are thread-safe.
*/
interface GraphMapper {
val graphDescription: GraphDescription
val traversal: GraphTraversalSource
/**
*
* Vertices
*
*/
/**
* Gets a graph traversal that emits a vertex with a given id.
*/
fun V(id: Any): GraphTraversalToOptional<*, V> = V(setOf(id)).toOptional()
/**
* Gets a graph traversal that emits vertices for given ids.
* No exception is thrown for ids that don't correspond to a vertex, thus the number of vertices the traversal emits
* may be less than the number of ids.
*/
fun V(vararg ids: Any): GraphTraversalToMany<*, V> = V(ids.toSet())
/**
* Gets a graph traversal that emits vertices for given ids.
* No exception is thrown for ids that don't correspond to a vertex, thus the number of vertices the traversal emits
* may be less than the number of ids.
*/
fun V(ids: Set): GraphTraversalToMany<*, V> {
return if (ids.none()) {
traversal.inject()
} else {
traversal.V(*ids.toTypedArray()).map { vertex ->
deserialize(vertex.get())
}
}.toMany()
}
/**
* Get a traversal that emits all vertices for class V (which has been registered as a vertex with this GraphMapper).
* V may be a superclass of classes registered as a vertex.
*/
fun V(kClass: KClass, then: GraphTraversal<*, GraphVertex>.() -> GraphTraversal<*, GraphVertex> = { this }): GraphTraversalToMany<*, V> {
val labels = graphDescription.vertexClasses.filter { vertexKClass ->
vertexKClass.isSubclassOf(kClass)
}.map { vertexClass ->
graphDescription.getVertexDescription(vertexClass).label
}
if (labels.isEmpty()) throw UnregisteredClass(kClass)
logger.debug("Will get all vertices with labels $labels")
return labels.map { label ->
traversal.V().hasLabel(label)
}.reduce { traversal1, traversal2 ->
traversal.V().union(traversal1, traversal2)
}.then().map { vertex ->
deserialize(vertex.get())
}.toMany()
}
/**
* Saves vertices to the graph. If the property annotated with @ID is null,
* a new vertex will be created, otherwise this object will overwrite the current vertex with that id.
* The returned object will always have a non-null @ID. If the property annotated with @ID is non-null,
* but the vertex cannot be found, an exception is thrown.
*/
fun saveV(vararg objs: V): List = objs.map { saveV(it) }
/**
* Saves vertices to the graph. If the property annotated with @ID is null,
* a new vertex will be created, otherwise this object will overwrite the current vertex with that id.
* The returned object will always have a non-null @ID. If the property annotated with @ID is non-null,
* but the vertex cannot be found, an exception is thrown.
*/
fun saveV(objs: Iterable): List = objs.map { saveV(it) }
/**
* Saves an object annotated with @Element to the graph. If property annotated with @ID is null,
* a new vertex will be created, otherwise this object will overwrite the current vertex with that id.
* The returned object will always have a non-null @ID. If the property annotated with @ID is non-null,
* but the vertex cannot be found, an exception is thrown.
*/
fun saveV(deserialized: V): V {
val serialized = serialize(deserialized)
logger.debug("Saved ${serialized.label()} vertex with id: ${serialized.id()}\n")
return deserialize(serialized)
}
/**
*
* Edges
*
*/
/**
* Gets a graph traversal that emits an edge with the given id.
*/
fun > E(id: Any): GraphTraversalToOptional<*, E> = E(setOf(id)).toOptional()
/**
* Gets a graph traversal that emits edges for given ids.
* No exception is thrown for ids that don't correspond to an edge, thus the number of edges the traversal emits
* may be less than the number of ids.
*/
fun > E(vararg ids: Any): GraphTraversalToMany<*, E> = E(ids.toSet())
/**
* Gets a graph traversal that emits edges for given ids.
* No exception is thrown for ids that don't correspond to an edge, thus the number of edges the traversal emits
* may be less than the number of ids.
*/
fun > E(ids: Set): GraphTraversalToMany<*, E> {
return if (ids.none()) {
traversal.inject()
} else {
traversal.E(*ids.toTypedArray()).map { edge ->
deserialize(edge.get())
}
}.toMany()
}
/**
* Get a traversal that emits all edges for class E (which has been registered with a relationship as
* an edge with this GraphMapper).
*/
fun > E(
kClass: KClass,
then: GraphTraversal<*, GraphEdge>.() -> GraphTraversal<*, GraphEdge> = { this }
): GraphTraversalToMany<*, E> {
val labels = graphDescription.edgeClasses.filter { edgeKClass ->
edgeKClass.isSubclassOf(kClass)
}.map { vertexClass ->
graphDescription.getEdgeDescription(vertexClass).label
}
if (labels.isEmpty()) throw UnregisteredClass(kClass)
logger.debug("Will get all edges with labels $labels")
return labels.map { label ->
traversal.E().hasLabel(label)
}.reduce { traversal1, traversal2 ->
traversal.E().union(traversal1, traversal2)
}.then().map { edge ->
deserialize(edge.get())
}.toMany()
}
/**
* Saves edges to the graph. If the property annotated with @ID is null,
* a new edge will be created, otherwise this object will overwrite the current edge with that id.
* The returned object will always have a non-null @ID.
*/
fun > saveE(vararg edges: E): List = edges.map { saveE(it) }
/**
* Saves edges to the graph. If the property annotated with @ID is null,
* a new edge will be created, otherwise this object will overwrite the current edge with that id.
* The returned object will always have a non-null @ID.
*/
fun > saveE(edges: Iterable): List = edges.map { saveE(it) }
/**
* Saves edges to the graph. If the property annotated with @ID is null,
* a new edge will be created, otherwise this object will overwrite the current edge with that id.
* The returned object will always have a non-null @ID.
*/
fun > saveE(edge: E): E {
val serialized = serialize(edge)
logger.debug("Saved ${serialized.label()} edge with id ${serialized.id()}")
return deserialize(serialized)
}
/**
*
* Traversals
*
*/
/**
* Traverses from a vertex to the path's required destination object.
*/
fun traverse(boundPath: SingleBoundPath.ToSingle): GraphTraversalToSingle<*, TO> =
traversePath(boundPath).traversal.map { it.get().second }.toSingle()
/**
* Traverses from a vertex to the path's optional destination object.
*/
fun traverse(boundPath: SingleBoundPath.ToOptional): GraphTraversalToOptional<*, TO> =
traversePath(boundPath).traversal.map { it.get().second }.toOptional()
/**
* Traverses from vertex to the path's destination objects.
*/
fun traverse(boundPath: SingleBoundPath.ToMany): GraphTraversalToMany<*, TO> =
traversePath(boundPath).traversal.map { it.get().second }.toMany()
/**
* Traverses from multiple vertices to the path's required destination object for each origin vertex.
*/
fun traverse(boundPath: BoundPath.ToSingle): Map =
traversePath(boundPath).toSingleMap(boundPath.froms)
/**
* Traverses from multiple vertices to the path's optional destination object for each origin vertex.
*/
fun traverse(boundPath: BoundPath.ToOptional): Map =
traversePath(boundPath).toOptionalMap(boundPath.froms)
/**
* Traverses from multiple vertices to the path's destination objects for each origin vertex.
*/
fun traverse(boundPath: BoundPath.ToMany): Map> =
traversePath(boundPath).toMultiMap(boundPath.froms)
/**
* Traverses from any number of vertices to the path's destination object(s) for each origin vertex.
*/
fun traversePath(boundPath: BoundPath): GraphTraversalToMany<*, Pair> {
val froms = boundPath.froms
val path = boundPath.path
if (froms.none()) {
return traversal.inject>().toMany()
}
val traversalStart = froms.fold(initial = traversal.inject()) { traversal, from ->
traversal.inject(from).`as`(fromKey)
}
@Suppress("UNCHECKED_CAST")
val traversed = path.path().fold(initial = traversalStart as GraphTraversal) { traversal, step ->
step as Path
step(StepTraverser(traversal, this)) as GraphTraversal
}
@Suppress("UNCHECKED_CAST")
return traversed.`as`(toKey).select(fromKey, toKey).map {
val map = it.get()
val from = map[fromKey] as FROM
val to = map[toKey] as TO
logger.debug("Traversed from $from to $to")
from to to
}.toMany()
}
fun > serialize(edge: E): GraphEdge =
EdgeSerializer(graphDescription, traversal)(edge)
fun > deserialize(graphEdge: GraphEdge): E =
EdgeDeserializer(graphDescription)(graphEdge)
fun serialize(vertex: V): GraphVertex =
VertexSerializer(graphDescription, traversal)(vertex)
fun deserialize(graphVertex: GraphVertex): V =
VertexDeserializer(graphDescription)(graphVertex)
fun vertexID(vertex: V): Any? {
if (graphDescription.vertexClasses.contains(vertex::class)) {
val vertexDescription = graphDescription.getVertexDescription(vertex::class)
return vertexDescription.id.property.get(vertex)
}
return null
}
fun > edgeID(edge: E): Any? {
if (graphDescription.edgeClasses.contains(edge::class)) {
val edgeDescription = graphDescription.getEdgeDescription(edge::class)
return edgeDescription.id.property.get(edge)
}
return null
}
companion object {
private const val fromKey = "from"
private const val toKey = "to"
private val logger = LoggerFactory.getLogger(GraphMapper::class.java)
}
}
typealias GraphVertex = org.apache.tinkerpop.gremlin.structure.Vertex
typealias GraphEdge = org.apache.tinkerpop.gremlin.structure.Edge
/**
* Get a traversal that emits all vertices for class V (which has been registered as a vertex with this GraphMapper).
* V may be a superclass of classes registered as a vertex.
*/
inline fun GraphMapper.allV(
noinline then: GraphTraversal<*, GraphVertex>.() -> GraphTraversal<*, GraphVertex> = { this }
): GraphTraversalToMany<*, V> = V(V::class, then)
/**
* Get a traversal that emits all edges for class E (which has been registered with a relationship as
* an edge with this GraphMapper).
*/
inline fun > GraphMapper.allE(
noinline then: GraphTraversal<*, GraphEdge>.() -> GraphTraversal<*, GraphEdge> = { this }
): GraphTraversalToMany<*, E> = E(E::class, then)
© 2015 - 2024 Weber Informatics LLC | Privacy Policy