All Downloads are FREE. Search and download functionalities are using the official Maven repository.

apoc.convert.ConvertExtended Maven / Gradle / Ivy

package apoc.convert;

import apoc.Extended;
import apoc.meta.Types;
import apoc.util.MissingDependencyException;
import apoc.util.collection.Iterables;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Path;
import org.neo4j.graphdb.Relationship;
import org.neo4j.procedure.Description;
import org.neo4j.procedure.Name;
import org.neo4j.procedure.UserFunction;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

import static apoc.convert.ConvertExtendedUtil.MAPPING_KEY;
import static apoc.convert.ConvertExtendedUtil.getYamlFactory;
import static apoc.convert.ConvertExtendedUtil.parse;
import static apoc.util.ExtendedUtil.toValidYamlValue;
import static apoc.util.Util.labelStrings;
import static apoc.util.Util.map;

@Extended
public class ConvertExtended {
    private static final String YAML_MISSING_DEPS_ERROR = """
            Cannot find the Yaml client jar.
            Please put the apoc-yaml-dependencies-5.x.x-all.jar into plugin folder.
            See the documentation: https://neo4j.com/labs/apoc/5/overview/apoc.convert/apoc.convert.toYaml/#yaml-dependencies""";

    
    @UserFunction("apoc.convert.toYaml")
    @Description("apoc.convert.toYaml(value, $config) - Serializes the given value to a YAML string")
    public String toYaml(@Name("value") Object value, @Name(value = "config", defaultValue = "{}") Map config) {
        Object result = writeYamlResult(value);
        try {
            return getYamlFactory(result, config);
        } catch (NoClassDefFoundError e) {
            throw new MissingDependencyException(YAML_MISSING_DEPS_ERROR);
        } catch (IOException e) {
            throw new RuntimeException("Can't convert " + "value" + " to yaml", e);
        }
    }

    @UserFunction("apoc.convert.fromYaml")
    @Description("apoc.convert.fromYaml(value, $config) - Deserializes the YAML string to Neo4j value")
    public Object fromYaml(@Name("value") String value, @Name(value = "config", defaultValue = "{}") Map config) throws Exception {
        try {
            Object parse = parse(value, config);
            var mappingConf = (Map) config.getOrDefault(MAPPING_KEY, Map.of());
            return toValidYamlValue(parse, null, mappingConf, true);
        } catch (NoClassDefFoundError e) {
            throw new MissingDependencyException(YAML_MISSING_DEPS_ERROR);
        }
    }

    /**
     * convert result recursively, 
     * which handle complex types, like list/map of nodes/rels/paths
     */
    private Object writeYamlResult(Object value) {
        Types type = Types.of(value);
        return switch (type) {
            case NODE -> nodeToMap((Node) value);
            case RELATIONSHIP -> relToMap((Relationship) value);
            
            case PATH -> writeYamlResult(Iterables.stream((Path) value)
                    .map(i -> i instanceof Node ? nodeToMap((Node) i) : relToMap((Relationship) i))
                    .collect(Collectors.toList()));
            
            case LIST -> ConvertUtils.convertToList(value).stream()
                    .map(this::writeYamlResult)
                    .collect(Collectors.toList());
            
            case MAP -> ((Map) value)
                    .entrySet()
                    .stream()
                    .collect(
                            HashMap::new, // workaround for https://bugs.openjdk.java.net/browse/JDK-8148463
                            (mapAccumulator, entry) ->
                                    mapAccumulator.put(entry.getKey(), writeYamlResult(entry.getValue())),
                            HashMap::putAll);
            
            default -> value;
        };
    }

    private Map relToMap(Relationship rel) {
        Map mapRel = map(
                "id", rel.getElementId(),
                "type", "relationship",
                "label", rel.getType().toString(),
                "start", nodeToMap(rel.getStartNode()),
                "end", nodeToMap(rel.getEndNode()));

        return mapWithOptionalProps(mapRel, rel.getAllProperties());
    }

    private Map nodeToMap(Node node) {
        Map mapNode = map("id", node.getElementId());

        mapNode.put("type", "node");

        if (node.getLabels().iterator().hasNext()) {
            mapNode.put("labels", labelStrings(node));
        }
        return mapWithOptionalProps(mapNode, node.getAllProperties());
    }

    private Map mapWithOptionalProps(Map mapEntity, Map props) {
        if (!props.isEmpty()) {
            mapEntity.put("properties", props);
        }
        return mapEntity;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy