neo4j java node dynamic properties

2019-09-06 09:25发布

问题:

I am trying to create nodes of a specific type with properties which can be dynamic . For Example : I can create a Person node with name,age,address properties. But these need not be the only properties when I create another Person node. This node can have name,age,address and an additional property salary. Using spring data or query DSL needs me to create Java POJO class Person with fixed number of instance variables name,age and address .

@NodeEntity
public class Person {
@GraphId private Long id;

private String name;
private String age;
private String address;
}

I cannot add a dynamic property for salary for another Person node. Is there a way I can achieve this ?

回答1:

Dynamic properties are not supported in Neo4j-OGM at the moment (see https://jira.spring.io/browse/DATAGRAPH-555)

If you only interact with your graph via the OGM and do not have to query on individual dynamic properties, you could try a Map of properties with a custom Converter, that converts this Map to a String (like json). The OGM will then use this converter to serialize the map to and from the graph. Note that because the values are squashed into a String, it is now not trivial to query on an individual dynamic property.

To create a custom converter you need to implement org.neo4j.ogm.typeconversion.AttributeConverter and provide the implementation to convert from a Map to String. Then, annotate your map property in your domain entity like this:

 @Convert(MoneyConverter.class)

Edit:

As pointed out by Michael, if the salary is the only extra optional property, then it makes sense to have this property but set it only when it has a value. Dynamic properties are overkill in this case. You may want to use dynamic properties when you have an unknown and arbitrary set of properties to be persisted with the node



回答2:

You can workaround the limitations by creating a CompositeAttributeConverter saving each dynamic property in the graph (not only as JSON-String wich cannot be queried well - as mentioned by luanne in the accepted answer)

import java.lang.reflect.Field;
import java.util.*;

import org.neo4j.ogm.typeconversion.CompositeAttributeConverter;

public abstract class DynamicPropertiesConverter implements CompositeAttributeConverter<Map<String, ?>> {

    private Set<String> blacklist;

    public DynamicPropertiesConverter(Class<?> clazz) {
        blacklist = new HashSet<>();
        addAllFields(clazz);
    }
    public DynamicPropertiesConverter(Set<String> blacklist) {
        this.blacklist = blacklist;
    }

    public void addAllFields(Class<?> type) {
        for (Field field : type.getDeclaredFields()) {
            blacklist.add(field.getName());
        }
        if (type.getSuperclass() != null) {
            addAllFields(type.getSuperclass());
        }
    }

    @Override
    public Map<String, ?> toGraphProperties(Map<String, ?> value) {
        Map<String, ?> result = new HashMap<>(value);
        result.keySet().removeAll(blacklist);
        return result;
    }

    @Override
    public Map<String, ?> toEntityAttribute(Map<String, ?> value) {
        return toGraphProperties(value);
    }
}

Now you can create a special version of this converter:

public class DynamicNodePropertiesConverter extends DynamicPropertiesConverter {
    public DynamicNodePropertiesConverter() {
        super(Node.class);
    }
}

And use it like this:

import java.util.Map;

import DynamicNodePropertiesConverter;
import org.neo4j.ogm.annotation.NodeEntity;
import org.neo4j.ogm.annotation.Relationship;
import org.neo4j.ogm.annotation.typeconversion.Convert;

@NodeEntity
public class Node {

    @Convert(DynamicNodePropertiesConverter.class)
    private Map<String, Object> data;

    /* getter and setter */
}