Neo4j 5.x Spring Boot Starter Data Neo4j 3.5.6 Adding a property to a relation

Hi, full disclosure, I am new to Neo4j. As a learning tool I am rewriting an app to use Neo4j with a view to leveraging it’s functionality for a reccommendation tool and eventually a machine learning engine. To that end I would like to implement a relationship between two nodes as follows.

What I'd like to do is to add one or more properties to the HAS_INGREDIENT relationship. For example quantity, such that Recipe is 'Pancakes' which has 'milk' as an Ingredient and the relationship attribute quantity is '200ml'. What I want to avoid is having to introduce another Node, Recipe_Ingredient, requiring a link to both Recipe and Ingredient.

 package com.example.demo.recipe;

import org.springframework.data.annotation.Id;
import org.springframework.data.neo4j.core.schema.Node;
import org.springframework.data.neo4j.core.schema.Property;
import org.springframework.data.neo4j.core.schema.Relationship;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;


@Component
@Node("Recipe")
public class RecipeEntity {
    @Id
    private String recipeId;
    @Property
    private String name;
    @Property
    private String description;


    @Relationship(type = "HAS_INGREDIENT",  direction = Relationship.Direction.OUTGOING)
    private ArrayList<IngredientEntity> ingredients;

where IngredientEntity is defined as ...

package com.example.demo.recipe;

import org.springframework.data.neo4j.core.schema.Id;
import org.springframework.data.neo4j.core.schema.Node;
import org.springframework.data.neo4j.core.schema.Property;
import org.springframework.stereotype.Component;

import java.util.UUID;

@Component
@Node("Ingredient")
public class IngredientEntity implements Ingredient{

    @Id
    private String id;
    @Property
    private String name;

    //default no arg constructor

    public IngredientEntity() {}

    //Constructor
    public IngredientEntity(String name, int quantity, String unit) {
        this.id = UUID.randomUUID().toString();
        this.name = name;
    }

    @Override
    public String getId() {
        return id.toString();
    }

    @Override
    public String getName() {
        return name;
    }

    public String toString() {
        return "Ingredient{id='" + id + "', name='" + name + "'}";
    }
}

I have seen in older versions of the Neo4j driver code, the ability to add an attribute to relationship class using properties="<some value>" Firstly I'd like to know if this is still possible, secondly is there an example tutorial showing how to build the Relationship Property in Java, Spring-boot.

Thanks in advance for any help

FYI these are the imports from my POM

		<java.version>17</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-neo4j</artifactId>
			<version>3.5.6</version>
		</dependency>

		<!-- Neo4j Driver -->
		<!--dependency>
			<groupId>org.neo4j.driver</groupId>
			<artifactId>neo4j-java-driver</artifactId>
		</dependency -->

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-docker-compose</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>
	</dependencies>

This looks already promising.

The only bit you’re missing here is the introduction of a RelationshipProperties class. ( Metadata-based Mapping :: Spring Data Neo4j )
Something like this

@RelationshipProperties
public class IngredientRelationship {

@RelationshipId
String id;

@TargetNode
IngredientEntity ingredientEntity;

int quantity;
// other properties
}

and change the relationship definition in your RecipeEntity to

private List<IngredientRelationship> ingredients;

Completely unrelated, but since I am here:

  1. You don’t have to annotate your properties with @Property if you are not planning to have different field names in Java entities and Neo4j properties.
  2. SDN provides a UUID generator out-of-the-box. You don’t have to do this on your own. There is more complexity involved in handling your own @Id without @GeneratedValue (e.g. the use of the additional @Version property Metadata-based Mapping :: Spring Data Neo4j )
    Just use this and let SDN handle the ids.
@Id
@GeneratedValue(UUIDStringGenerator.class)
1 Like

Many thanks Gerrit, I looked up the documentation links you suggested about this and the penny dropped. I also watched your YouTube session here : https://www.youtube.com/live/laAOQLKWy0o?si=nmL5ERMuytcPN3U-
My demo code is now working. I've put the finished code on github here : GitHub - ChristopherWebster/neo4j_demo: A simple Write-Read test from java using Spring-Data-Neo4j demonstrating both anotated relationships and Relationship property mapping. for future reference. Now I need to go back to my project and do some major refactoring. Thanks again for you help.

1 Like