Kotlin wrapper for NFGraph along with new features

61 views
Skip to first unread message

Jayson Minard

unread,
Apr 10, 2015, 12:30:23 AM4/10/15
to netfli...@googlegroups.com
I have started a Kotlin wrapper for NFGraph (is possible to use from Java, but will look less sexy)

Besides working on various syntax to make things easier to read and use, I have changed it to expect Enum for node types and relationship types.  This means that your code will be more "safe" with less string usage, only the primary keys are strings and typically they come from other systems instead of hardcoded string values.

Added support for ambiguous relations such as [A -> R1 -> B] and [A -> R1 -> C] by grouping them on the surface, and then aliasing to [A -> R1.B -> B] and [A -> R1.C -> C] under the covers.  When asking for connections of [A -> R1] can then get back mixed node types of B and C, therefore the results now include the node type enum.  The handling of ordinals is built-in for both graph building and when using the graph later.  Serialization of ordinals and extra information of the schema is also handled.  

Included is automatic reverse edges so you can just set one direction of a relationship and the backwards relationship is automatically added.

It's rough, but feedback welcome.  Here is a test case using the features:


... it'll come along as I use it in our system and flush out more methods, optimize things (about to start building a 9 million node graph with 5-6 times the number of edges)...

enjoy.

Jayson Minard

unread,
Apr 10, 2015, 12:34:42 AM4/10/15
to netfli...@googlegroups.com
better syntax highlighting shows the syntax more clearly (check github for more updated version)...

package com.collokia.kommon.nfgraph

import com.collokia.kommon.nfgraph.MyNodes.*
import com.collokia.kommon.nfgraph.MyRelations.*
import com.collokia.kommon.nfgraph.internal.NodeAndId
import com.collokia.kommon.nfgraph.internal.RelationStructure
import org.junit.Test
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import kotlin.test.assertEquals
import kotlin.test.assertTrue


enum class MyNodes {
Movie
Actor
Director
Award
}

enum class MyRelations {
StarredIn
Starring
DirectedBy
Directed
WonAward
AwardWinner
}

public class TestNfGraph {
private fun Set<NodeAndId<MyNodes>>.hasOnly(testFor: Set<NodeAndId<MyNodes>>): Boolean {
return (this.size() == testFor.size() && this.containsAll(testFor))
}

[Test] fun basicGraphBuilding() {
val schema = defineGraphSchema<MyNodes, MyRelations>(RelationStructure.COMPACT) {
// TODO: maybe syntax like
// Movie[Starring]..Actor[StarredIn]
// need to be able to show many versus single in other syntax

from(Movie).connectEdges(Starring).to(Actor).autoMirrorEdges(StarredIn)
from(Movie).connectOneEdge(DirectedBy).to(Director).autoMirrorEdges(Directed)

// we support duplicate relations A->R->B and A->R->C by creating A->R.B->B and A->R.C->C under the covers
// here, from the perspective of Award, it has three possible WonAward relations all with same name
from(Movie).connectEdges(WonAward).to(Award).hashed().autoMirrorEdges(AwardWinner).compact()
from(Actor).connectEdges(WonAward).to(Award).autoMirrorEdges(AwardWinner)
from(Director).connectEdges(WonAward).to(Award).autoMirrorEdges(AwardWinner)

modelScope() {
// TODO: test relationships in model scope
}
}

val builder = constructGraph(schema) {
// TODO: maybe syntax options like
// Move["Star Wars"]..Starring..Actor["Harrison Ford"]
// Move["Star Wars"] - Starring - Actor["Harrison Ford"]
// Move["Star Wars"] + Starring + Actor["Harrison Ford"]

connect(Movie["Star Wars"]).edge(Starring).to(Actor["Harrison Ford"])
connect(Movie["Star Wars"]).edge(Starring).to(Actor["Mark Hamill"])
connect(Movie["Star Wars"]).edge(Starring).to(Actor["Carrie Fisher"])
connect(Movie["Star Wars"]).edge(Starring).to(Actor["Peter Mayhew"])

// less objects created version of syntax...
connect(Movie["Star Wars"], DirectedBy, Director["George Lucas"])
connect(Movie["Star Wars"], WonAward, Award["1979 Academy Award for Best Visual Effects"])
connect(Movie["Star Wars"], WonAward, Award["1979 Academy Award for Best Original Music Score"])
connect(Actor["Harrison Ford"], WonAward, Award["2000 People's Choice Award"])
connect(Director["George Lucas"], WonAward, Award["2005 AFI Life Achievement Award"])
connect(Award["1994 Academy Award for Best Picture"], AwardWinner, Director["Steven Spielberg"])
connect(Movie["Indiana Jones"], Starring, Actor["Harrison Ford"])
connect(Movie["Indiana Jones"], DirectedBy, Director["Steven Spielberg"])

connect(Actor["Harrison Ford"], WonAward, Award["People's Choice Award"])
connect(Director["Steven Spielberg"], WonAward, Award["People's Choice Award"])
connect(Movie["Star Wars"], WonAward, Award["People's Choice Award"])
connect(Movie["Indiana Jones"], WonAward, Award["People's Choice Award"])
}

// serialize so we test end to end
val outputBuffer = ByteArrayOutputStream()
builder.serialize(outputBuffer)

// bring graph out of serializatino and use
useGraph<MyNodes, MyRelations>(ByteArrayInputStream(outputBuffer.toByteArray())) {
assertTrue(Actor["Harrison Ford"].getConnections(StarredIn).hasOnly(setOf(Movie("Star Wars"), Movie("Indiana Jones"))))
assertEquals(Movie("Star Wars"), Actor["Mark Hamill"].getSingleConnection(StarredIn))
assertEquals(Movie("Star Wars"), Actor["Carrie Fisher"].getSingleConnection(StarredIn))
assertEquals(Movie("Star Wars"), Actor["Peter Mayhew"].getSingleConnection(StarredIn))
assertTrue(Movie["Star Wars"].getConnections(Starring).hasOnly(setOf(Actor("Harrison Ford"), Actor("Mark Hamill"), Actor("Carrie Fisher"), Actor("Peter Mayhew"))))
assertEquals(Director("George Lucas"), Movie["Star Wars"].getSingleConnection(DirectedBy))
assertEquals(Movie("Star Wars"), Director["George Lucas"].getSingleConnection(Directed))

assertEquals(Director("Steven Spielberg"), Award["1994 Academy Award for Best Picture"].getSingleConnection(AwardWinner))
assertEquals(Award("2005 AFI Life Achievement Award"), Director["George Lucas"].getSingleConnection(WonAward))
assertEquals(Movie("Star Wars"), Award["1979 Academy Award for Best Visual Effects"].getSingleConnection(AwardWinner))
assertEquals(Movie("Star Wars"), Award["1979 Academy Award for Best Original Music Score"].getSingleConnection(AwardWinner))

assertTrue(Award["People's Choice Award"].getConnections(AwardWinner).hasOnly(setOf(Movie("Star Wars"), Movie("Indiana Jones"), Director("Steven Spielberg"), Actor("Harrison Ford"))))
}
}
}

Reply all
Reply to author
Forward
0 new messages