Skip to content

Liquigraph 2.x (Archive)âš“

Caution

Liquigraph 2.x is not maintained anymore. Please upgrade to the latest version.

Crash Courseâš“

Liquigraph executes migrations defined in a single change log. These migrations are actually called change sets.

Each change set must define at least 1 migration query in the Cypher query language.

Change sets are immutable (i.e. not allowed to change) and incremental (i.e. executed only once) by default. Liquigraph persists the executed change sets in the target graph database every time it runs.

See the detailed section to understand the Liquigraph model in more details.

The Migration Fileâš“

Open your favourite editor and define a change log with two change sets as follows:

<?xml version="1.0" encoding="UTF-8"?>
<changelog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:noNamespaceSchemaLocation="https://www.liquigraph.org/schema/1.0/liquigraph.xsd">
    <changeset id="hello-world" author="you">
        <query>CREATE (n:Sentence {text:'Hello monde!'}) RETURN n</query>
    </changeset>
    <changeset id="hello-world-fixed" author="you">
        <query>MATCH (n:Sentence {text:'Hello monde!'}) SET n.text='Hello world!' RETURN n</query>
    </changeset>
</changelog>

These migrations can be run using the Java API, via your Maven project or simply using the command line. Choose the way that fits your project.

Java APIâš“

Save the migration file at ${your_project}/src/main/resources/changelog.xml.

Then, include Liquigraph in your pom.xml:

<dependency>
    <groupId>org.liquigraph</groupId>
    <artifactId>liquigraph-core</artifactId>
    <version>2.0.3</version>
</dependency>

If you want to consume a SNAPSHOT version, you need to add the following to your POM:

<repositories>
  <repository>
      <id>ossrh</id>
      <name>Sonatype Snapshots</name>
      <url>https://oss.sonatype.org/content/repositories/snapshots</url>
      <snapshots>
          <enabled>true</enabled>
      </snapshots>
      <releases>
          <enabled>false</enabled>
      </releases>
  </repository>
</repositories>

Finally, running the migrations can be done as follows:

import org.liquigraph.core.api.Liquigraph;
import org.liquigraph.core.configuration.Configuration;
import org.liquigraph.core.configuration.ConfigurationBuilder;

// [...]

Configuration configuration = new ConfigurationBuilder()
        .withMasterChangelogLocation("changelog.xml")
        .withUri("jdbc:neo4j:http://localhost:7474")
        .withRunMode()
        .build();

Liquigraph liquigraph = new Liquigraph();
liquigraph.runMigrations(configuration);

If you want to simulate (also known as \"dry-run\") your migrations without actually running them, replace withRunMode() by withDryRunMode(Paths.get(outputDirectory)), where outputDirectory specifies the path of the directory where output.cypher will be written in. :::

The Maven wayâš“

Save the migration file at ${your_project}/src/main/resources/changelog.xml (see the above section to learn how to consume SNAPSHOT versions):.

Then, include Liquigraph plugin in your pom.xml:

<plugin>
    <groupId>org.liquigraph</groupId>
    <artifactId>liquigraph-maven-plugin</artifactId>
    <version>2.0.3</version>
    <configuration>
        <changelog>changelog.xml</changelog><!-- classpath location -->
        <jdbcUri>jdbc:neo4j:http://localhost:7474</jdbcUri>
    </configuration>
    <executions>
        <execution>
          <id>run-migrations</id>
          <goals>
            <goal>dry-run</goal>
            <goal>run</goal>
          </goals>
        </execution>
    </executions>
</plugin>

And finally, run:

$> mvn clean package

Highway to shellâš“

Save the migration file at ${your_project}/migrations/changelog.xml.

Tip

Homebrew users, skip the following installation steps altogether. Specific instructions are here.

Prerequisitesâš“

  1. make sure at least JRE 8 is set up and java is included in your path
  2. decompress Liquigraph zip, tar.gz or tar.bz2 in LIQUIGRAPH_DIR
  3. include LIQUIGRAPH_DIR/liquigraph-cli/ to your PATH
  4. Unix users: make sure LIQUIGRAPH_DIR/liquigraph-cli/liquigraph.sh is executable

You can now check your installation by executing the following command (reminder to non-Unix users: replace .sh with .bat):

Running Liquigraphâš“

Interactive modeâš“
$> cd LIQUIGRAPH_DIR/liquigraph-cli
$> ./liquigraph.sh --help

A description should be displayed. Then, simulate the changes (replace /tmp with whatever floats your boat):

$> ./liquigraph.sh --changelog "${your_project}/migrations/changelog.xml" \
    --username neo4j \
    --password \ # leave empty (password prompt will appear)
    --graph-db-uri jdbc:neo4j:http://localhost:7474/ \
    --dry-run-output-directory /tmp
# check contents
$> less /tmp/output.cypher

And finally, run:

$> ./liquigraph.sh --changelog "${your_project}/migrations/changelog.xml" \
    --username neo4j \
    --password \ # leave empty (password prompt will appear)
    --graph-db-uri jdbc:neo4j:http://localhost:7474/
Non-interactive modeâš“

The above methods will prompt for your Neo4j password. If you wish to run liquigraph without password prompt (i.e. non interactively):

$> touch my_password.txt
# update mypassword.txt to contain your password and chmod appropriately
$> ./liquigraph.sh --changelog "${your_project}/migrations/changelog.xml" \
    --username neo4j \
    --graph-db-uri jdbc:neo4j:http://localhost:7474/ \
    --password < my_password.txt

Detailed sectionâš“

Supported JDBC urlsâš“

  • http: The URL pattern is: jdbc:neo4j:http://<host>:<port>/
    For instance: jdbc:neo4j:http://localhost:7474
  • https: The URL pattern is: jdbc:neo4j:https://<host>:<port>/
    For instance: jdbc:neo4j:https://localhost:7663
  • bolt: The URL pattern is: jdbc:neo4j:bolt://<host>:<port>/
    For instance: jdbc:neo4j:bolt://localhost:7687

Definitionsâš“

Change logâš“

A Liquigraph change log defines a set of migrations (change sets) to be performed. There can be only one change log as entry point per project.

<?xml version="1.0" encoding="UTF-8"?>
<changelog xmlns="https://liquigraph.github.io/schema/1.0/liquigraph.xsd">
    <!-- import "sub-change logs"-->
    <import resource="version_1/sub_changelog.xml" />
    <import resource="version_2/sub_changelog.xml" />
    <!-- and/or define directly change sets-->
    <changeset [...] />
    <!-- [...] -->
</changelog>

Both sub_changelog.xml files could import change logs and/or define changeset elements. Their root element is also changelog.

Change setâš“

A Liquigraph change set describes one or more create or update statement. These statements must be written in Cypher Query Language and are wrapped in a single transaction (1 transaction per change set). A change set can be run only once (incremental) and cannot be altered (immutable) against the same graph database instance, by default. Finally, a change set is uniquely identified within the change log by its mandatory ID and author attributes.

<changeset id="unique_identifier" author="team_or_individual_name">
    <!-- 1 to n queries: all executed in 1 transaction -->
    <query>CREATE (m:MyAwesomeNode) RETURN m</query>
    <query>CREATE (m:MyOtherAwesomeNode) RETURN m</query>
</changeset>

Both sub_changelog.xml files could import change logs and/or define changeset elements.

Execution contextâš“

An execution context is a simple string, defined at change set level. A change set can have 0, 1 or more execution contexts (in the latter case, they\'re comma-separated). For instance:

<changeset id="hello-world" author="you" contexts="foo,bar">
   <query>CREATE (n:Sentence {text:'Hello monde!'}) RETURN n</query>
</changeset>

If no execution contexts are specified at runtime, all change sets will match.\ If one or more execution contexts are specified at runtime, change sets will be selected:

  • if they do not declare any execution contexts
  • or one of their declared contexts match one of the runtime contexts
Change set immutabilityâš“

As previously mentioned, Liquigraph change sets are immutable by default (an error will be thrown if they have been altered). That said, there may be situations where change sets should be run whenever their contents have changed. When a change occurs, the change set computed checksum will change and Liquigraph will execute the changeeset queries.

To allow such a scenario, you just need to add an extra attribute to the change set element:

<changeset id="hello-world" author="you" run-on-change="true">
   <query>CREATE (n:Sentence {text:'Hello monde!'}) RETURN n</query>
</changeset>
Change set incrementalityâš“

Liquigraph change sets are incremental by default (they will be executed only once). That said, there may be situations where change sets should be run at every execution. To achieve this, you just need to define one extra attribute:

<changeset id="hello-world" author="you" run-always="true">
   <query>CREATE (n:Sentence {text:'Hello monde!'}) RETURN n</query>
</changeset>
Combining mutability and non-incrementalityâš“

A mutable change set (run-on-change=true) is not going to run unless its content change.\ Similarly, a non-incremental change set (run-always="true") will always run as long as it never changes (else an error would occur, change sets are immutable by default).\ If you need to run a change set all the time and allow its content to change, then you need to combine both attributes.

<changeset id="hello-world" author="you" run-always="true"  run-on-change="true">
   <query>CREATE (n:Sentence {text:'Hello monde!'}) RETURN n</query>
</changeset>
Change set preconditionâš“

Change set preconditions act like guards. They make sure that the change set queries will be executed if and only if the precondition is met.

A precondition can be simple (1 Cypher query) or compound (with boolean AND/OR operators).

In any case, each of the subqueries/the simple query has to return exactly one column named result of type boolean (true|false).

Note that a precondition cannot modify the database itself: all changes will be rolled back.

If the precondition fails, an error policy has to be selected amongst the following choices:

  • CONTINUE: ignores the precondition error and skips the associated change set execution
  • MARK_AS_EXECUTED: ignores the precondition error and marks the change set as executed (without actually executing it)
  • FAIL: halts the whole execution, reporting the precondition error

Here is a basic precondition example (you are right: this is the dumbest precondition ever):

<changeset id="hello-world" author="you">
   <precondition if-not-met="MARK_AS_EXECUTED">
      <query>RETURN true AS result</query>
   </precondition>
   <query>CREATE (n:Sentence {text:'Hello monde!'}) RETURN n</query>
</changeset>

Here comes a slightly more complicated example:

<changeset id="contest-winner-selection" author="futuroscope-engineering">
   <precondition if-not-met="FAIL">
      <or>
         <and>
            <query>MATCH (p:User {twitter:'@fbiville'}) RETURN NOT (p.underAge) AS result</query>
            <query><![CDATA[MATCH (p:User {twitter:'@fbiville'}) OPTIONAL MATCH (p)-[:SUFFERS_FROM]->(d:NEURO_DISORDER {name:'photosensitive epilepsy'}) RETURN (d IS NULL) AS result]]></query>
         </and>
         <query><![CDATA[MATCH (p:User {twitter:'@fbiville'}) OPTIONAL MATCH (p)<-[:HAS_PARENTAL_CONTROL]-(parent:User) RETURN NOT (parent IS NULL) AS RESULT]]></query>
      </or>
   </precondition>
   <query><![CDATA[MATCH (p:User {twitter:'@fbiville'}) CREATE (p)-[:IS_OFFERED_FREE_PASS_TO]->(:Location {name:'Futuroscope'})]]></query>
</changeset>
Change set postconditionâš“

Change set postconditions allow a single change set to be applied as many times as necessary to complete the migration, by repeating the change set queries as long as the postcondition is met.

A postcondition can be simple (1 Cypher query) or compound (with boolean AND/OR operators).

In any case, each of the subqueries/the simple query has to return exactly one column named result of type boolean (true|false).

Note that a postcondition cannot modify the database itself: all changes will be rolled back.

Once the postcondition returns false, the change set is considered complete.

A repeatable change set can be used to perform a migration on a large database without risking an OutOfMemoryError because of the large number of nodes or relationships impacted, by splitting the migration into smaller batches.

Here is a postcondition example, deleting all relationships in the database by batches of 10:

<changeset id="delete-relationships" author="you">
   <query><![CDATA[MATCH ()-[r]->() WITH r LIMIT 10 DELETE r]]></query>
   <postcondition>
      <query><![CDATA[OPTIONAL MATCH ()-[r]->() WITH r LIMIT 1 RETURN (r IS NOT NULL) AS result]]></query>
   </postcondition>
</changeset>