Efficiently creating a large number of nodes

It is possible to efficiently insert a large number of nodes (up to
hundreds of thousands of millions) in a single request using the
clojurewerkz.neocons.rest.nodes/create-batch function:

(ns neocons.docs.examples(:require[clojurewerkz.neocons.rest:asnr][clojurewerkz.neocons.rest.nodes:asnn]))(defn -main[&args];; efficiently insert a batch of nodes, can handle hunreds of thousands or even millions of;; nodes with reasonably small heaps. Returns a lazy sequence, for it with clojure.core/doall;; if you want to parse & calculate the entire response at once.(let [conn(nr/connect"http://localhost:7474/db/data/")nodes(nn/create-batchconn[{:url"http://clojureneo4j.info":domain"clojureneo4j.info"}{:url"http://clojuremongodb.info":domain"clojuremongodb.info"}{:url"http://clojureriak.info":domain"clojureriak.info"}])];; printing here will force the lazy response sequence to be evaluated(println nodes)))

It returns a lazy sequence of results, so if you want to retrieve them all at once, force the evaluation with
clojure.core/doall.

Nodes are just Clojure maps

The function returns a new node which is a Clojure record but for all
intents and purposes should be treated and handled as a map. In Neo4J,
nodes have identifiers so :id key for created and fetched nodes is
always set. Node identifiers are used by various Neocons API
functions.

Fetched nodes also have the :location-uri and :data
fields. :location-uri returns a URI from which a node can be fetched
again with a GET request. Location URI is typically not used by
applications. :data, however, contains node properties and is very
commonly used.

Creating relationships

Now that we know how to create nodes, lets create two nodes
representing two Web pages that link to each other and add a directed
relationship between them:

Relationships are just Clojure maps

Similarly to nodes, relationships are technically records with a few
mandatory attributes:

:id

:start

:end

:type

:data

:location-uri

:id, :data and :location-uri serve the same purpose as with
nodes. :start and :end return location URIs of nodes on both ends
of a relationship. :type returns relationship type (like "links" or
"friend" or "connected-to").

Just like nodes, created relationships have :id set on them.

clojurewerkz.neocons.rest.relationships/create-many is a function
that lets you creates multiple relationships for a node, all with the
same direction and type. It is covered in the Populating the graph
guide.

Node and relationship attributes

Nodes and relationships in Neo4J have properties (attributes). It is
possible for a node or relationship to not have any properties. The
semantics of properties varies from application to application. For
example, in a social application nodes that represent people may have
:name and :date-of-birth properties, while relationships may have
:created_at properties that store a moment in time when two people
have met.

Node properties

Nodes properties are passed to the
clojurewerkz.neocons.rest.nodes/create function when a node is
created. In the following example, a node is created with two
properties, :url and :domain:

Relationship properties

Relationship properties are very similar to node properties. They are
passed to the clojurewerkz.neocons.rest.relationship/create function
when a relationship is created. In the following example, a
relationship between two nodes is created with two properties,
:link-text and :created-at:

Indexes

Indexes are data structures that data stores maintain to make certain
queries significantly faster. Nodes and relationships in Neo4J are
typically retrieved by id but this is not always convenient. Often
what's needed is a way to efficiently retrieve a node by email or URL
or other attribute other than :id. Indexes make that possible. In
this sense Neo4J is not very different from other databases.

You can index nodes and relationships and have as many indexes as you
need (within the limit of Neo4J server disk and RAM resources).

Indexing of nodes

Before nodes can be indexed, an index needs to be created. Neo4J has a
feature called automatic
indexes
but it may be disabled via server configuration, so typically it is a
good idea to just create an index and use it.

clojurewerkz.neocons.rest.nodes/create-index is the function to use
to create a new index for nodes. Indexes can be created with a
specific configuration: it determines whether it is a regular or
full text search index and allows for specifying additional index
parameters
(like analyzer for full text search indexes).

(ns neocons.docs.examples(:require[clojurewerkz.neocons.rest:asnr][clojurewerkz.neocons.rest.nodes:asnn]))(defn -main[&args];; creates a new full text search index with the given analyzer(let [conn(nr/connect"http://localhost:7474/db/data/")idx(nn/create-indexconn"imported"{:type"fulltext":provider"lucene":analyzer"org.neo4j.index.impl.lucene.LowerCaseKeywordAnalyzer"})](println idx)))

To add a node to an index, use
clojurewerkz.neocons.rest.nodes/add-to-index. To remove a node from
an index, use clojurewerkz.neocons.rest.nodes/delete-from-index.

It returns a (possibly empty) collection of nodes found. There is also
a similar function, clojurewerkz.neocons.rest.nodes/find-one, that
works just like find but assumes there only ever going to be a
single node with the given key in the index, so it can be returned
instead of a collection with the only value.

With full text search indexes, the function to use is clojurewerkz.neocons.rest.nodes/query:

Indexing of relationships

Similarly to node indexes, relationship indexes typically need to be
created before they are used.
clojurewerkz.neocons.rest.nodes/create-index is the function to use
to create a new relationship index. Just like node Indexes,
relationship ones can be created with a specific configuration.

(ns neocons.docs.examples(:require[clojurewerkz.neocons.rest:asnr][clojurewerkz.neocons.rest.relationships:asnrel]))(defn -main[&args];; creates a new full text search index with the given analyzer(let [conn(nr/connect"http://localhost:7474/db/data/")idx(nrel/create-indexconn"imported"{:type"fulltext":provider"lucene":analyzer"org.neo4j.index.impl.lucene.LowerCaseKeywordAnalyzer"})](println idx)))

To add a relationship to an index, use
clojurewerkz.neocons.rest.relationships/add-to-index. To remove a
relationship from an index, use
clojurewerkz.neocons.rest.relationships/delete-from-index.

There is also a similar function,
clojurewerkz.neocons.rest.relationships/find-one, that works just
like find but assumes there only ever going to be a single
relationship with the given key in the index, so it can be returned
instead of a collection with the only value.

With full text search indexes, the function to use is
clojurewerkz.neocons.rest.relationships/query:

clojurewerkz.neocons.rest.relationships/maybe-delete will delete a
relationship by id but only if it exists. Otherwise it just does
nothing. Unlike nodes, relationships can be deleted without any
restrictions, so there is no
clojurewerkz.neocons.rest.relationships/destroy.

Performing batch operations via Neo4J REST API

Neocons supports batch operations via Neo4J REST API. The API is
fairly low level but is very efficient (can handle millions of
operations per request). To use it, you pass a collection of maps to
clojurewerkz.neocons.rest.batch/perform:

(ns neocons.docs.examples(:require[clojurewerkz.neocons.rest:asnr][clojurewerkz.neocons.rest.nodes:asnn]))(defn -main[&args];; efficiently executes a batch of operations in a single HTTP request, can handle hunreds of thousands or even millions of;; nodes with reasonably small heaps. Returns a lazy sequence, for it with clojure.core/doall;; if you want to parse & calculate the entire response at once.(let [conn(nr/connect"http://localhost:7474/db/data/")ops[{:method"POST":to"/node":body{}:id0}{:method"POST":to"/node":body{}:id1}{:method"POST",
:to"/node/0/relationships",
:body{:to1:data{}:type"knows"}:id2}]res(doall (b/performconnops))];; printing here will force the lazy response sequence to be evaluated(println res)))

Performing Operations in a Transaction (Neo4J 2.0+)

Neocons 2.0 and later support Neo4j 2.0's transactions over HTTP
API. This
API only accepts a series of Cypher statements and can not be used
along side the other REST APIs.

To create a cypher statement, you can use the
clojurewerkz.neocons.rest.transaction/statement method which takes a
cypher query string and an optional map of parameters.

Similarly you can use the clojurewerkz.neocons.rest.transaction/rollback method instead of
clojurewerkz.neocons.rest.transaction/commit to rollback the existing transaction.

For encapulating the commit-on-success and rollback-on-error pattern, you can use the
clojurewerkz.neocons.rest.transaction/with-transaction macro which has
parameters: transaction, commit-on-success? and a body. If commit-on-success?
is false then the user will have to manually commit the transaction. This can be
useful if you wanted to test changes made by cypher statement without actually committing
them to the database. If there are any errors in the body or any cypher errors in
any statement sent the server, the transaction will automatically be rolled back.

Node Labels (Neo4J 2.0+)

Neo4j 2.0 added the concept of
Labels
and clojurewerkz.neocons.rest.labels implements that functionality
over HTTP
API. A
label is not a property of a node; rather, it is a category that a node is
placed in to permit indexing and constraining of some property.

An example which shows the basic functionality is listed below.

(ns neocons.docs.examples(:require[clojurewerkz.neocons.rest:asnr][clojurewerkz.neocons.rest.nodes:asnn][clojurewerkz.neocons.rest.labels:asnl]))(defn -main[&args]; create a node(let [conn(nr/connect"http://localhost:7474/db/data/")node (nn/createconn{:name"Clint Eastwood"})]; Add a single label to the node(nl/addconnnode "Person"); or, add multiple labels to the node(nl/addconnnode ["Person""Actor"]); replace the current labels with new ones(nl/replaceconnnode ["Actor""Director"]); remove a particular label(nl/removeconnnode "Person"); listing all labels for a node(println (nl/get-all-labelsconnnode)); list all labels for the whole graph db(println (nl/get-all-labelsconn)); getting all nodes with a label(println (nl/get-all-nodesconn"Actor")); get nodes by label and property(println (nl/get-all-nodesconn"Person":name"Client Eastwood"))))

Schema & Constraints (Neo4J 2.0+)

Since Neo4j 2.0, you can add schema meta information for speed
improvements or modeling benefits. They fall into two categories,
Indices and Constraints. These features are very new and subject to
change.

Indexing

(ns neocons.docs.examples(:require[clojurewerkz.neocons.rest:asnr][clojurewerkz.neocons.rest.index:asni]))(defn -main[&args](let [conn(nr/connect"http://localhost:7474/db/data/")]; create an index on a label and a property name(ni/createconn"Person":name); get all indices on a label(println (ni/get-allconn"Person")); drop an index on a label and a property name(ni/drop"Person"conn:name)))

Constraints

(ns neocons.docs.examples(:require[clojurewerkz.neocons.rest:asnr][clojurewerkz.neocons.rest.constraints:asnc]))(defn -main[&args](let [conn(nr/connect"http://localhost:7474/db/data/")]; create an uniqueness constraint on a label and a property name(nc/create-uniqueconn"Person":name); get an existing uniqueness constraint on a label and a property name(println (nc/get-uniqueconn"Person":name)); get all existing uniquness constraints on a label(println (nc/get-uniqueconn"Person")); get all existing constraints on a label(println (nc/get-allconn"Person")); get all existing constraints for the whole graph db(println (nc/get-allconn)); drop an existing uniqueness constraint on a label and a property name(nc/drop-constraintconn"Person":name)))

What to read next

The documentation is organized as a number of guides, covering all kinds of topics.

We recommend that you read the following guides first, if possible, in this order: