Skip to content
This repository has been archived by the owner on Jan 19, 2022. It is now read-only.

Commit

Permalink
Merge branch 'master' of github.com:SpringSource/grails-data-mapping
Browse files Browse the repository at this point in the history
  • Loading branch information
graemerocher committed Dec 12, 2011
2 parents 956da8f + b440bcf commit 4daa360
Show file tree
Hide file tree
Showing 30 changed files with 462 additions and 18 deletions.
3 changes: 3 additions & 0 deletions grails-datastore-gorm-neo4j/build.gradle
Expand Up @@ -7,7 +7,10 @@ dependencies {
//runtime "org.apache.lucene:lucene-core:3.1.0" // looks like neo4j 1.4.1. has no real dependency on lucene-core
testCompile project(":grails-datastore-gorm-test"),
project(":grails-datastore-gorm-tck")
testRuntime "javax.servlet:servlet-api:2.5"
testRuntime "org.grails:grails-gorm:$grailsVersion"
testRuntime "org.grails:grails-web:$grailsVersion"

}

/*
Expand Down
2 changes: 1 addition & 1 deletion grails-documentation-neo4j/build.gradle
Expand Up @@ -2,7 +2,7 @@ configurations {
documentation
}
dependencies {
documentation group: 'org.grails', name: 'grails-docs', version: '1.3.+'
documentation group: 'org.grails', name: 'grails-docs', version: '2.0.0.RC3'
project(":grails-datastore-core")
documentation "org.slf4j:jcl-over-slf4j:1.5.8"
documentation "org.slf4j:slf4j-api:1.5.8"
Expand Down
@@ -0,0 +1,84 @@
As mentioned the GORM for Neo4j plugin will configure all the defaults for you, but if you wish to customize those defaults you can do so in the your @grails-app/conf/DataSource.groovy@ file:

{code}
grails {
neo4j {
type = "embedded"
location = "/var/neo4j"
params = []
}
}
{code}

The @type@ provides currently the following choices:

h4. type = "embedded"
Runs the Neo4j in embedded mode, Neo4j and Tomcat use the same JVM. No seperate setup outside the Grails application is required. @location@ specifies the directory where Neo4j stores its data.

Example:
{code}
grails {
neo4j {
type = "embedded"
location = "/var/neo4j"
}
}
{code}

{note}
If your configuration is empty, 'embedded' is used as default.
{note}

h4. type = "rest"
Uses a @org.neo4j.rest.graphdb.RestGraphDatabase@ instance to connect to a Neo4j database. See http://docs.neo4j.org/chunked/stable/server.html for how to setup a Neo4j server.

@location@ specifies the URL of he Neo4j REST server.

Example:
{code}
grails {
neo4j {
type = "rest"
location = "http://localhost:7474/db/data/"
}
}
{code}

Additionally you must add another dependency to your application's @grails-app/conf/BuildConfig.groovy@:

{code}
compile('org.neo4j:neo4j-enterprise:1.5')
{code}

{node}
For Neo4j HA either a commercial license is required, or you could use AGPL, see http://neo4j.org/licensing-guide/
{node}

h4. type = "ha"
Uses a Neo4j HA setup, for details see http://docs.neo4j.org/chunked/stable/ha.html. In this case params must at least contain

Example:
{code}
grails {
neo4j {
type = "ha"
location = "/var/neo4j"
params = [ // see http://docs.neo4j.org/chunked/stable/ha-configuration.html
'ha.server_id': 1,
'ha.coordinators': 'localhost:2181,localhost:2182,localhost:2183'
]
}
}
{code}

h4. custom graph database
If you use a custom implementation of GraphDatabaseService, you can use
{code}
grails {
neo4j {
type = "my.fancy.custom.GraphDatabaseServiceImplementation"
location = "/var/neo4j"
params = [ :]
}
}
{code}
@@ -0,0 +1,16 @@
If you have both the Hibernate and Neo4j plugins installed then by default all classes in the @grails-app/domain@ directory will be persisted by Hibernate and not Neo4j. If you want to persist a particular domain class with Neo4j then you must use the @mapWith@ property in the domain class:

{code}
static mapWith = "neo4j"
{code}

Alternatively you can persist Hibernate entities to Neo4j using the special @neo4j@ scope added to all Hibernate entities:

{code}
def hibernatePerson = Person.get(1)

hibernatePerson.neo4j.save()

def neo4jPerson = Person.neo4j.get(1)
{code}

17 changes: 17 additions & 0 deletions grails-documentation-neo4j/src/docs/guide/gettingStarted.gdoc
@@ -0,0 +1,17 @@
To get started with GORM for Neo4j you need to install the plugin into a Grails application:

{code}
grails install-plugin neo4j
{code}

Or configure it as a dependency in @BuildConfig.groovy@:

{code}
plugins {
compile "\:neo4j:latest.version"
}
{code}

By default Neo4j will used as embedded database inside the JVM, the default directory for the Neo4j datastore is @data/neo4j@.


Expand Up @@ -5,7 +5,7 @@ The goal of this grails-data-mapping subproject is to provide a 'as-complete-as-
* Marshalling from Neo4j nodespace to Groovy/Java types and back again
* Support for GORM dynamic finders, criteria and named queries
* Session-managed transactions
* Validating domain instances backed by the Mongo datastore
* Validating domain instances
* access to Neo4j's traversal capabilities
* access the Neo4j graph database in embedded and REST flavour
* access the Neo4j graph database in all flavours (Embedded, REST and HA)

5 changes: 5 additions & 0 deletions grails-documentation-neo4j/src/docs/guide/mapping.gdoc
@@ -0,0 +1,5 @@
Starting with "Neo4j's reference node":http://api.neo4j.org/current/org/neo4j/graphdb/GraphDatabaseService.html#getReferenceNode(), each domain class itself is represented by a subreference node. The subreference node has a relationship to the reference node. Each instance of this domain class is represented by a node connected to the respective subreference node.
All simple properties of the domain class are mapping to the node's properties. References to other domain classes are mapped by relationships.

TODO: add image illustrating this

@@ -1,5 +1,11 @@
Below are the details of the changes across releases:

h4. 1.0-SNAPSHOT

* works with Neo4j HA
* implementing new GORM property criteria filters
* uses Neo4j 1.5s

h4. 0.9-SNAPSHOT
* first GORM compliant version of the plugin
* works with embedded and REST Neo4j databases
Expand Down
Empty file.
11 changes: 11 additions & 0 deletions grails-documentation-neo4j/src/docs/guide/toc.yml
@@ -0,0 +1,11 @@
introduction:
title: Introduction
compatibility: Compatibility with GORM for Hibernate
releaseNotes: Release Notes
gettingStarted:
title: Getting Started
usingNeo4jStandalone: Using Neo4j Standalone
combiningNeo4jAndHibernate: Combining Neo4j And Hibernate
advancedConfiguration: Advanced Configuration
mapping: Mapping domain classes to Neo4j node space
sampleApp: Sample Application
@@ -0,0 +1,19 @@
If you plan to use Neo4j as your primary datastore then you need to uninstall the Hibernate plugin:

{code}
grails uninstall-plugin hibernate
{code}

Or if it has been defined in the @grails-app/conf/BuildConfig.groovy@ file comment out the hibernate line in the plugins block

{code}
compile "\:hibernate:$grailsVersion"
{code}

With this done all domain classes in grails-app/domain will be persisted via Neo4j and not Hibernate. You can create a domain class by running the regular @create-domain-class@ command:

{code}
grails create-domain-class Person
{code}

The @Person@ domain class will automatically be a persistent entity that can be stored in Neo4j.
@@ -0,0 +1,42 @@
h1. createInstanceForNode

h2. Purpose

Retrieve a domain class instance associated with the given node.

h2. Example

Assume some domain class:
{code}
class Person {
String name
}
{code}


{code}
def person = new Person(name:'Joe')
person.save(flush:true)

// somewhere later

Node node = .... // some code returning a Neo4j node
def samePerson = Person.createInstanceForNode(node)

// works also with nodeId
samePerson = Person.createInstanceForNode(person.id)
{code}

h2. Description

{code}def createInstanceForNode(nodeOrId) {code}

@createInstanceForNode@ may by invoke statically on each domain class and returns a domain class instance that is
represented by that node.

h3. Arguments

@nodeOrId@: a Neo4j node or the node's id

h3. Return value
A domain class instance or null if the node does not represent an entity.
@@ -0,0 +1,16 @@
h1. getNode

h2. Purpose

Provide acces to the Neo4j node associated with this domain instance.

h2. Example

{code}
def person = Person.findByName("Joe")
def node = person.node
{code}

h2. Description

Returns a "Neo4j's Node":http://api.neo4j.org/current/org/neo4j/graphdb/Node.html instance.
@@ -0,0 +1,7 @@
h1. schemaless attributes

h2. Purpose

h2. Example

h2. Description
@@ -0,0 +1,48 @@
h1. schemaless attributes

h2. Purpose

For domain classes mapped by Neo4j you can put and get arbitrary attributes on a instances by using map semantics on the
domain instance.

{note}
Setting arbitrary attribute in only allowed when the domain instance is persisted (aka @save@ has been called)!
{note}

h2. Example

A simple domain class:
{code}
class Person implements Serializable {
String firstName
String lastName
Integer age = 0
}
{code}

{code}
when:
def person = new Person(lastName:'person1').save()
person['notDeclaredProperty'] = 'someValue' // n.b. the 'dot' notation is not valid for undeclared properties
person['emptyArray'] = []
person['someIntArray'] = [1,2,3]
person['someStringArray'] = ['a', 'b', 'c']
person['someDoubleArray'] = [0.9, 1.0, 1.1]
session.flush()
session.clear()
person = Person.get(person.id)

then:
person['notDeclaredProperty'] == 'someValue'
person['lastName'] == 'person1' // declared properties are also available via map semantics
person['someIntArray'] == [1,2,3]
person['someStringArray'] == ['a', 'b', 'c']
person['someDoubleArray'] == [0.9, 1.0, 1.1]
{code}

h2. Description

The non declared attribtes are stored a regular properties on the domain instance's node. The values of the schemaless
attributes must be a valid type for Neo4j property (String, primitives and arrays of the former two).


@@ -0,0 +1,61 @@
h1. traverse

h2. Purpose

Perform a Neo4j node taversal starting at the node of this domain instance.

h2. Example

{code}
given:
def person = new Person(lastName: "person1")
person.save()
new Person(lastName: "person2").save()

when:
def traverserResult = person.traverse(Traverser.Order.BREADTH_FIRST, StopEvaluator.END_OF_GRAPH, ReturnableEvaluator.ALL_BUT_START_NODE,
GrailsRelationshipTypes.INSTANCE, Direction.BOTH, GrailsRelationshipTypes.SUBREFERENCE, Direction.BOTH)
def size = traverserResult.size()

then:

size == person.traverse(StopEvaluator.END_OF_GRAPH, ReturnableEvaluator.ALL_BUT_START_NODE,
GrailsRelationshipTypes.INSTANCE, Direction.BOTH, GrailsRelationshipTypes.SUBREFERENCE, Direction.BOTH).size()

size+1 == person.traverse(Traverser.Order.BREADTH_FIRST,
{ TraversalPosition p -> false },
{ TraversalPosition p -> true },
GrailsRelationshipTypes.INSTANCE, Direction.BOTH, GrailsRelationshipTypes.SUBREFERENCE, Direction.BOTH).size()

size+1 == person.traverse(
{ TraversalPosition p -> false },
{ TraversalPosition p -> true },
GrailsRelationshipTypes.INSTANCE, Direction.BOTH, GrailsRelationshipTypes.SUBREFERENCE, Direction.BOTH).size()

size+1 == person.traverse(
{ TraversalPosition p -> false },
{ TraversalPosition p -> true } ).size()

Person.count() == person.traverse(
{ TraversalPosition p -> false },
{ TraversalPosition p -> p.currentNode().getProperty("__type__",null) == Person.name } ).size()

2 == person.traverse(
{ TraversalPosition p -> true },
{ TraversalPosition p -> true } ).size()
{code}

h2. Description

@traverse@ is invoked on any domain instance and comes in 4 variants

{code}
def traverse(Traverser.Order order, StopEvaluator stopEvaluator, ReturnableEvaluator returnableEvaluator, Object... args )
def traverse(StopEvaluator stopEvaluator, ReturnableEvaluator returnableEvaluator, Object... args)
def traverse(Closure stopEvaluator, Closure returnableEvaluator, Object... args)
def traverse(Traverser.Order order, Closure stopEvaluator, Closure returnableEvaluator, Object... args)
{code}

All variants return a collection of domain class instances (if the matching nodes can be mapped to a domain class) or of nodes.
See also the "Neo4j API":http://api.neo4j.org/current/

0 comments on commit 4daa360

Please sign in to comment.