All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jars.nosqlunit-documentation.1.0.0.source-code.neo4j.xml Maven / Gradle / Ivy

The newest version!
<?xml version="1.0" encoding="UTF-8"?>
<chapter version="5.0" xml:id="neo4j" xmlns="http://docbook.org/ns/docbook"
	xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xi="http://www.w3.org/2001/XInclude"
	xmlns:svg="http://www.w3.org/2000/svg" xmlns:m="http://www.w3.org/1998/Math/MathML"
	xmlns:html="http://www.w3.org/1999/xhtml" xmlns:db="http://docbook.org/ns/docbook">

	<title>Neo4j Engine</title>

	<section>
		<title>Neo4j</title>

		<para>
			<application>Neo4j</application>
			is a high-performance,
			<emphasis>NoSQL</emphasis>
			graph database with all the features of a mature and robust database.
		</para>

		<para>
			<emphasis role="bold">NoSQLUnit</emphasis>
			supports
			<emphasis>Neo4j</emphasis>
			by using next classes:
		</para>
		<para>
			<table border="1">
				<caption>Lifecycle Management Rules</caption>

				<tr>
					<td>In Memory</td>

					<td>
						<classname>com.lordofthejars.nosqlunit.neo4j.InMemoryNeo4j
						</classname>
					</td>
				</tr>

				<tr>
					<td>Embedded</td>

					<td>
						<classname>com.lordofthejars.nosqlunit.neo4j.EmbeddedNeo4j
						</classname>
					</td>
				</tr>

				<tr>
					<td>Managed Wrapping</td>

					<td>
						<classname>com.lordofthejars.nosqlunit.neo4j.ManagedWrappingNeoServer
						</classname>
					</td>
				</tr>
				<tr>
					<td>Managed</td>

					<td>
						<classname>com.lordofthejars.nosqlunit.neo4j.ManagedNeoServer
						</classname>
					</td>
				</tr>
			</table>
		</para>
		<para>
			<table border="1">
				<caption>Manager Rule</caption>

				<tr>
					<td>NoSQLUnit Management</td>

					<td>
						<classname>com.lordofthejars.nosqlunit.neo4j.Neo4jRule
						</classname>
					</td>
				</tr>
			</table>
		</para>
		<section>
			<title>Maven Setup</title>

			<para>
				To use
				<emphasis role="bold">NoSQLUnit</emphasis>
				with
				<application>Neo4j</application>
				you only need to add next
				dependency:
			</para>

			<example xml:id="conf.nosqlunit_neo4j_dep">
				<title>NoSqlUnit Maven Repository</title>

				<programlisting language="xml"><![CDATA[<dependency>
	<groupId>com.lordofthejars</groupId>
	<artifactId>nosqlunit-neo4j</artifactId>
	<version>${version.nosqlunit}</version>
</dependency>]]></programlisting>
			</example>
		</section>

		<section>
			<title>Dataset Format</title>

			<para>
				Default dataset file format in
				<emphasis>Neo4j</emphasis>
				module is
				<link xlink:href="http://graphml.graphdrawing.org/">GraphML</link>
				.
				<emphasis>GraphML</emphasis>
				is a comprehensive and easy-to-use file format for graphs.
			</para>

			<para>
				Datasets must have next
				<link linkend="ex.neo4j_dataset">
					format
				</link>
				:
			</para>

			<example xml:id="ex.neo4j_dataset">
				<title>Example of GraphML Dataset</title>

				<programlisting language="xml"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns">
    <key id="attr1" for="edge" attr.name="attr1" attr.type="float"/>
    <key id="attr2" for="node" attr.name="attr2" attr.type="string"/>
    <graph id="G" edgedefault="directed">
        <node id="1">
            <data key="attr2">value1</data>
        </node>
        <node id="2">
            <data key="attr2">value2</data>
        </node>
        <edge id="7" source="1" target="2" label="label1">
            <data key="attr1">float</data>
        </edge>
    </graph>
</graphml>]]></programlisting>
			</example>

			<para>
				where:
				<itemizedlist>
					<listitem>
						<para>
							<emphasis>graphml</emphasis>
							: the root element of the GraphML document
						</para>
					</listitem>
					<listitem>
						<para>
							<emphasis>key</emphasis>
							: description for graph element properties, you must define if
							property type is for nodes or relationships, name, and type of
							element. In our case string, int, long, float, double and boolean
							are supported.
						</para>
					</listitem>
					<listitem>
						<para>
							<emphasis>graph</emphasis>
							: the beginning of the graph representation. In our case only one
							level of graphs are supported. Inner graphs will be ignored.
						</para>
					</listitem>
					<listitem>
						<para>
							<emphasis>node</emphasis>
							: the beginning of a vertex representation. Please note that id 0
							is reserved for reference node, so cannot be used as id.
						</para>
					</listitem>
					<listitem>
						<para>
							<emphasis>edge</emphasis>
							: the beginning of an edge representation. Source and target
							attributes are filled with node id. If you want to link with
							reference node, use a 0 which is the id of root node. Note that
							label attribute is not in defined in standard definition of
							GraphML specification; GraphML supports adding new attributes to
							all GrpahML elements, and label attribute has been added to
							facilitate the creation of edge labels.
						</para>
					</listitem>
					<listitem>
						<para>
							<emphasis>data</emphasis>
							: the key/value data associated with a graph element. Data value
							will be validated against type defined in key element.
						</para>
					</listitem>
				</itemizedlist>
			</para>
		</section>

		<section>
			<title>Getting Started</title>

			<section>
				<title>Lifecycle Management Strategy</title>

				<para>
					First step is defining which lifecycle management strategy is
					required for your tests. Depending on kind of test you are
					implementing (unit test, integration test, deployment test, ...)
					you will require an in-memory approach, embedded approach, managed
					approach or remote
					approach.
				</para>
				<section>
					<title>In-memory Lifecycle</title>
					<para>
						To configure
						<emphasis role="bold">in-memory</emphasis>
						approach you should only instantiate next
						<link linkend="program.neo_inmemory_conf">rule</link>
						:
					</para>

					<example xml:id="program.neo_inmemory_conf">
						<title>In-Memory Neo4j</title>

						<programlisting language="java"><![CDATA[import static com.lordofthejars.nosqlunit.neo4j.InMemoryNeo4j.InMemoryNeo4jRuleBuilder.newInMemoryNeo4j;

@ClassRule
public static InMemoryNeo4j inMemoryNeo4j = newInMemoryNeo4j().build();]]></programlisting>
					</example>

				</section>
				<section>
					<title>Embedded Lifecycle</title>
					<para>
						To configure
						<emphasis role="bold">embedded</emphasis>
						approach you should only instantiate next
						<link linkend="program.neo_embedded_conf">rule</link>
						:
					</para>

					<example xml:id="program.neo_embedded_conf">
						<title>Embedded Neo4j</title>

						<programlisting language="java"><![CDATA[import static com.lordofthejars.nosqlunit.neo4j.EmbeddedNeo4j.EmbeddedNeo4jRuleBuilder.newEmbeddedNeo4jRule;

@ClassRule
public static EmbeddedNeo4j embeddedNeo4j = newEmbeddedNeo4jRule().build();]]></programlisting>
					</example>

					<para>
						By default embedded
						<emphasis>Neo4j</emphasis>
						rule uses next
						default values:
					</para>

					<table>
						<caption>Default Embedded Values</caption>
						<tr>
							<td>
								Target path
							</td>
							<td>
								This is the directory where
								<emphasis>Neo4j</emphasis>
								server is started and is
								<constant>target/neo4j-temp</constant>
								.
							</td>
						</tr>
					</table>
				</section>
				
				<section>
					<title>Managed Lifecycle</title>
					<para>To configure managed way, two possible approaches can be
						used:
					</para>

					<para>
						The first one is using an
						<emphasis role="bold">embedded database wrapped by a server
						</emphasis>
						.
						This is a way to give an embedded database visibility through
						network (internally we are creating a
						<classname>WrappingNeoServerBootstrapper</classname>
						instance)
						:
					</para>

					<example xml:id="program.neo_wrapped_managed_conf">
						<title>Managed Wrapped Neo4j</title>

						<programlisting language="java"><![CDATA[import static com.lordofthejars.nosqlunit.neo4j.ManagedWrappingNeoServer.ManagedWrappingNeoServerRuleBuilder.newWrappingNeoServerNeo4jRule;

@ClassRule
public static ManagedWrappingNeoServer managedWrappingNeoServer = newWrappingNeoServerNeo4jRule().port(8888).build();]]></programlisting>
					</example>

					<para>
						By default wrapped managed
						<emphasis>Neo4j</emphasis>
						rule uses next
						default values, but can be configured
						programmatically as shown in previous
						<link linkend="program.neo_wrapped_managed_conf">example</link>
						:
					</para>

					<table>
						<caption>Default Wrapped Values</caption>
						<tr>
							<td>
								Target path
							</td>
							<td>
								The directory where
								<emphasis>Neo4j</emphasis>
								server is started and is
								<constant>target/neo4j-temp</constant>
								.
							</td>
						</tr>
						<tr>
							<td>
								Port
							</td>
							<td>
								Where server is listening incoming messages is 7474.
							</td>
						</tr>
					</table>

					<para>
						The second strategy is
						<emphasis role="bold">starting and stopping an already installed
							server
						</emphasis>
						on executing machine, by calling start and stop command lines.
						Next
						<link linkend="program.neo_managed_conf">rule</link>
						should be registered:
					</para>

					<example xml:id="program.neo_managed_conf">
						<title>Managed Neo4j</title>

						<programlisting language="java"><![CDATA[import static com.lordofthejars.nosqlunit.neo4j.ManagedNeoServer.Neo4jServerRuleBuilder.newManagedNeo4jServerRule;

@ClassRule
public static ManagedNeoServer managedNeoServer = newManagedNeo4jServerRule().neo4jPath("/opt/neo4j").build();]]></programlisting>
					</example>

					<para>
						By default managed
						<emphasis>Neo4j</emphasis>
						rule uses next
						default values, but can be configured
						programmatically as shown in previous
						<link linkend="program.neo_managed_conf">example</link>
						:
					</para>

					<table border="1">
						<caption>Default Managed Values</caption>
						<tr>
							<td>
								Target path
							</td>
							<td>
								This is the directory where
								<emphasis>Neo4j</emphasis>
								process will be started and by default is
								<constant>target/neo4j-temp</constant>
								.
							</td>
						</tr>
						<tr>
							<td>
								Port
							</td>
							<td>
								Where server is listening incoming messages is 7474.
							</td>
						</tr>
						<tr>
							<td>
								Neo4jPath
							</td>
							<td>
								<emphasis>Neo4j</emphasis>
								installation directory which by default is
								retrieved from
								<varname>NEO4J_HOME</varname>
								system environment
								variable.
							</td>
						</tr>
					</table>

					<warning>
						<para>
							Versions prior to
							<emphasis>Neo4j</emphasis>
							1.8, port cannot be configured from command line, and port should
							be changed manually in
							<filename>conf/neo4j-server.properties</filename>
							. Although this restriction, if you have configured
							<emphasis>Neo4j</emphasis>
							to run through a different port, it should be specified too in
							<classname>ManagedNeoServer</classname>
							rule.
						</para>
					</warning>
				</section>
				<section>
					<title>Remote Lifecycle</title>
					<para>
						Configuring
						<emphasis role="bold">remote</emphasis>
						approach
						does not require any special rule because you (or System
						like
						<application>Maven</application>
						) is the responsible of starting and
						stopping the server. This mode
						is used in deployment tests where you
						are testing your application
						on real environment.
					</para>
				</section>
			</section>

			<section>
				<title>Configuring Neo4j Connection</title>

				<para>
					Next step is configuring
					<emphasis role="bold">Neo4j</emphasis>
					rule in charge of maintaining
					<emphasis>Neo4j</emphasis>
					graph into known state by inserting and deleting defined
					datasets.
					You must register
					<classname>Neo4jRule</classname>
					<emphasis>JUnit</emphasis>
					rule class, which
					requires a configuration parameter with
					information like host,
					port, uri or target directory.
				</para>

				<para>To make developer's life easier and code more readable, a
					fluent
					interface can be used to create these configuration objects.
					Two
					different kind of configuration builders exist.
				</para>
				<section>
					<title>In-Memory/Embedded Connection</title>
					<para>
						The first one is for configuring a connection to
						in-memory/embedded
						<emphasis>Neo4j</emphasis>
						.
					</para>

					<example xml:id="program.embedded_connection_neo4j_parameters">
						<title>Neo4j with embedded configuration</title>

						<programlisting language="java"><![CDATA[import static com.lordofthejars.nosqlunit.neo4j.EmbeddedNeoServerConfigurationBuilder.newEmbeddedNeoServerConfiguration;

@Rule
public Neo4jRule neo4jRule = new Neo4jRule(newEmbeddedNeoServerConfiguration().build());]]></programlisting>
					</example>
					<para>
						If you are only registering one embedded
						<emphasis>Neo4j</emphasis>
						instance like previous
						<link linkend="program.neo_embedded_conf">example</link>
						,
						calling
						<function>build</function>
						is enough. If you are using more than one
						<emphasis>Neo4j</emphasis>
						embedded connection like explained in
						<link linkend="advanced.simultaneous-engine-title">Simultaneous Engine</link>
						section,
						<varname>targetPath</varname>
						shall be provided by using
						<function>buildFromTargetPath</function>
						method.
					</para>
					<para>
						If you are using in-memory approach mixed with embedded approach,
						target path for in-memory instance can be found at
						<constant>InMemoryNeo4j.INMEMORY_NEO4J_TARGET_PATH</constant>
						variable.
					</para>
				</section>
				<section>
					<title>Remote Connection</title>
					<para>
						The second one is for configuring a connection to remote
						<emphasis>Neo4j</emphasis>
						server (it is irrelevant at this level if it is wrapped or not).
						Default values are:
					</para>

					<table border="1">
						<caption>Default Managed Connection Values</caption>

						<tr>
							<td>Connection URI</td>

							<td>http://localhost:7474/db/data</td>
						</tr>

						<tr>
							<td>Authentication</td>

							<td>No authentication parameters.</td>
						</tr>
					</table>

					<example xml:id="program.managed_neo4j_connection_parameters">
						<title>Neo4j with managed configuration</title>

						<programlisting language="java"><![CDATA[import static com.lordofthejars.nosqlunit.neo4j.ManagedNeoServerConfigurationBuilder.newManagedNeoServerConfiguration;

@Rule
public Neo4jRule neo4jRule = new Neo4jRule(newManagedNeoServerConfiguration().build());]]></programlisting>
					</example>
				</section>
			</section>
			<section>
				<title>Verifying Graph</title>
				<para>
					<classname>@ShouldMatchDataSet</classname>
					is also supported for
					<emphasis>Neo4j</emphasis>
					graphs but we should keep in mind some considerations.
				</para>
				<para>
					To compare two graphs, stored graph is exported into
					<link linkend="ex.neo4j_dataset">GraphML</link>
					format
					and then is compared with expected
					<emphasis>GraphML</emphasis>
					using
					<emphasis>XmlUnit</emphasis>
					framework.
					This approach implies two aspects to be considered, the
					first one
					is that although your graph does not contains any
					connection to
					reference node, reference node will
					appear too with the
					form (
					<![CDATA[<node id="0"></node>]]>
					). The other aspect is that id's are
					<emphasis>Neo4j's</emphasis>
					internal id, so when
					you write the expected file, remember to follow
					the same id
					strategy followed by
					<emphasis>Neo4j</emphasis>
					so id attribute of each node could be
					matched correctly with
					generated output. Inserted nodes' id starts from 1 (0 is reserved
					for reference node), meanwhile edges starts from 0.
				</para>
				<para>
					This way to compare graphs may change in future (although this
					strategy
					will be always supported).
				</para>
				<para>
					As I have noted in
					<link linkend="verifying_database">verification section</link>
					I find that using
					<classname>@ShouldMatchDataSet</classname>
					is a bad approach during testing because test
					readibility is
					affected negatively. So as general guide, my advice
					is to try to
					avoid using @ShouldMatchDataSet in your tests as much as possible.
				</para>
			</section>
			<section>
				<title>Full Example</title>
				<para>
					To show how to use
					<emphasis role="bold">NoSQLUnit</emphasis>
					with
					<emphasis>Neo4j</emphasis>
					,
					we are going to create a
					very simple application that counts Neo's
					friends.
				</para>
				<para>
					<link linkend="program.matrix_neo4j_manager">MatrixManager</link>
					is the business class responsible of inserting new friends and
					counting the number of Neo's friends.
				</para>
				<example xml:id="program.matrix_neo4j_manager">
					<title>Neo4j with managed configuration</title>

					<programlisting language="java"><![CDATA[public class MatrixManager {

	public enum RelTypes implements RelationshipType {
		NEO_NODE, KNOWS, CODED_BY
	}

	private GraphDatabaseService graphDb;

	public MatrixManager(GraphDatabaseService graphDatabaseService) {
		this.graphDb = graphDatabaseService;
	}

	public int countNeoFriends() {

		Node neoNode = getNeoNode();
		Traverser friendsTraverser = getFriends(neoNode);

		return friendsTraverser.getAllNodes().size();

	}

	public void addNeoFriend(String name, int age) {
		Transaction tx = this.graphDb.beginTx();
		try {
			Node friend = this.graphDb.createNode();
			friend.setProperty("name", name);
			Relationship relationship = getNeoNode().createRelationshipTo(friend, RelTypes.KNOWS);
			relationship.setProperty("age", age);
			tx.success();
		} finally {
			tx.finish();
		}
	}

	private static Traverser getFriends(final Node person) {
		return person.traverse(Order.BREADTH_FIRST, StopEvaluator.END_OF_GRAPH, ReturnableEvaluator.ALL_BUT_START_NODE,
				RelTypes.KNOWS, Direction.OUTGOING);
	}

	private Node getNeoNode() {
		return graphDb.getReferenceNode().getSingleRelationship(RelTypes.NEO_NODE, Direction.OUTGOING).getEndNode();
	}

}]]></programlisting>
				</example>

				<para>
					And now one unit test and one integration test is written:
				</para>
				<para>
					For
					<link linkend="program.matrix_neo4j_unit">unit</link>
					test we are going to use embedded approach:
				</para>
				<example xml:id="program.matrix_neo4j_unit">
					<title>Neo4j with managed configuration</title>

					<programlisting language="java"><![CDATA[import static org.junit.Assert.assertThat;
import static org.hamcrest.CoreMatchers.is;
import static com.lordofthejars.nosqlunit.neo4j.EmbeddedNeo4j.EmbeddedNeo4jRuleBuilder.newEmbeddedNeo4jRule;
import static com.lordofthejars.nosqlunit.neo4j.EmbeddedNeoServerConfigurationBuilder.newEmbeddedNeoServerConfiguration;

import javax.inject.Inject;

import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.neo4j.graphdb.GraphDatabaseService;

import com.lordofthejars.nosqlunit.annotation.UsingDataSet;
import com.lordofthejars.nosqlunit.core.LoadStrategyEnum;
import com.lordofthejars.nosqlunit.neo4j.EmbeddedNeo4j;
import com.lordofthejars.nosqlunit.neo4j.Neo4jRule;

public class WhenNeoFriendsAreRequired {

	@ClassRule
	public static EmbeddedNeo4j embeddedNeo4j = newEmbeddedNeo4jRule().build();
	
	@Rule
	public Neo4jRule neo4jRule = new Neo4jRule(newEmbeddedNeoServerConfiguration().build(), this);
	
	@Inject
	private GraphDatabaseService graphDatabaseService;
	
	@Test
	@UsingDataSet(locations="matrix.xml", loadStrategy=LoadStrategyEnum.CLEAN_INSERT)
	public void all_direct_and_inderectly_friends_should_be_counted() {
		MatrixManager matrixManager = new MatrixManager(graphDatabaseService);
		int countNeoFriends = matrixManager.countNeoFriends();
		assertThat(countNeoFriends, is(3));
	}
	
}]]></programlisting>
				</example>

				<para>
					And as
					<link linkend="program.matrix_neo4j_integration">integration test</link>
					, the managed one:
				</para>
				<example xml:id="program.matrix_neo4j_integration">
					<title>Neo4j with managed configuration</title>

					<programlisting language="java"><![CDATA[import static com.lordofthejars.nosqlunit.neo4j.ManagedWrappingNeoServer.ManagedWrappingNeoServerRuleBuilder.newWrappingNeoServerNeo4jRule;
import static com.lordofthejars.nosqlunit.neo4j.ManagedNeoServerConfigurationBuilder.newManagedNeoServerConfiguration;

import javax.inject.Inject;

import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.neo4j.graphdb.GraphDatabaseService;

import com.lordofthejars.nosqlunit.annotation.ShouldMatchDataSet;
import com.lordofthejars.nosqlunit.annotation.UsingDataSet;
import com.lordofthejars.nosqlunit.core.LoadStrategyEnum;
import com.lordofthejars.nosqlunit.neo4j.ManagedWrappingNeoServer;
import com.lordofthejars.nosqlunit.neo4j.Neo4jRule;

public class WhenNeoMeetsANewFriend {

	@ClassRule
	public static ManagedWrappingNeoServer managedWrappingNeoServer = newWrappingNeoServerNeo4jRule().build();
	
	@Rule
	public Neo4jRule neo4jRule = new Neo4jRule(newManagedNeoServerConfiguration().build(), this);
	
	@Inject
	private GraphDatabaseService graphDatabaseService;
	
	@Test
	@UsingDataSet(locations="matrix.xml", loadStrategy=LoadStrategyEnum.CLEAN_INSERT)
	@ShouldMatchDataSet(location="expected-matrix.xml")
	public void friend_should_be_related_into_neo_graph() {
		
		MatrixManager matrixManager = new MatrixManager(graphDatabaseService);
		matrixManager.addNeoFriend("The Oracle", 4);
	}
	
}]]></programlisting>
				</example>

				<para>Note that in both cases we are using the same dataset as
					initial state, which looks like:
				</para>
				<example xml:id="program.expected_neo4j_file">
					<title>matrix.xml Neo4j file</title>

					<programlisting language="xml"><![CDATA[<graphml xmlns="http://graphml.graphdrawing.org/xmlns"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns
        http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
    <key id="name" for="node" attr.name="name" attr.type="string"/>
    <key id="age" for="edge" attr.name="age" attr.type="int"/>
    <graph id="G" edgedefault="directed">
        <node id="1">
            <data key="name">Thomas Anderson</data>
        </node>
        <node id="2">
            <data key="name">Trinity</data>
        </node>
        <node id="3">
            <data key="name">Morpheus</data>
        </node>
        <node id="4">
            <data key="name">Agent Smith</data>
        </node>
        <node id="5">
            <data key="name">The Architect</data>
        </node>
        <edge id="1" source="0" target="1" label="NEO_NODE">
        </edge>
        <edge id="2" source="1" target="2" label="KNOWS">
            <data key="age">3</data>
        </edge>
        <edge id="3" source="1" target="3" label="KNOWS">
            <data key="age">5</data>
        </edge>
        <edge id="4" source="2" target="3" label="KNOWS">
            <data key="age">18</data>
        </edge>
        <edge id="5" source="3" target="4" label="KNOWS">
            <data key="age">20</data>
        </edge>
        <edge id="6" source="4" target="5" label="CODED_BY">
            <data key="age">20</data>
        </edge>
    </graph>
</graphml>]]></programlisting>
				</example>
			</section>

		</section>
	</section>

</chapter>




© 2015 - 2025 Weber Informatics LLC | Privacy Policy