= Included Procedures Overview

== Built in Help

// tag::help[]

image::{img}/apoc-help-apoc.jpg[width=600]

[cols="1m,5"]
|===
| call apoc.help('search') | lists name, description-text and if the procedure performs writes, search string is checked against beginning (package) or end (name) of procedure
|===

.helpful
[source,cypher]
----
CALL apoc.help("apoc") YIELD name, text
WITH * WHERE text IS null
RETURN name AS undocumented
----

// end::help[]

== Builtin Package and Procedure count

// tag::procedurecount[]

To find the procedure count with the package in Neo4j:

image::{img}/apoc.dbms.procedure.count.jpg[width=600]

.Cypher for getting count of procedure in a package
[source,cypher]
----
CALL dbms.procedures() YIELD name
RETURN head(split(name,".")) as package, count(*), collect(name) as procedures;
----

// end::procedurecount[]

// tag::overview[]

== Configuration Options

Set these config options in `$NEO4J_HOME/neo4j.conf`

All boolean options default to **false**, i.e. they are disabled, unless mentioned otherwise.

[cols="1m,5"]
|===
| apoc.trigger.enabled=false/true | Enable triggers
| apoc.ttl.enabled=false/true | Enable time to live background task
| apoc.ttl.schedule=5 | Set frequency in seconds to run ttl background task (default 60)
| apoc.import.file.use_neo4j_config=true | Enable reading properties: `dbms.directories.import`,`dbms.security.allow_csv_import_from_file_urls`
| apoc.import.file.enabled=true | Enable reading local files from disk
| apoc.export.file.enabled=true | Enable writing local files to disk
| apoc.jdbc.<key>.uri=jdbc-url-with-credentials | store jdbc-urls under a key to be used by apoc.load.jdbc
| apoc.es.<key>.uri=es-url-with-credentials | store es-urls under a key to be used by elasticsearch procedures
| apoc.mongodb.<key>.uri=mongodb-url-with-credentials | store mongodb-urls under a key to be used by mongodb procedures
| apoc.couchbase.<key>.uri=couchbase-url-with-credentials | store couchbase-urls under a key to be used by couchbase procedures
| apoc.jobs.scheduled.num_threads=number-of-threads | Many periodic procedures rely on a scheduled executor that has a pool of threads with a default fixed size. You can configure the pool size using this configuration property
| apoc.jobs.default.num_threads=number-of-threads | Number of threads in the default APOC thread pool used for background executions.
|===


== Manual Indexes

// tag::fulltext[]

=== Index Queries

Procedures to add to and query manual indexes

NOTE: Please note that there are (case-sensitive) http://neo4j.com/docs/developer-manual/current/#cypher-schema[automatic schema indexes], for equality, non-equality, existence, range queries, starts with, ends-with and contains!

[cols="1m,5"]
|===
| apoc.index.addAllNodes('index-name',{label1:['prop1',...],...}, {options}) | add all nodes to this full text index with the given fields, additionally populates a 'search' index field with all of them in one place
| apoc.index.addNode(node,['prop1',...]) | add node to an index for each label it has
| apoc.index.addNodeByLabel('Label',node,['prop1',...]) | add node to an index for the given label
| apoc.index.addNodeByName('name',node,['prop1',...]) | add node to an index for the given name
| apoc.index.addRelationship(rel,['prop1',...]) | add relationship to an index for its type
| apoc.index.addRelationshipByName('name',rel,['prop1',...]) | add relationship to an index for the given name
| apoc.index.removeNodeByName('name',node) remove node from an index for the given name
| apoc.index.removeRelationshipByName('name',rel) remove relationship from an index for the given name
|===

image::{img}/apoc.index.nodes-with-score.jpg[width=600]

[cols="1m,5"]
|===
| apoc.index.search('index-name', 'query') YIELD node, weight | search for the first 100 nodes in the given full text index matching the given lucene query returned by relevance
| apoc.index.nodes('Label','prop:value*') YIELD node, weight | lucene query on node index with the given label name
| apoc.index.relationships('TYPE','prop:value*') YIELD rel, weight | lucene query on relationship index with the given type name
| apoc.index.between(node1,'TYPE',node2,'prop:value*') YIELD rel, weight | lucene query on relationship index with the given type name bound by either or both sides (each node parameter can be null)
| apoc.index.out(node,'TYPE','prop:value*') YIELD node, weight | lucene query on relationship index with the given type name for *outgoing* relationship of the given node, *returns end-nodes*
| apoc.index.in(node,'TYPE','prop:value*') YIELD node, weight | lucene query on relationship index with the given type name for *incoming* relationship of the given node, *returns start-nodes*
|===

=== Index Management

[cols="1m,5"]
|===
| CALL apoc.index.list() YIELD type,name,config | lists all manual indexes
| CALL apoc.index.remove('name') YIELD type,name,config | removes manual indexes
| CALL apoc.index.forNodes('name',{config}) YIELD type,name,config | gets or creates manual node index
| CALL apoc.index.forRelationships('name',{config}) YIELD type,name,config | gets or creates manual relationship index
|===

.Add node to index example
[source,cypher]
----
match (p:Person) call apoc.index.addNode(p,["name","age"]) RETURN count(*);
// 129s for 1M People
call apoc.index.nodes('Person','name:name100*') YIELD node, weight return * limit 2
----

// end::fulltext[]

=== Schema Index Queries

Schema Index lookups that keep order and can apply limits

[cols="1m,5"]
|===
| apoc.index.orderedRange(label,key,min,max,sort-relevance,limit) yield node | schema range scan which keeps index order and adds limit, values can be null, boundaries are inclusive
| apoc.index.orderedByText(label,key,operator,value,sort-relevance,limit) yield node | schema string search which keeps index order and adds limit, operator is 'STARTS WITH' or 'CONTAINS'
|===



== Meta Graph

image::{img}/apoc.meta.graph.jpg[width=600]

Returns a virtual graph that represents the labels and relationship-types available in your database and how they are connected.

.Procedures
[cols="1m,5"]
|===
| CALL apoc.meta.graphSample() | examines the database statistics to build the meta graph, very fast, might report extra relationships
| CALL apoc.meta.graph | examines the database statistics to create the meta-graph, post filters extra relationships by sampling
| CALL apoc.meta.subGraph({labels:[labels],rels:[rel-types],excludes:[label,rel-type,...]}) | examines a sample sub graph to create the meta-graph
| CALL apoc.meta.data | examines a subset of the graph to provide a tabular meta information
| CALL apoc.meta.schema | examines a subset of the graph to provide a map-like meta information
| CALL apoc.meta.stats  yield labelCount, relTypeCount, propertyKeyCount, nodeCount, relCount, labels, relTypes, stats | returns the information stored in the transactional database statistics
|===

.Functions
[cols="1m,5"]
|===
| apoc.meta.type(value) | type name of a value (`INTEGER,FLOAT,STRING,BOOLEAN,RELATIONSHIP,NODE,PATH,NULL,UNKNOWN,MAP,LIST`)
| apoc.meta.isType(value,type) | returns a row if type name matches none if not
| apoc.meta.types(node or relationship or map) | returns a a map of property-keys to their names
|===


.isType example
[source,cypher]
----
MATCH (n:Person)
RETURN apoc.meta.isType(n.age,"INTEGER") as ageType
----

== Schema

[cols="1m,5"]
|===
| apoc.schema.assert({indexLabel:[indexKeys],...},{constraintLabel:[constraintKeys],...}, dropExisting : true) yield label, key, unique, action | drops all other existing indexes and constraints when `dropExisting` is `true` (default is `true`), and asserts that at the end of the operation the given indexes and unique constraints are there, each label:key pair is considered one constraint/label.
|===


== Locking

[cols="1m,5"]
|===
| call apoc.lock.nodes([nodes]) | acquires a write lock on the given nodes
| call apoc.lock.rels([relationships]) | acquires a write lock on the given relationship
| call apoc.lock.all([nodes],[relationships]) | acquires a write lock on the given nodes and relationships
|===

== from/toJson

.Functions
[cols="1m,5"]
|===
| apoc.convert.toJson([1,2,3]) | converts value to json string
| apoc.convert.toJson( {a:42,b:"foo",c:[1,2,3]}) | converts value to json map
| apoc.convert.toSortedJsonMap(node\|map, ignoreCase:true ) | returns a JSON map with keys sorted alphabetically, with optional case sensitivity
| apoc.convert.fromJsonList('[1,2,3]') | converts json list to Cypher list
| apoc.convert.fromJsonMap( '{"a":42,"b":"foo","c":[1,2,3]}') | converts json map to Cypher map
| apoc.convert.toTree([paths]) | creates a stream of nested documents representing the at least one root of these paths
| apoc.convert.getJsonProperty(node,key) | converts serialized JSON in property back to original object
| apoc.convert.getJsonPropertyMap(node,key) | converts serialized JSON in property back to map
| CALL apoc.convert.toTree([paths]) yield value | creates a stream of nested documents representing the at least one root of these paths
| CALL apoc.convert.setJsonProperty(node,key,complexValue) | sets value serialized to JSON as property with the given name on the node
|===

== Export / Import


=== Export to CSV

// tag::export.csv[]

`YIELD file, source, format, nodes, relationships, properties, time, rows`

[cols="1m,5"]
|===
| apoc.export.csv.query(query,file,config) | exports results from the Cypher statement as CSV to the provided file
| apoc.export.csv.all(file,config) | exports whole database as CSV to the provided file
| apoc.export.csv.data(nodes,rels,file,config) | exports given nodes and relationships as CSV to the provided file
| apoc.export.csv.graph(graph,file,config) | exports given graph object as CSV to the provided file
|===
// end::export.csv[]

=== Export to Cypher Script

include::exportCypher.adoc[leveloffset=1]

=== GraphML Import / Export

GraphML is used by other tools, like Gephi and CytoScape to read graph data.

// tag::export.graphml[]

`YIELD file, source, format, nodes, relationships, properties, time`

[cols="1m,5"]
|===
| apoc.import.graphml(file-or-url,{batchSize: 10000, readLabels: true, storeNodeIds: false, defaultRelationshipType:"RELATED"}) | imports graphml into the graph
| apoc.export.graphml.all(file,config) | exports whole database as graphml to the provided file
| apoc.export.graphml.data(nodes,rels,file,config) | exports given nodes and relationships as graphml to the provided file
| apoc.export.graphml.graph(graph,file,config) | exports given graph object as graphml to the provided file
| apoc.export.graphml.query(query,file,config) | exports nodes and relationships from the Cypher statement as graphml to the provided file
|===
// end::export.graphml[]

.configuration options
[options=header]
|===
| param | default | description
| batchSize | 20000 | define the batch size
// | silent | false | if enabled write progress output
| delim | "," | define the delimiter character (export csv)
| quotes | | quote-character used for CSV
| useTypes | false | add type on file header (export csv and graphml export)
| format | "neo4j-shell" | In export to Cypher script define the export format. Possible values are: "cypher-shell","neo4j-shell" and "plain"
| nodesOfRelationships | false | if enabled add relationship between nodes (export Cypher)
| storeNodeIds| false | set nodes' ids (import/export graphml)
| readLabels | false | read nodes' labels (import/export graphml)
| defaultRelationshipType | "RELATED" | set relationship type (import/export graphml)
| separateFiles | false | export results in separated file by type (nodes, relationships..)
| cypherFormat | create | In export to cypher script, define the cypher format (for example use `MERGE` instead of `CREATE`). Possible values are: "create", "updateAll", "addStructure", "updateStructure".
|===

== Loading Data from RDBMS

image::{img}/apoc-jdbc-northwind-load.jpg[width=600]

// tag::jdbc[]

[cols="1m,5"]
|===
| CALL apoc.load.jdbc('jdbc:derby:derbyDB','PERSON') YIELD row CREATE (:Person {name:row.name}) | load from relational database, either a full table or a sql statement
| CALL apoc.load.jdbc('jdbc:derby:derbyDB','SELECT * FROM PERSON WHERE AGE > 18') | load from relational database, either a full table or a sql statement
| CALL apoc.load.driver('org.apache.derby.jdbc.EmbeddedDriver') | register JDBC driver of source database
|===

To simplify the JDBC URL syntax and protect credentials, you can configure aliases in `conf/neo4j.conf`:

----
apoc.jdbc.myDB.url=jdbc:derby:derbyDB
----

----
CALL apoc.load.jdbc('jdbc:derby:derbyDB','PERSON')

becomes

CALL apoc.load.jdbc('myDB','PERSON')
----

The 3rd value in the `apoc.jdbc.<alias>.url=` effectively defines an alias to be used in  `apoc.load.jdbc('<alias>',....`

// end::jdbc[]

== Loading Data from Web-APIs (JSON, XML, CSV)

// tag::xml[]
Supported protocols are `file`, `http`, `https` with redirect allowed. In case no protocol is passed, this procedure set will try to check whether the url is actually a file.
Moreover, if 'apoc.import.file.use_neo4j_config' is enabled the procedures verify whether file system access is allowed and eventually constrained to a specific directory by
reading the two configuration parameters `dbms.security.allow_csv_import_from_file_urls` and `dbms.directories.import` respectively.
[cols="1m,5"]
|===
| CALL apoc.load.json('http://example.com/map.json', [path], [config]) YIELD value as person CREATE (p:Person) SET p = person | load from JSON URL (e.g. web-api) to import JSON as stream of values if the JSON was an array or a single value if it was a map
| CALL apoc.load.xml('http://example.com/test.xml', ['xPath'], [config]) YIELD value as doc CREATE (p:Person) SET p.name = doc.name | load from XML URL (e.g. web-api) to import XML as single nested map with attributes and `_type`, `_text` and `_children` fields.
| CALL apoc.load.xmlSimple('http://example.com/test.xml') YIELD value as doc CREATE (p:Person) SET p.name = doc.name | load from XML URL (e.g. web-api) to import XML as single nested map with attributes and `_type`, `_text` fields and `_<childtype>` collections per child-element-type.
| CALL apoc.load.csv('url',{sep:";"}) YIELD lineNo, list, map | load CSV fom URL as stream of values +
config contains any of: `{skip:1,limit:5,header:false,sep:'TAB',ignore:['tmp'],arraySep:';',mapping:{years:{type:'int',arraySep:'-',array:false,name:'age',ignore:false}}`
|===

.failOnError

Adding on config the parameter `failOnError:false` (by default `true`), in case of error the procedure don't fail but just return zero rows.
// end::xml[]

== Interacting with Elastic Search

// tag::elasticsearch[]

[cols="3m,2"]
|===
| apoc.es.stats(host-url-Key) | elastic search statistics
| apoc.es.get(host-or-port,index-or-null,type-or-null,id-or-null,query-or-null,payload-or-null) yield value | perform a GET operation
| apoc.es.query(host-or-port,index-or-null,type-or-null,query-or-null,payload-or-null) yield value | perform a SEARCH operation
| apoc.es.getRaw(host-or-port,path,payload-or-null) yield value | perform a raw GET operation
| apoc.es.postRaw(host-or-port,path,payload-or-null) yield value | perform a raw POST operation
| apoc.es.post(host-or-port,index-or-null,type-or-null,query-or-null,payload-or-null) yield value | perform a POST operation
| apoc.es.put(host-or-port,index-or-null,type-or-null,query-or-null,payload-or-null) yield value | perform a PUT operation
|===

// end::elasticsearch[]

== Interacting with MongoDB

// tag::mongodb[]

[cols="3m,2"]
|===
| CALL apoc.mongodb.get(host-or-port,db-or-null,collection-or-null,query-or-null,[compatibleValues=true|false]) yield value | perform a find operation on mongodb collection
| CALL apoc.mongodb.count(host-or-port,db-or-null,collection-or-null,query-or-null) yield value | perform a find operation on mongodb collection
| CALL apoc.mongodb.first(host-or-port,db-or-null,collection-or-null,query-or-null,[compatibleValues=true|false]) yield value | perform a first operation on mongodb collection
| CALL apoc.mongodb.find(host-or-port,db-or-null,collection-or-null,query-or-null,projection-or-null,sort-or-null,[compatibleValues=true|false]) yield value | perform a find,project,sort operation on mongodb collection
| CALL apoc.mongodb.insert(host-or-port,db-or-null,collection-or-null,list-of-maps) | inserts the given documents into the mongodb collection
| CALL apoc.mongodb.delete(host-or-port,db-or-null,collection-or-null,list-of-maps) | inserts the given documents into the mongodb collection
| CALL apoc.mongodb.update(host-or-port,db-or-null,collection-or-null,list-of-maps) | inserts the given documents into the mongodb collection
|===

If your documents have date fields or any other type that can be automatically converted by Neo4j, you need to set *compatibleValues* to true. These values will be converted according to Jackson databind default mapping.

Copy these jars into the plugins directory:

* bson-3.4.2.jar
* mongo-java-driver-3.4.2.jar
* mongodb-driver-3.4.2.jar
* mongodb-driver-core-3.4.2.jar

You should be able to get them from https://mongodb.github.io/mongo-java-driver/[here], and https://mvnrepository.com/artifact/org.mongodb/bson/3.4.2[here (BSON)] (via Download)

Or you get them locally from your gradle build of apoc.

----
gradle copyRuntimeLibs
cp lib/mongodb*.jar lib/bson*.jar $NEO4J_HOME/plugins/
----

[source,cypher]
----
CALL apoc.mongodb.first('mongodb://localhost:27017','test','test',{name:'testDocument'})
----

If you need automatic conversion of *unpackable* values then the cypher query will be:

[source,cypher]
----
CALL apoc.mongodb.first('mongodb://localhost:27017','test','test',{name:'testDocument'},true)
----

// end::mongodb[]

== Interacting with Couchbase

// tag::couchbase[]

[cols="3m,2"]
|===
| CALL apoc.couchbase.get(nodes, bucket, documentId) yield id, expiry, cas, mutationToken, content | Retrieves a couchbase json document by its unique ID
| CALL apoc.couchbase.exists(nodes, bucket, documentId) yield value | Check whether a couchbase json document with the given ID does exist
| CALL apoc.couchbase.insert(nodes, bucket, documentId, jsonDocument) yield id, expiry, cas, mutationToken, content | Insert a couchbase json document with its unique ID
| CALL apoc.couchbase.upsert(nodes, bucket, documentId, jsonDocument) yield id, expiry, cas, mutationToken, content | Insert or overwrite a couchbase json document with its unique ID
| CALL apoc.couchbase.append(nodes, bucket, documentId, jsonDocument) yield id, expiry, cas, mutationToken, content | Append a couchbase json document to an existing one
| CALL apoc.couchbase.prepend(nodes, bucket, documentId, jsonDocument) yield id, expiry, cas, mutationToken, content | Prepend a couchbase json document to an existing one
| CALL apoc.couchbase.remove(nodes, bucket, documentId) yield id, expiry, cas, mutationToken, content | Remove the couchbase json document identified by its unique ID
| CALL apoc.couchbase.replace(nodes, bucket, documentId, jsonDocument) yield id, expiry, cas, mutationToken, content | Replace the content of the couchbase json document identified by its unique ID.
| CALL apoc.couchbase.query(nodes, bucket, statement) yield queryResult | Executes a plain un-parameterized N1QL statement.
| CALL apoc.couchbase.posParamsQuery(nodes, bucket, statement, params) yield queryResult | Executes a N1QL statement with positional parameters.
| CALL apoc.couchbase.namedParamsQuery(nodes, bucket, statement, paramNames, paramValues) yield queryResult | Executes a N1QL statement with named parameters.
|===

Copy these jars into the plugins directory:

----
mvn dependency:copy-dependencies
cp target/dependency/java-client-2.3.1.jar target/dependency/core-io-1.3.1.jar target/dependency/rxjava-1.1.5.jar $NEO4J_HOME/plugins/
----

[source,cypher]
----
CALL apoc.couchbase.get(['localhost'], 'default', 'artist:vincent_van_gogh')
----
// end::couchbase[]

== Streaming Data to Gephi

// tag::gephi[]

[cols="1m,5"]
|===
| apoc.gephi.add(url-or-key, workspace, data, weightproperty, ['exportproperty']) | streams provided data to Gephi
|===

// end::gephi[]

== Creating Data

[cols="1m,5"]
|===
| CALL apoc.create.node(['Label'], {key:value,...}) | create node with dynamic labels
| CALL apoc.create.nodes(['Label'], [{key:value,...}]) | create multiple nodes with dynamic labels
| CALL apoc.create.addLabels( [node,id,ids,nodes], ['Label',...]) | adds the given labels to the node or nodes
| CALL apoc.create.removeLabels( [node,id,ids,nodes], ['Label',...]) | removes the given labels from the node or nodes
| CALL apoc.create.setProperty( [node,id,ids,nodes], key, value) | sets the given property on the node(s)
| CALL apoc.create.setProperties( [node,id,ids,nodes], [keys], [values]) | sets the given property on the nodes(s)
| CALL apoc.create.setRelProperty( [rel,id,ids,rels], key, value) | sets the given property on the relationship(s)
| CALL apoc.create.setRelProperties( [rel,id,ids,rels], [keys], [values]) | sets the given property on the relationship(s)
| CALL apoc.create.relationship(person1,'KNOWS',{key:value,...}, person2) | create relationship with dynamic rel-type
| CALL apoc.create.uuids(count) YIELD uuid, row | creates count UUIDs
| CALL apoc.nodes.link([nodes],'REL_TYPE') | creates a linked list of nodes from first to last
| CALL apoc.nodes.isDense(node/[nodes]/id/[ids]) yield node, dense | returns each node and a 'dense' flag if it is a dense node
| CALL apoc.node.relationship.exists(node, rel-direction-pattern) | yields true effectively when the node has the relationships of the pattern
|===

[cols="1m,5"]
|===
| apoc.create.uuid() | returns an UUID
|===

== Virtual Nodes/Rels

Virtual Nodes and Relationships don't exist in the graph, they are only returned to the UI/user for representing a graph projection.
They can be visualized or processed otherwise.
Please note that they have negative id's.

[cols="1m,5"]
|===
| CALL apoc.create.vNode(['Label'], {key:value,...}) YIELD node | returns a virtual node
| apoc.create.vNode(['Label'], {key:value,...}) | returns a virtual node
| CALL apoc.create.vNodes(['Label'], [{key:value,...}]) | returns virtual nodes
| CALL apoc.create.vRelationship(nodeFrom,'KNOWS',{key:value,...}, nodeTo) YIELD rel | returns a virtual relationship
| apoc.create.vRelationship(nodeFrom,'KNOWS',{key:value,...}, nodeTo) | returns a virtual relationship
| CALL apoc.create.vPattern({_labels:['LabelA'],key:value},'KNOWS',{key:value,...}, {_labels:['LabelB'],key:value}) | returns a virtual pattern
| CALL apoc.create.vPatternFull(['LabelA'],{key:value},'KNOWS',{key:value,...},['LabelB'],{key:value}) | returns a virtual pattern
| CALL apoc.nodes.group([labels],[properties],[{node-aggregation},{rel-aggregation]) yield nodes, relationships | Group all nodes and their relationships by given keys, create virtual nodes and relationships for the summary information, you can provide an aggregations map for nodes and rels [{kids:'sum',age:['min','max','avg'],gender:'collect'},{`*`,'count'}]
|===

// * TODO `CALL apoc.create.vGraph([nodes, {_labels:[],... prop:value,...}], [rels,{_from:keyValueFrom,_to:{_label:,_key:,_value:value}, _type:'KNOWS', prop:value,...}],['pk1','Label2:pk2'])

== Virtual Graph

Create a graph object (map) from information that's passed in.
It's basic structure is: `{name:"Name",properties:{properties},nodes:[nodes],relationships:[relationships]}`

[cols="1m,5"]
|===
| apoc.graph.from(data,'name',{properties}) yield graph | creates a virtual graph object for later processing it tries its best to extract the graph information from the data you pass in
| apoc.graph.fromData([nodes],[relationships],'name',{properties}) | creates a virtual graph object for later processing
| apoc.graph.fromPaths(path,'name',{properties}) | creates a virtual graph object for later processing
| apoc.graph.fromPaths([paths],'name',{properties}) | creates a virtual graph object for later processing
| apoc.graph.fromDB('name',{properties}) | creates a virtual graph object for later processing
| apoc.graph.fromCypher('statement',{params},'name',{properties}) | creates a virtual graph object for later processing
|===

== Generating Graphs

Generate undirected (random direction) graphs with semi-real random distributions based on theoretical models.

[cols="1m,5"]
|===
| apoc.generate.er(noNodes, noEdges, 'label', 'type') | generates a graph according to Erdos-Renyi model (uniform)
| apoc.generate.ws(noNodes, degree, beta, 'label', 'type') | generates a graph according to Watts-Strogatz model (clusters)
| apoc.generate.ba(noNodes, edgesPerNode, 'label', 'type') | generates a graph according to Barabasi-Albert model (preferential attachment)
| apoc.generate.complete(noNodes, 'label', 'type') | generates a complete graph (all nodes connected to all other nodes)
| apoc.generate.simple([degrees], 'label', 'type') | generates a graph with the given degree distribution
|===

Example

[source,cypher]
----
CALL apoc.generate.ba(1000, 2, 'TestLabel', 'TEST_REL_TYPE')
CALL apoc.generate.ws(1000, null, null, null)
CALL apoc.generate.simple([2,2,2,2], null, null)
----

== Warmup

(thanks @SaschaPeukert)

[cols="1m,5"]
|===
| CALL apoc.warmup.run() | Warmup the node and relationship page-caches by loading one page at a time
|===

== Monitoring

(thanks @ikwattro)

[cols="1m,5"]
|===
| apoc.monitor.ids | node and relationships-ids in total and in use
| apoc.monitor.kernel | store information such as kernel version, start time, read-only, database-name, store-log-version etc.
| apoc.monitor.store | store size information for the different types of stores
| apoc.monitor.tx | number of transactions total,opened,committed,concurrent,rolled-back,last-tx-id
| apoc.monitor.locks(minWaitTime long) | db locking information such as avertedDeadLocks, lockCount, contendedLockCount and contendedLocks etc. (enterprise)
|===

// include::{img}/apoc.monitor.png[width=600]

// tag::cypher[]

== Cypher Execution

[cols="1m,5"]
|===
| CALL apoc.cypher.run(fragment, params) yield value | executes reading fragment with the given parameters
| apoc.cypher.runFirstColumn(statement, params, [expectMultiplevalues]) | function that executes statement with given parameters returning first column only, if expectMultipleValues is true will collect results into a list
| CALL apoc.cypher.runFile(file or url) yield row, result | runs each statement in the file, all semicolon separated - currently no schema operations
| CALL apoc.cypher.runFiles([files or urls]) yield row, result | runs each statement in the files, all semicolon separated
| CALL apoc.cypher.runSchemaFile(file or url) - allows only schema operations, runs each schema statement in the file, all semicolon separated
| CALL apoc.cypher.runSchemaFiles([files or urls]) - allows only schema operations, runs each schema statement in the files, all semicolon separated
| CALL apoc.cypher.runMany('cypher;\nstatements;',{params}) | runs each semicolon separated statement and returns summary - currently no schema operations
| CALL apoc.cypher.mapParallel(fragment, params, list-to-parallelize) yield value | executes fragment in parallel batches with the list segments being assigned to _
| CALL apoc.cypher.doIt(fragment, params) yield value | executes writing fragment with the given parameters
| CALL apoc.cypher.runTimeboxed('cypherStatement',{params}, timeout) | abort statement after timeout millis if not finished
|===

== Conditional Cypher Execution

[cols="1m,5"]
|===
| CALL apoc.when(condition, ifQuery, elseQuery:'', params:{}) yield value | based on the conditional, executes read-only ifQuery or elseQuery with the given parameters
| CALL apoc.do.when(condition, ifQuery, elseQuery:'', params:{}) yield value | based on the conditional, executes writing ifQuery or elseQuery with the given parameters
| CALL apoc.case([condition, query, condition, query, ...], elseQuery:'', params:{}) yield value | given a list of conditional / read-only query pairs, executes the query associated with the first conditional evaluating to true (or the else query if none are true) with the given parameters
| CALL apoc.do.case([condition, query, condition, query, ...], elseQuery:'', params:{}) yield value | given a list of conditional / writing query pairs, executes the query associated with the first conditional evaluating to true (or the else query if none are true) with the given parameters
|===

// end::cypher[]
// TODO runFile: begin/commit/schema await/constraints/indexes

// tag::trigger[]


////
== Triggers

include::trigger.adoc[leveloffset=2]
////

== Triggers

Enable `apoc.trigger.enabled=true` in `$NEO4J_HOME/config/neo4j.conf` first.

[cols="1m,5"]
|===
| CALL apoc.trigger.add(name, statement, selector) yield name, statement, installed | add a trigger statement under a name, in the statement you can use {createdNodes}, {deletedNodes} etc., the selector is {phase:'before/after/rollback'} returns previous and new trigger information
| CALL apoc.trigger.remove(name) yield name, statement, installed | remove previously added trigger, returns trigger information
| CALL apoc.trigger.list() yield name, statement, installed | update and list all installed triggers
| CALL apoc.trigger.pause(name) | it pauses the trigger
| CALL apoc.trigger.resume(name) | it resumes the paused trigger
|===

Helper Functions

[cols="1m,5"]
|===
| apoc.trigger.nodesByLabel({assignedLabels},'Label') | function to filter labelEntries by label, to be used within a trigger statement with {assignedLabels} and {removedLabels} {phase:'before/after/rollback'} returns previous and new trigger information
| apoc.trigger.propertiesByKey({assignedNodeProperties},'key') | function to filter propertyEntries by property-key, to be used within a trigger statement with {assignedNode/RelationshipProperties} and {removedNode/RelationshipProperties}. Returns [{old,new,key,node,relationship}]
|===

== Job Management

// tag::periodic[]

[cols="1m,5"]
|===
| CALL apoc.periodic.commit(statement, params) | repeats an batch update statement until it returns 0, this procedure is blocking
| CALL apoc.periodic.list() | list all jobs
| CALL apoc.periodic.submit('name',statement) | submit a one-off background statement
| CALL apoc.periodic.schedule('name',statement,repeat-time-in-seconds) | submit a repeatedly-called background statement
| CALL apoc.periodic.countdown('name',statement,delay-in-seconds) | submit a repeatedly-called background statement until it returns 0
| CALL apoc.periodic.rock_n_roll(statementIteration, statementAction, batchSize) YIELD batches, total | iterate over first statement and apply action statement with given transaction batch size. Returns to numeric values holding the number of batches and the number of total processed rows. E.g.
| CALL apoc.periodic.iterate('statement returning items', 'statement per item', {batchSize:1000,parallel:true,retries:3,iterateList:true}) YIELD batches, total | run the second statement for each item returned by the first statement. Returns number of batches and total processed rows
|===

* there are also static methods `Jobs.submit`, and `Jobs.schedule` to be used from other procedures
* jobs list is checked / cleared every 10s for finished jobs


.copies over the `name` property of each person to `lastname`
[source,cypher]
----
CALL apoc.periodic.rock_n_roll('match (p:Person) return id(p) as id_p', 'MATCH (p) where id(p)={id_p} SET p.lastname =p.name', 20000)
----

// end::periodic[]

== Graph Refactoring

[cols="1m,5"]
|===
| call apoc.refactor.cloneNodes([node1,node2,...]) |  clone nodes with their labels and properties
| call apoc.refactor.cloneNodesWithRelationships([node1,node2,...]) | clone nodes with their labels, properties and relationships
| call apoc.refactor.mergeNodes([node1,node2]) | merge nodes onto first in list
| call apoc.refactor.mergeRelationships([rel1,rel2,...],{config}) | merge relationships onto first in list
| call apoc.refactor.to(rel, endNode) | redirect relationship to use new end-node
| call apoc.refactor.from(rel, startNode) | redirect relationship to use new start-node
| call apoc.refactor.invert(rel) | inverts relationship direction
| call apoc.refactor.setType(rel, 'NEW-TYPE') | change relationship-type
| call apoc.refactor.extractNode([rel1,rel2,...], [labels], 'OUT','IN') | extract node from relationships
| call apoc.refactor.collapseNode([node1,node2],'TYPE') | collapse node to relationship, node with one rel becomes self-relationship
| call apoc.refactor.normalizeAsBoolean(entity, propertyKey, true_values, false_values) | normalize/convert a property to be boolean
| call apoc.refactor.categorize(node, propertyKey, type, outgoing, label) | turn each unique propertyKey into a category node and connect to it
|===

On mergeRelationship with config properties you can choose from 3 different management:
 * "overwrite" : if there is the same property in more relationship, in the new one will have the last relationship's property value
 * "discard" : if there is the same property in more relationship, the new one will have the first relationship's property value
 * "combine" : if there is the same property in more relationship, the new one a value's array with all relationships' values

TODO:

* merge nodes by label + property


== Spatial

[cols="1m,5"]
|===
| CALL apoc.spatial.geocode('address') YIELD location, latitude, longitude, description, osmData | look up geographic location of location from openstreetmap geocoding service
| CALL apoc.spatial.sortPathsByDistance(Collection<Path>) YIELD path, distance | sort a given collection of paths by geographic distance based on lat/long properties on the path nodes
|===

== Helpers

=== Static Value Storage

[cols="1m,5"]
|===
| apoc.static.get(name) | returns statically stored value from config (apoc.static.<key>) or server lifetime storage
| apoc.static.getAll(prefix) |  returns statically stored values from config (apoc.static.<prefix>) or server lifetime storage
| apoc.static.set(name, value) | stores value under key for server livetime storage, returns previously stored or configured value
|===

=== Conversion Functions

Sometimes type information gets lost, these functions help you to coerce an "Any" value to the concrete type

[cols="1m,5"]
|===
| apoc.convert.toString(value) | tries it's best to convert the value to a string
| apoc.convert.toMap(value) | tries it's best to convert the value to a map
| apoc.convert.toList(value) | tries it's best to convert the value to a list
| apoc.convert.toBoolean(value) | tries it's best to convert the value to a boolean
| apoc.convert.toNode(value) | tries it's best to convert the value to a node
| apoc.convert.toRelationship(value) | tries it's best to convert the value to a relationship
| apoc.convert.toSet(value) | tries it's best to convert the value to a set
| apoc.convert.toFloat(value) | tries it's best to convert the value to a floating point value
| apoc.convert.toInteger(value) | tries it's best to convert the value to a integer value
|===

=== Map Functions

[cols="1m,5"]
|===
| apoc.map.fromNodes(label, property) | creates map from nodes with this label grouped by property
| apoc.map.fromPairs([[key,value],[key2,value2],...]) | creates map from list with key-value pairs
| apoc.map.fromLists([keys],[values]) | creates map from a keys and a values list
| apoc.map.fromValues([key,value,key1,value1]) | creates map from alternating keys and values in a list
| apoc.map.merge({first},{second}) yield value | creates map from merging the two source maps
| apoc.map.mergeList([{maps}]) yield value | merges all maps in the list into one
| apoc.map.setKey(map,key,value) | returns the map with the value for this key added or replaced
| apoc.map.removeKey(map,key) | returns the map with the key removed
| apoc.map.removeKeys(map,[keys]) | returns the map with the keys removed
| apoc.map.clean(map,[keys],[values]) yield value | removes the keys and values (e.g. null-placeholders) contained in those lists, good for data cleaning from CSV/JSON
| apoc.map.groupBy([maps/nodes/relationships],'key') yield value | creates a map of the list keyed by the given property, with single values
| apoc.map.groupByMulti([maps/nodes/relationships],'key') yield value | creates a map of the list keyed by the given property, with list values
| apoc.map.sortedProperties(map, ignoreCase:true) | returns a list of key/value list pairs, with pairs sorted by keys alphabetically, with optional case sensitivity
| apoc.map.updateTree(tree,key,[[value,{data}]]) | returns map - adds the {data} map on each level of the nested tree, where the key-value pairs match
|===


=== Collection Functions

[cols="1m,5"]
|===
| apoc.coll.sum([0.5,1,2.3]) | sum of all values in a list
| apoc.coll.avg([0.5,1,2.3]) | avg of all values in a list
| apoc.coll.min([0.5,1,2.3]) | minimum of all values in a list
| apoc.coll.max([0.5,1,2.3]) | maximum of all values in a list
| apoc.coll.sumLongs([1,3,3]) | sums all numeric values in a list
| apoc.coll.partition(list,batchSize) | partitions a list into sublists of `batchSize`
| apoc.coll.zip([list1],[list2]) | all values in a list
| apoc.coll.pairs([1,2,3]) YIELD value | [1,2],[2,3],[3,null]
| apoc.coll.pairsMin([1,2,3]) YIELD value | [1,2],[2,3]
| apoc.coll.toSet([list]) | returns a unique list backed by a set
| apoc.coll.sort(coll) | sort on Collections
| apoc.coll.sortNodes([nodes], 'name') | sort nodes by property
| apoc.coll.sortMaps([maps], 'key') | sort maps by map key
| apoc.coll.reverse(coll) | returns the reversed list
| apoc.coll.contains(coll, value) | optimized contains operation (using a HashSet) (returns single row or not)
| apoc.coll.containsAll(coll, values) | optimized contains-all operation (using a HashSet) (returns single row or not)
| apoc.coll.containsSorted(coll, value) | optimized contains on a sorted list operation (Collections.binarySearch) (returns single row or not)
| apoc.coll.containsAllSorted(coll, value) | optimized contains-all on a sorted list operation (Collections.binarySearch) (returns single row or not)
| apoc.coll.union(first, second) | creates the distinct union of the 2 lists
| apoc.coll.subtract(first, second) | returns unique set of first list with all elements of second list removed
| apoc.coll.removeAll(first, second) | returns first list with all elements of second list removed
| apoc.coll.intersection(first, second) | returns the unique intersection of the two lists
| apoc.coll.disjunction(first, second) | returns the disjunct set of the two lists
| apoc.coll.unionAll(first, second) | creates the full union with duplicates of the two lists
| apoc.coll.split(list,value) | splits collection on given values rows of lists, value itself will not be part of resulting lists
| apoc.coll.indexOf(coll, value) | position of value in the list
| apoc.coll.shuffle(coll) | returns the shuffled list
| apoc.coll.randomItem(coll) | returns a random item from the list
| apoc.coll.randomItems(coll, itemCount, allowRepick: false) | returns a list of `itemCount` random items from the list, optionally allowing picked elements to be picked again
| apoc.coll.containsDuplicates(coll) | returns true if a collection contains duplicate elements
| apoc.coll.duplicates(coll) | returns a list of duplicate items in the collection
| apoc.coll.duplicatesWithCount(coll) | returns a list of duplicate items in the collection and their count, keyed by `item` and `count` (e.g., `[{item: xyz, count:2}, {item:zyx, count:5}]`)
| apoc.coll.occurrences(coll, item) | returns the count of the given item in the collection
| apoc.coll.sortMulti | sort list of maps by several sort fields (ascending with ^ prefix) and optionally applies limit and skip
| apoc.coll.flatten | flattens a nested list
|===

=== Lookup Functions

[cols="1m,5"]
|===
| CALL apoc.get.nodes(node|id|[ids]) yield node | quickly returns all nodes with these id's
| CALL apoc.get.rels(rels|id|[ids]) yield rel | quickly returns all relationships with these id's
|===

=== Math Functions

[cols="1m,5"]
|===
| apoc.math.round(value,[precision=0],mode=[HALF_UP,CEILING,FLOOR,UP,DOWN,HALF_EVEN,HALF_DOWN,DOWN,UNNECESSARY]) | rounds value with optionally given precision (default 0) and optional rounding mode (default HALF_UP)
| apoc.math.maxLong() | return the maximum value a long can have
| apoc.math.minLong() | return the minimum value a long can have
| apoc.math.maxDouble() | return the largest positive finite value of type double
| apoc.math.minDouble() | return the smallest positive nonzero value of type double
| apoc.math.maxInt() | return the maximum value a int can have
| apoc.math.minInt() | return the minimum value a int can have
| apoc.math.maxByte() | return the maximum value a byte can have
| apoc.math.minByte() | return the minimum value a byte can have
|===

=== Text Functions

[cols="1m,5"]
|===
| apoc.text.replace(text, regex, replacement)| replace each substring of the given string that matches the given regular expression with the given replacement.
| apoc.text.regexGroups(text, regex) | returns an array containing a nested array for each match. The inner array contains all match groups.
| apoc.text.join(['text1','text2',...], delimiter) | join the given strings with the given delimiter.
| apoc.text.format(text,[params]) | sprintf format the string with the params given
| apoc.text.lpad(text,count,delim) | left pad the string to the given width
| apoc.text.rpad(text,count,delim) | right pad the string to the given width
| apoc.data.domain(email_or_url) | returns domain part of the value
| apoc.text.random(length, [valid]) | returns a random string to the specified length
| apoc.text.capitalize(text) | capitalise the first letter of the word
| apoc.text.capitalizeAll(text) | capitalise the first letter of every word in the text
| apoc.text.decapitalize(text) | decapitalize the first letter of the word
| apoc.text.apoc.text.decapitalizeAll(text) | decapitalize the first letter of all words
| apoc.text.swapCase(text) | Swap the case of a string
| apoc.text.camelCase(text) | Convert a string to camelCase
| apoc.text.upperCamelCase(text) | Convert a string to UpperCamelCase
| apoc.text.snakeCase(text) | Convert a string to snake-case
| apoc.text.toUpperCase(text) | Convert a string to UPPER_CASE
|===

=== Phonetic Comparison Functions

[cols="1m,5"]
|===
| apoc.text.phonetic(value) | Compute the US_ENGLISH phonetic soundex encoding of all words of the text value which can be a single string or a list of strings
| apoc.text.clean(text) | strip the given string of everything except alpha numeric characters and convert it to lower case.
| apoc.text.compareCleaned(text1, text2) | compare the given strings stripped of everything except alpha numeric characters converted to lower case.
|===

.Procedure
[cols="1m,5"]
|===
| apoc.text.phoneticDelta(text1, text2) yield phonetic1, phonetic2, delta | Compute the US_ENGLISH soundex character difference between two given strings
|===



== Utilities

[cols="1m,5"]
|===
| apoc.util.sha1([values]) | computes the sha1 of the concatenation of all string values of the list
| apoc.util.md5([values]) | computes the md5 of the concatenation of all string values of the list
| apoc.util.sleep({duration}) | sleeps for <duration> millis, transaction termination is honored
| apoc.util.validate(predicate, message,[params]) | raises exception if prediate evaluates to true
|===


== Config

[cols="1m,5"]
|===
| apoc.config.list | Lists the Neo4j configuration as key,value table
| apoc.config.map | Lists the Neo4j configuration as map
|===

== Time to Live (TTL)

Enable TTL with setting in `neo4j.conf` : `apoc.ttl.enabled=true`

There are some convenience procedures to expire nodes.

You can also do it yourself by running

[source,cypher]
----
SET n:TTL
SET n.ttl = timestamp() + 3600
----

[cols="1m,5"]
|===
| CALL apoc.date.expire.in(node,time,'time-unit') | expire node in given time-delta by setting :TTL label and `ttl` property
| CALL apoc.date.expire(node,time,'time-unit') | expire node at given time by setting :TTL label and `ttl` property
|===

Optionally set `apoc.ttl.schedule=5` as repeat frequency.

== Date/time Support

(thanks @tkroman)

=== Conversion Functions between formatted dates and timestamps

[cols="1m,5"]
|===
| apoc.date.parse('2015/03/25 03:15:59',['ms'/'s'], ['yyyy/MM/dd HH:mm:ss']) | same as previous, but accepts custom datetime format
| apoc.date.format(12345, ['ms'/'s'], ['yyyy/MM/dd HH:mm:ss']) | the same as previous, but accepts custom datetime format
| apoc.date.systemTimezone() | return the system timezone display format string
|===

* possible unit values: `ms,s,m,h,d` and their long forms `millis,milliseconds,seconds,minutes,hours,days`.
* possible time zone values: Either an abbreviation such as `PST`, a full name such as `America/Los_Angeles`, or a custom ID such as `GMT-8:00`. Full names are recommended. You can view a list of full names in https://en.wikipedia.org/wiki/List_of_tz_database_time_zones[this Wikipedia page].

=== Conversion of timestamps between different time units

* `apoc.date.convert(12345, 'ms', 'd')` convert a timestamp in one time unit into one of a different time unit

* possible unit values: `ms,s,m,h,d` and their long forms.

=== Adding/subtracting time unit values to timestamps

* `apoc.date.add(12345, 'ms', -365, 'd')` given a timestamp in one time unit, adds a value of the specified time unit

* possible unit values: `ms,s,m,h,d` and their long forms.

=== Reading separate datetime fields

Splits date (optionally, using given custom format) into fields returning a map from field name to its value.

* `apoc.date.fields('2015-03-25 03:15:59')`

== Current timestamp

`apoc.date.currentTimestamp()` provides the System.currentTimeMillis which is current throughout transaction execution compared to Cypher's timestamp() function which does not update within a transaction

== Bitwise operations

// TODO function

Provides a wrapper around the java bitwise operations.
|===
| apoc.bitwise.op(a long, "operation", b long ) as <identifier>
|===

examples
|===
| operator | name | example | result
| a & b | AND | apoc.bitwise.op(60,"&",13) | 12
| a \| b | OR | apoc.bitwise.op(60,"\|",13) | 61
| a ^ b | XOR | apoc.bitwise.op(60,"&",13) | 49
| ~a | NOT | apoc.bitwise.op(60,"&",0) | -61
| a << b | LEFT SHIFT | apoc.bitwise.op(60,"<<",2) | 240
| a >> b | RIGHT SHIFT | apoc.bitwise.op(60,">>",2) | 15
| a >>> b | UNSIGNED RIGHT SHIFT | apoc.bitwise.op(60,">>>",2) | 15
|===

== Path Expander

(thanks @keesvegter)

The apoc.path.expand procedure makes it possible to do variable length path traversals where you can specify the direction of the relationship per relationship type and a list of Label names which act as a "whitelist" or a "blacklist" or define end nodes for the expansion. The procedure will return a list of Paths in a variable name called "path".

[cols="1m,5"]
|===
| call apoc.path.expand(startNode <id>\|Node, relationshipFilter, labelFilter, minDepth, maxDepth ) yield path as <identifier> | expand from given nodes(s) taking the provided restrictions into account
|===

Variations allow more configurable expansions, and expansions for more specific use cases:

[cols="1m,5"]
|===
| call apoc.path.expandConfig(startNode <id>Node/list, {minLevel, maxLevel, relationshipFilter, labelFilter, bfs:true, uniqueness:'RELATIONSHIP_PATH', filterStartNode:true, limit, optional:false}) yield path | expand from given nodes(s) taking the provided restrictions into account
| call apoc.path.subgraphNodes(startNode <id>Node/list, {maxLevel, relationshipFilter, labelFilter, bfs:true, filterStartNode:true, limit, optional:false}) yield node | expand a subgraph from given nodes(s) taking the provided restrictions into account; returns all nodes in the subgraph
| call apoc.path.subgraphAll(startNode <id>Node/list, {maxLevel, relationshipFilter, labelFilter, bfs:true, filterStartNode:true, limit}) yield nodes, relationships | expand a subgraph from given nodes(s) taking the provided restrictions into account; returns the collection of subgraph nodes, and the collection of all relationships within the subgraph
| call apoc.path.spanningTree(startNode <id>Node/list, {maxLevel, relationshipFilter, labelFilter, bfs:true, filterStartNode:true, limit, optional:false}) yield path | expand a spanning tree from given nodes(s) taking the provided restrictions into account; the paths returned collectively form a spanning tree
|===

=== Relationship Filter

Syntax: `[<]RELATIONSHIP_TYPE1[>]|[<]RELATIONSHIP_TYPE2[>]|...`

[opts=header,cols="m,m,a"]
|===
| input | type | direction
| LIKES> | LIKES | OUTGOING
| <FOLLOWS | FOLLOWS  | INCOMING
| KNOWS  | KNOWS | BOTH
|===

=== Label Filter

Syntax: `[+-/>]LABEL1|LABEL2|...`

As of APOC 3.1.3.x multiple label filter operations are allowed.

In prior versions, only one type of operation is allowed in the label filter (`+` or `-` or `/` or `>`, never more than one).

With APOC 3.2.x.x, label filters will no longer apply to starting nodes of the expansion by default.

[opts=header,cols="m,m,a"]
|===
| input | label | result
| -Foe | Foe | blacklist filter - No node in the path will have a label in the blacklist.
| +Friend | Friend | whitelist filter - All nodes in the path must have a label in the whitelist (exempting termination and end nodes, if using those filters).

If no whitelist operator is present, all labels are considered whitelisted.
| /Friend | Friend | termination filter - Only return paths up to a node of the given labels, and stop further expansion beyond it.

Termination nodes do not have to respect the whitelist. Termination filtering takes precedence over end node filtering.
| >Friend | Friend | end node filter - Only return paths up to a node of the given labels, but continue expansion to match on end nodes beyond it.

End nodes do not have to respect the whitelist to be returned, but expansion beyond them is only allowed if the node has a label in the whitelist.
|===

=== Uniqueness

Uniqueness of nodes and relationships guides the expansion and the returned results.
Uniqueness is only configurable using `expandConfig()`.

`subgraphNodes()`, `subgraphAll()`, and `spanningTree()` all use 'NODE_GLOBAL' uniqueness.

[opts=header,cols="m,a"]
|===
| value | description
| RELATIONSHIP_PATH | For each returned node there's a (relationship wise) unique path from the start node to it. This is Cypher's default expansion mode.
| NODE_GLOBAL | A node cannot be traversed more than once. This is what the legacy traversal framework does.
| NODE_LEVEL | Entities on the same level are guaranteed to be unique.
| NODE_PATH | For each returned node there's a unique path from the start node to it.
| NODE_RECENT | This is like NODE_GLOBAL, but only guarantees uniqueness among the most recent visited nodes, with a configurable count. Traversing a huge graph is quite memory intensive in that it keeps track of all the nodes it has visited.
For huge graphs a traverser can hog all the memory in the JVM, causing OutOfMemoryError. Together with this Uniqueness you can supply a count, which is the number of most recent visited nodes. This can cause a node to be visited more than once, but scales infinitely.
| RELATIONSHIP_GLOBAL | A relationship cannot be traversed more than once, whereas nodes can.
| RELATIONSHIP_LEVEL | Entities on the same level are guaranteed to be unique.
| RELATIONSHIP_RECENT | Same as for NODE_RECENT, but for relationships.
| NONE | No restriction (the user will have to manage it)
|===



== Parallel Node Search

Utility to find nodes in parallel (if possible). These procedures return a single list of nodes or a list of 'reduced' records with node id, labels, and the properties where the search was executed upon.

[cols="5m,4"]
|===
| call apoc.search.node(labelPropertyMap, searchType, search ) yield node | A distinct set of Nodes will be returned.
| call apoc.search.nodeAll(labelPropertyMap, searchType, search ) yield node | All the found Nodes will be returned.
| call apoc.search.nodeReduced(labelPropertyMap, searchType, search ) yield id, labels, values | A merged set of 'minimal' Node information will be returned. One record per node (-id).
| call apoc.search.nodeAllReduced(labelPropertyMap, searchType, search ) yield id, labels, values | All the found 'minimal' Node information will be returned. One record per label and property.
|===

[cols="1m,4,3"]
|===
| labelPropertyMap |   `'{ label1 : "propertyOne", label2 :["propOne","propTwo"] }'` | (JSON or Map) For every Label-Property combination a search will be executed in parallel (if possible): Label1.propertyOne, label2.propOne and label2.propTwo.
| searchType |  'exact' or 'contains' or 'starts with' or 'ends with' | Case insensitive string search operators
| searchType |  "<", ">", "=", "<>", "<=", ">=", "=~" | Operators
| search | 'Keanu' | The actual search term (string, number, etc).
|===

.example
[source,cypher]
----
CALL apoc.search.nodeAll('{Person: "name",Movie: ["title","tagline"]}','contains','her') YIELD node AS n RETURN n
call apoc.search.nodeReduced({Person: 'born', Movie: ['released']},'>',2000) yield id, labels, properties RETURN *
----

== Graph Algorithms (work in progress)

Provides some graph algorithms (not very optimized yet)

[cols="3m,3"]
|===
| apoc.algo.dijkstra(startNode, endNode, 'KNOWS\|<WORKS_WITH\|IS_MANAGER_OF>', 'distance') YIELD path, weight | run dijkstra with relationship property name as cost function
| apoc.algo.dijkstraWithDefaultWeight(startNode, endNode, 'KNOWS\|<WORKS_WITH\|IS_MANAGER_OF>',  'distance', 10) YIELD path, weight | run dijkstra with relationship property name as cost function and a default weight if the property does not exist
| apoc.algo.aStar(startNode, endNode, 'KNOWS\|<WORKS_WITH\|IS_MANAGER_OF>', 'distance','lat','lon')  YIELD path, weight | run A* with relationship property name as cost function
| apoc.algo.aStar(startNode, endNode, 'KNOWS\|<WORKS_WITH\|IS_MANAGER_OF>', {weight:'dist',default:10, x:'lon',y:'lat'}) YIELD path, weight | run A* with relationship property name as cost function
| apoc.algo.allSimplePaths(startNode, endNode, 'KNOWS\|<WORKS_WITH\|IS_MANAGER_OF>', 5) YIELD path,  weight | run allSimplePaths with relationships given and maxNodes
| apoc.stats.degrees(relTypesDirections) yield type, direction, total, min, max, mean, p50, p75, p90, p95, p99, p999 | compute degree distribution in parallel
|===


[cols="3m,3"]
|===
| apoc.algo.betweenness(['TYPE',...],nodes,BOTH) YIELD node, score | calculate betweenness  centrality for given nodes
| apoc.algo.closeness(['TYPE',...],nodes, INCOMING) YIELD node, score | calculate closeness  centrality for given nodes
| apoc.algo.cover(nodeIds) YIELD rel | return relationships between this set of nodes
|===

[cols="3m,3"]
|===
| apoc.algo.pageRank(nodes) YIELD node, score | calculates page rank for given nodes
| apoc.algo.pageRankWithConfig(nodes,{iterations:_,types:_}) YIELD node, score | calculates page rank for given nodes
|===

[cols="3m,3"]
|===
| apoc.algo.community(times,labels,partitionKey,type,direction,weightKey,batchSize) | simple label propagation kernel
| apoc.algo.cliques(minSize) YIELD clique | search the graph and return all maximal cliques at least at  large as the minimum size argument.
| apoc.algo.cliquesWithNode(startNode, minSize) YIELD clique | search the graph and return all maximal cliques that  are at least as large than the minimum size argument and contain this node
|===

[cols="3m,3"]
|===
| apoc.algo.cosineSimilarity([vector1], [vector2]) | Compute cosine similarity
| apoc.algo.euclideanDistance([vector1], [vector2]) | Compute Euclidean distance
| apoc.algo.euclideanSimilarity([vector1], [vector2]) | Compute Euclidean similarity
|===

Example: find the weighted shortest path based on relationship property `d` from `A` to `B` following just `:ROAD` relationships

[source,cypher]
----
MATCH (from:Loc{name:'A'}), (to:Loc{name:'D'})
CALL apoc.algo.dijkstra(from, to, 'ROAD', 'd') yield path as path, weight as weight
RETURN path, weight
----

// end::overview[]
