Package org.cedj.geekseek.domain.relation.neo

Source Code of org.cedj.geekseek.domain.relation.neo.GraphRelationRepository$Named

/*
* Licensed by the authors under the Creative Commons
* Attribution-ShareAlike 2.0 Generic (CC BY-SA 2.0)
* License:
*
* http://creativecommons.org/licenses/by-sa/2.0/
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.cedj.geekseek.domain.relation.neo;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.inject.Inject;

import org.cedj.geekseek.domain.Repository;
import org.cedj.geekseek.domain.model.Identifiable;
import org.cedj.geekseek.domain.relation.RelationRepository;
import org.cedj.geekseek.domain.relation.model.Relation;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.UniqueFactory;

@ApplicationScoped
public class GraphRelationRepository implements RelationRepository {

    private static final String PROP_INDEX_NODE = "all_nodes";
    private static final String PROP_INDEX_REL = "all_relations";
    private static final String PROP_ID = "id";
    private static final String PROP_NODE_CLASS = "_classname";
    private static final String PROP_CREATED = "created";
    private static final String REL_TYPE_ALL = "all";

    @Inject
    private GraphDatabaseService graph;

    @Inject
    private BeanManager manager;

    /* (non-Javadoc)
     * @see org.cedj.geekseek.domain.relation.RelationRepositoryTe#add(org.cedj.geekseek.domain.model.Identifiable, java.lang.String, org.cedj.geekseek.domain.model.Identifiable)
     */
    @Override
    public Relation add(Identifiable source, final String type, Identifiable target) {

        Transaction tx = graph.beginTx();
        try {
            Node root =graph.getNodeById(0);
            String sourceTypeName = source.getClass().getSimpleName();
            String targetTypeName = target.getClass().getSimpleName();
            Node sourceTypeNode = getOrCreateNodeType(sourceTypeName);
            Node targetTypeNode = getOrCreateNodeType(targetTypeName);
            getOrCreateRelationship(root, sourceTypeNode, Named.relation(sourceTypeName));
            getOrCreateRelationship(root, targetTypeNode, Named.relation(targetTypeName));

            Node sourceNode = getOrCreateNode(source, sourceTypeName);
            getOrCreateRelationship(sourceTypeNode, sourceNode, Named.relation(REL_TYPE_ALL));
            Node targetNode = getOrCreateNode(target, targetTypeName);
            getOrCreateRelationship(targetTypeNode, targetNode, Named.relation(REL_TYPE_ALL));

            getOrCreateRelationship(sourceNode, targetNode, Named.relation(type));

            tx.success();
        } catch(Exception e) {
            tx.failure();
            throw new RuntimeException(
                "Could not add relation of type " + type + " between " + source + " and " + target, e);
        } finally {
          tx.finish();
        }
        return new Relation(source.getId(), target.getId(), type);
    }

    @Override
    public void remove(Identifiable source, String type, Identifiable target) {

        Transaction tx = graph.beginTx();
        try {
            Index<Node> nodeIndex = graph.index().forNodes(PROP_INDEX_NODE);
            Index<Relationship> relationIndex = graph.index().forRelationships(PROP_INDEX_REL);

            Node sourceNode = nodeIndex.get(PROP_ID, source.getId()).getSingle();
            Node targetNode = nodeIndex.get(PROP_ID, target.getId()).getSingle();
            for(Relationship rel : sourceNode.getRelationships(Named.relation(type))) {
                if(rel.getEndNode().equals(targetNode)) {
                    rel.delete();
                    relationIndex.remove(rel);
                }
            }

            tx.success();
        } catch(Exception e) {
            tx.failure();
            throw new RuntimeException(
                "Could not add relation of type " + type + " between " + source + " and " + target, e);
        } finally {
          tx.finish();
        }
    }

    /* (non-Javadoc)
     * @see org.cedj.geekseek.domain.relation.RelationRepositoryTe#findTargets(org.cedj.geekseek.domain.model.Identifiable, java.lang.String, java.lang.Class)
     */
    @Override
    public <T extends Identifiable> List<T> findTargets(Identifiable source, final String type, final Class<T> targetType) {

        Repository<T> repo = locateTargetRepository(targetType);
        if(repo == null) {
            throw new RuntimeException("Could not locate a " + Repository.class.getName() + " instance for Type " + targetType.getName());
        }

        List<T> targets = new ArrayList<T>();
        Index<Node> index = graph.index().forNodes(PROP_INDEX_NODE);
        Node node = index.get(PROP_ID, source.getId()).getSingle();
        if(node == null) {
            return targets;
        }
        Transaction tx = graph.beginTx();

        try {
            Iterable<Relationship> relationships = node.getRelationships(Named.relation(type));
            for(Relationship relation : relationships) {
                String targetId = relation.getOtherNode(node).getProperty(PROP_ID).toString();
                T target = repo.get(targetId);
                if(target == null) {
                    // Do some auto clean up if target node not found in Target repo
                    relation.getOtherNode(node).delete();
                    for(Relationship other : relation.getOtherNode(node).getRelationships()) {
                        other.delete();
                    }
                } else {
                    targets.add(target);
                }
            }
            tx.success();
        } catch(Exception e) {
            tx.failure();
            throw new RuntimeException(
                "Could not clean up relation of type " + type + " from " + source, e);
        } finally {
          tx.finish();
        }
        return targets;
    }

    /**
     * Helper method that looks in the BeanManager for a Repository that match signature
     * Repository<T>.
     *
     * Used to dynamically find repository to load targets from.
     *
     * @param targetType Repository object type to locate
     * @return Repository<T>
     */
    private <T extends Identifiable> Repository<T> locateTargetRepository(final Class<T> targetType) {
        ParameterizedType paramType = new ParameterizedType() {
            @Override
            public Type getRawType() {
                return Repository.class;
            }
            @Override
            public Type getOwnerType() {
                return null;
            }
            @Override
            public Type[] getActualTypeArguments() {
                return new Type[] {targetType};
            }
        };

        Set<Bean<?>> beans = manager.getBeans(paramType);
        Bean<?> bean = manager.resolve(beans);
        CreationalContext<?> cc = manager.createCreationalContext(null);

        @SuppressWarnings("unchecked")
        Repository<T> repo = (Repository<T>)manager.getReference(bean, paramType, cc);
        return repo;
    }

    private Node getOrCreateNodeType(String type) {
        UniqueFactory<Node> factory = new UniqueFactory.UniqueNodeFactory(graph, PROP_INDEX_NODE) {
            @Override
            protected void initialize(Node created, Map<String, Object> properties) {
                created.setProperty(PROP_ID, properties.get(PROP_ID));
            }
        };
        return factory.getOrCreate(PROP_ID, type);
    }

    private Node getOrCreateNode(Identifiable source, final String nodeClassType) {
        UniqueFactory<Node> factory = new UniqueFactory.UniqueNodeFactory(graph, PROP_INDEX_NODE) {
            @Override
            protected void initialize(Node created, Map<String, Object> properties) {
                created.setProperty(PROP_ID, properties.get(PROP_ID));
                created.setProperty(PROP_NODE_CLASS, nodeClassType);
            }
        };
        return factory.getOrCreate(PROP_ID, source.getId());
    }

    private Relationship getOrCreateRelationship(final Node source, final Node target, final RelationshipType type) {
        final String key = generateKey(source, target, type);

        UniqueFactory<Relationship> factory = new UniqueFactory.UniqueRelationshipFactory(graph, PROP_INDEX_REL) {

            @Override
            protected Relationship create(Map<String, Object> properties) {
                Relationship rel = source.createRelationshipTo(target, type);
                rel.setProperty(PROP_ID, properties.get(PROP_ID));
                return rel;
            }

            @Override
            protected void initialize(Relationship rel, Map<String, Object> properties) {
                rel.setProperty(PROP_CREATED, System.currentTimeMillis());
            }
        };
        return factory.getOrCreate(PROP_ID, key);
    }

    /**
     * Generate some unique key we can identify a relationship with.
     */
    private String generateKey(Node source, Node target, RelationshipType type) {
        return source.getProperty(PROP_ID, "X") + "-" + type.name() + "-" + target.getProperty(PROP_ID, "X");
    }

    private static class Named implements RelationshipType {

        public static RelationshipType relation(String name) {
            return new Named(name);
        }

        private String name;

        private Named(String name) {
            this.name = name;
        }

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

Related Classes of org.cedj.geekseek.domain.relation.neo.GraphRelationRepository$Named

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.