Neo4j-OGM’s Service Provider Interface

Gerrit Meier
Neo4j Developer Blog
4 min readOct 15, 2018

Starting with Neo4j-OGM 3.1.4 we provide a new Service Provider Interface (SPI) to manipulate the Cypher statements generated by using the object graph mapper or manually defined within your application..

The interception of the outgoing queries will happen right before sending them using one of the two transport modes in Neo4j-OGM supporting this feature: embedded and Bolt. The HTTP transport mode is currently not supported. The moment one or more implementations of the CypherModificationProvider are found on the class path while the driver gets created the provider will be loaded.

Use Cases for modifying the outgoing Queries

The first question you might ask is: Why should I modify the generated queries? Good news, you do not have to at all, but there are some situations when it is easier to alter the query instead of patching multiple parts of your application.

You could, for example, do simple replacements for certain parts of the query string or do more sophisticated operations like parsing the Cypher statement and operating on its elements.

Defining Cypher version and runtime

One use case may be using a different Cypher version than your server’s one. You could simply intercept the query with the following example.

public class PrefixProvider implements CypherModificationProvider {

@Override
public Function<String, String> getCypherModification
(Map<String, Object> config) {
return (cypherQuery) ->
"CYPHER 3.3 runtime=interpreted " + cypherQuery;
}
}

This class also has to be registered by creating a SPI file in META-INF/services named org.neo4j.ogm.spi.CypherModificationProvider containing only the fully qualified class name of your provider.
Putting this e.g. as an additional library to your project will then manipulate any statement that will get sent to the server through Neo4j-OGM.

The next, more complex, example we want to present you in this post is adding a label to every node in a query.

Label Modification

Just let us start with a short theoretical part on this topic and explain the use case behind this. When persisting and loading data from Neo4j the Neo4j-OGM will attach labels matching the class name and inherited or provided label(s). Now imagine that you have already created some platform / application for a single user base but you want to provide it to other users also. Of course the newly users should not see the data of others.

Data visible to everyone

One typical solution is that you also introduce some kind of owner node in the graph and change every query by adding the additional owner that is linked to all the nodes she owns. This can end up in a lot of work and suddenly you are mixing technical demands with your pre-existing business logic.

To separate this concerns you can provide your very own modification provider, as shown in the code below. Please note that the code snippets are shortened for this post to give you an idea.

public class LabelMod implements CypherModificationProvider {

public final String CONFIG_KEY = "cypher.modification.label";
@Override
public Function<String, String> getCypherModification(
Map<String, Object> configProperties) {
Object label = configProperties.get(CONFIG_KEY); return LabelProvider.for(label)::addLabel;
}

One thing you might notice in this example is that it makes use of the also newly available option to provide arbitrary configuration parameters in Neo4j-OGM.This is intentionally a pure programmatic option because you should also be possible to provide any type of value if needed. This label provider works with String or Supplier types, for example.

The implementation of the LabelProvider class uses the openCypher library to find all nodes in the statement and attach the label to each of them. After adding the label the Statement object will get converted into a valid cypher statement again.

Using a static String would be suitable for having multiple running instances of the application for each user base. Providing a Supplier as you can see in the original project will give you the opportunity to define some kind of resolver function. With this called right before intercepting the outgoing statement it is possible to provide a dynamically resolved label, for example the logged in user. This solution matches the need of having different users on a single instance/deployment.

Filtered data

By using a fixed string or a calculated label value you are able to shrink down the data one user can see and operate on. While the SPI support in Neo4j-OGM is officially supported by us, the label adding example code is by far not complete and waiting for you to report issues and missing statement support.

--

--