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

apoc.meta.Meta Maven / Gradle / Ivy

There is a newer version: 5.24.0
Show newest version
package apoc.meta;

import apoc.export.util.NodesAndRelsSubGraph;
import apoc.result.GraphResult;
import apoc.result.MapResult;
import apoc.result.VirtualGraph;
import apoc.result.VirtualNode;
import apoc.result.VirtualRelationship;
import apoc.util.MapUtil;
import apoc.util.collection.Iterables;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.neo4j.cypher.export.CypherResultSubGraph;
import org.neo4j.cypher.export.DatabaseSubGraph;
import org.neo4j.cypher.export.SubGraph;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Entity;
import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.ResourceIterator;
import org.neo4j.graphdb.Result;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.schema.ConstraintDefinition;
import org.neo4j.graphdb.schema.ConstraintType;
import org.neo4j.graphdb.schema.IndexDefinition;
import org.neo4j.graphdb.schema.Schema;
import org.neo4j.internal.kernel.api.Read;
import org.neo4j.internal.kernel.api.TokenRead;
import org.neo4j.kernel.api.KernelTransaction;
import org.neo4j.logging.Log;
import org.neo4j.procedure.Context;
import org.neo4j.procedure.Description;
import org.neo4j.procedure.Name;
import org.neo4j.procedure.Procedure;
import org.neo4j.procedure.UserFunction;

import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import static apoc.util.MapUtil.map;
import static java.lang.String.format;
import static org.neo4j.internal.kernel.api.TokenRead.ANY_LABEL;
import static org.neo4j.internal.kernel.api.TokenRead.ANY_RELATIONSHIP_TYPE;

public class Meta {

    @Context
    public Transaction tx;

    @Context
    public KernelTransaction kernelTx;

    @Context
    public Transaction transaction;

    @Context public Log log;

    public static class MetaResult {
        public String label;
        public String property;
        public long count;
        public boolean unique;
        public boolean index;
        public boolean existence;
        public String type;
        public boolean array;
        public List sample;
        public long left; // 0,1,
        public long right; // 0,1,many
        public List other = new ArrayList<>();
        public List otherLabels = new ArrayList<>();
        public String elementType;
    }

    public static class MetaItem extends MetaResult {
        public long leftCount; // 0,1,
        public long rightCount; // 0,1,many

        public MetaItem addLabel(String label) {
            this.otherLabels.add(label);
            return this;
        }

        public MetaItem(String label, String name) {
            this.label = label;
            this.property = name;
        }

        public MetaItem inc() {
            count ++;
            return this;
        }
        public MetaItem rel(long out, long in) {
            this.type = Types.RELATIONSHIP.name();
            if (out>1) array = true;
            leftCount += out;
            rightCount += in;
            left = leftCount / count;
            right = rightCount / count;
            return this;
        }

        public MetaItem other(List labels) {
            for (String l : labels) {
                if (!this.other.contains(l)) this.other.add(l);
            }
            return this;
        }

        public MetaItem type(String type) {
            this.type = type;
            return this;
        }

        public MetaItem array(boolean array) {
            this.array = array;
            return this;
        }

        public MetaItem elementType(String elementType) {
            switch(elementType){
                case "NODE" : this.elementType = "node"; break;
                case "RELATIONSHIP" : this.elementType = "relationship"; break;
            }
            return this;
        }
    }

    @UserFunction("apoc.meta.cypher.isType")
    @Description("Returns true if the given value matches the given type.")
    public boolean isTypeCypher(@Name("value") Object value, @Name("type") String type) {
        return type.equalsIgnoreCase(typeCypher(value));
    }

    @UserFunction("apoc.meta.cypher.type")
    @Description("Returns the type name of the given value.")
    public String typeCypher(@Name("value") Object value) {
        Types type = Types.of(value);

        switch (type) {
            case ANY: // TODO Check if it's necessary
                return value.getClass().getSimpleName();
            default:
                return type.toString();
        }
    }

    @UserFunction("apoc.meta.cypher.types")
    @Description("Returns a map containing the type names of the given values.")
    public Map typesCypher(@Name("props") Object target) {
        Map properties = Collections.emptyMap();
        if (target instanceof Node) properties = ((Node)target).getAllProperties();
        if (target instanceof Relationship) properties = ((Relationship)target).getAllProperties();
        if (target instanceof Map) {
            //noinspection unchecked
            properties = (Map) target;
        }

        Map result = new LinkedHashMap<>(properties.size());
        properties.forEach((key,value) -> {
            result.put(key, typeCypher(value));
        });

        return result;
    }

    public static class MetaStats {
        public final long labelCount;
        public final long relTypeCount;
        public final long propertyKeyCount;
        public final long nodeCount;
        public final long relCount;
        public final Map labels;
        public final Map relTypes;
        public final Map relTypesCount;
        public final Map stats;

        public MetaStats(long labelCount, long relTypeCount, long propertyKeyCount, long nodeCount, long relCount, Map labels, Map relTypes, Map relTypesCount) {
            this.labelCount = labelCount;
            this.relTypeCount = relTypeCount;
            this.propertyKeyCount = propertyKeyCount;
            this.nodeCount = nodeCount;
            this.relCount = relCount;
            this.labels = labels;
            this.relTypes = relTypes;
            this.relTypesCount = relTypesCount;
            this.stats = map("labelCount", labelCount, "relTypeCount", relTypeCount, "propertyKeyCount", propertyKeyCount,
                    "nodeCount", nodeCount, "relCount", relCount,
                    "labels", labels, "relTypes", relTypes);
        }
    }

    interface StatsCallback {
        void label(int labelId, String labelName, long count);
        void rel(int typeId, String typeName, long count);
        void rel(int typeId, String typeName, int labelId, String labelName, long out, long in);
    }

    @Procedure("apoc.meta.stats")
    @Description("Returns the metadata stored in the transactional database statistics.")
    public Stream stats() {
        return Stream.of(collectStats());
    }

    @UserFunction(name = "apoc.meta.nodes.count")
    @Description("Returns the sum of the nodes with the given labels in the list.")
    public long count(@Name(value = "nodes", defaultValue = "[]") List nodes, @Name(value = "config", defaultValue = "{}") Map config) {
        MetaConfig conf = new MetaConfig(config);
        final DatabaseSubGraph subGraph = new DatabaseSubGraph(transaction);
        Stream