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

org.randombits.confluence.metadata.indexing.graph.DefaultIndexManager Maven / Gradle / Ivy

There is a newer version: 7.4.1
Show newest version
package org.randombits.confluence.metadata.indexing.graph;

import com.atlassian.confluence.event.events.cluster.ClusterEventWrapper;
import com.atlassian.event.Event;
import com.atlassian.event.api.EventListener;
import com.atlassian.event.api.EventPublisher;
import com.tinkerpop.blueprints.Direction;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.blueprints.VertexQuery;
import com.tinkerpop.blueprints.impls.orient.OrientGraph;
import com.tinkerpop.blueprints.impls.orient.OrientVertex;
import org.apache.commons.lang3.StringUtils;
import org.randombits.confluence.metadata.event.MetadataUpdatedEvent;
import org.randombits.confluence.metadata.FieldNotFoundException;
import org.randombits.confluence.metadata.indexing.IndexManager;
import org.randombits.confluence.metadata.indexing.graph.vertex.Collection;
import org.randombits.confluence.metadata.indexing.graph.vertex.Content;
import org.randombits.confluence.metadata.indexing.graph.vertex.KeyValueField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;

/**
 * Default implementation of {@link org.randombits.confluence.metadata.indexing.IndexManager}.
 *
 * @author kaifung
 * @since 7.0.0.20150209
 */
@Component
public class DefaultIndexManager implements IndexManager, DisposableBean {

    private static final Logger log = LoggerFactory.getLogger(IndexManager.class);

    private static final String ORIENTDB_CLASS_PREFIX = "class:";

    private final EventPublisher eventPublisher;

    private IndexDatabase indexDatabase;

    public DefaultIndexManager(EventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
        this.eventPublisher.register(this);
    }

    @Override
    public  T query(final String contentId, final String fieldName, Class clazz) throws IllegalArgumentException, FieldNotFoundException {
        return query(contentId, null, fieldName, clazz);
    }

    @Override
    public  T query(final String contentId, final List path, final String fieldName, Class clazz) throws IllegalArgumentException, FieldNotFoundException {
        if (!String.class.equals(clazz)) {
            throw new IllegalArgumentException("Unable to query index with invalid type : " + clazz);
        }

        if (StringUtils.isEmpty(contentId) || StringUtils.isEmpty(fieldName)) {
            throw new IllegalArgumentException("Content ID and field name must not be empty.");
        }

        T result = indexDatabase.executeTransaction(new IndexDatabase.Transaction() { @Override public T execute(OrientGraph graph) {
            T result = null;
            for (Vertex content : graph.getVertices(Content.CONTENT_ID.getPrefixedPropertyKey(), contentId)) {
                result = findVertex(content, fieldName, path);
            }
            return result;
        }});

        if (result == null) {
            throw new FieldNotFoundException("Unable to find field with name: " + fieldName);
        }

        return result;
    }

    @Override
    public void buildIndex(final String contentId, final Map metadata) {
        indexDatabase.executeTransaction(new IndexDatabase.Transaction() {
            @Override
            public Void execute(OrientGraph graph) {
                if (graph.countVertices() != 0) {
                    for (Vertex contentVertex : graph.getVertices(Content.CONTENT_ID.getPrefixedPropertyKey(), contentId)) {
                        removeVertex(graph, contentVertex);
                    }
                }
                Vertex content = graph.addVertex(ORIENTDB_CLASS_PREFIX + Content.CLASS_NAME, Content.CONTENT_ID.toCanonicalValue(), contentId);
                indexFields(content, metadata, graph);
                return null;
            }
        });
    }

    @Override
    public void destroy() throws Exception {
        eventPublisher.unregister(this);
    }

    /**
     * Listen to {@link org.randombits.confluence.metadata.event.MetadataUpdatedEvent}.
     *
     * @param metadataUpdatedEvent
     */
    @EventListener
    public void onEvent(MetadataUpdatedEvent metadataUpdatedEvent) {
        if (metadataUpdatedEvent.shouldBuildIndex()) {
            buildIndex(
                    metadataUpdatedEvent.getMetadataStorage().getContent().getIdAsString(),
                    metadataUpdatedEvent.getMetadataStorage().getBaseMap()
            );
        }
    }

    /**
     * Listen to events from other nodes in clustered environment.
     *
     * @param clusterEventWrapper
     */
    @EventListener
    public void onEvent(ClusterEventWrapper clusterEventWrapper) {
        Event event = clusterEventWrapper.getEvent();
        if (event instanceof MetadataUpdatedEvent) {
            onEvent((MetadataUpdatedEvent) event);
        }
    }

    private static boolean isVertexClass(Vertex vertex, String className) {
        if (vertex instanceof OrientVertex) {
            return ((OrientVertex) vertex).getLabel().equals(className);
        }
        return false;
    }

    private  T findVertex(Vertex sourceVertex, String fieldName, List path) {
        for (Vertex destinationVertex : sourceVertex.query().direction(Direction.OUT).labels(Content.EDGE_CONTAINS).vertices()) {
            if (path == null || path.isEmpty()) {
                // Stop traversing to next level.
                if (isVertexClass(destinationVertex, KeyValueField.CLASS_NAME)
                        && fieldName.equals(destinationVertex.getProperty(KeyValueField.NAME.toCanonicalValue()))) {
                    return destinationVertex.getProperty(KeyValueField.VALUE.toCanonicalValue());
                }
            } else {
                // Traverse to next level.
                if (isVertexClass(destinationVertex, Collection.CLASS_NAME)
                        && path.get(0).equals(destinationVertex.getProperty(Collection.NAME.toCanonicalValue()))) {
                    path.remove(0);
                    return findVertex(destinationVertex, fieldName, path);
                }
            }
        }
        return null;
    }

    private void removeVertex(OrientGraph graph, Vertex vertex) {
        VertexQuery vertexQuery = vertex.query().direction(Direction.OUT).labels(Content.EDGE_CONTAINS);
        if (vertexQuery.count() != 0) {
            for (Vertex outerVertex : vertexQuery.vertices()) {
                removeVertex(graph, outerVertex);
            }
        }
        graph.removeVertex(vertex);
    }

    private void indexFields(Vertex sourceVertex, Map metadata, OrientGraph graph) {
        for (Map.Entry entry : metadata.entrySet()) {
            if (entry.getValue() instanceof Map) {
                // Add Collection vertex.
                Vertex destinationVertex = graph.addVertex(ORIENTDB_CLASS_PREFIX + Collection.CLASS_NAME,
                        Collection.NAME.toCanonicalValue(), entry.getKey()
                );

                graph.addEdge(null, sourceVertex, destinationVertex, Content.EDGE_CONTAINS);
                indexFields(destinationVertex, (Map) entry.getValue(), graph);
            } else {
                // Add KeyValueField vertex.
                Vertex destinationVertex = graph.addVertex(ORIENTDB_CLASS_PREFIX + KeyValueField.CLASS_NAME,
                        KeyValueField.NAME.toCanonicalValue(), entry.getKey(),
                        KeyValueField.VALUE.toCanonicalValue(), entry.getValue()
                );

                graph.addEdge(null, sourceVertex, destinationVertex, Content.EDGE_CONTAINS);
            }
        }
    }

    @Autowired
    public void setIndexDatabase(IndexDatabase indexDatabase) {
        this.indexDatabase = indexDatabase;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy