org.ihtsdo.otf.snomed.service.ConceptLookUpServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.ihtsdo.otf.snomed.service.ConceptLookUpServiceImpl.java

Source

/**
* Copyright 2014 IHTSDO
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-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.ihtsdo.otf.snomed.service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import javax.annotation.Resource;

import org.ihtsdo.otf.refset.domain.ChangeRecord;
import org.ihtsdo.otf.refset.exception.EntityNotFoundException;
import org.ihtsdo.otf.refset.graph.RefsetGraphAccessException;
import org.ihtsdo.otf.refset.graph.RefsetGraphFactory;
import org.ihtsdo.otf.refset.graph.gao.EdgeLabel;
import org.ihtsdo.otf.snomed.domain.Concept;
import org.ihtsdo.otf.snomed.domain.Properties;
import org.ihtsdo.otf.snomed.exception.ConceptServiceException;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.util.StringUtils;

import com.thinkaurelius.titan.core.TitanGraph;
import com.thinkaurelius.titan.core.TitanIndexQuery.Result;
import com.tinkerpop.blueprints.Direction;
import com.tinkerpop.blueprints.Vertex;

/**
 * Service to look up Terminology data.
 *
 */
public class ConceptLookUpServiceImpl implements ConceptLookupService {

    private static final Logger LOGGER = LoggerFactory.getLogger(ConceptLookUpServiceImpl.class);

    private RefsetGraphFactory factory;

    /* (non-Javadoc)
     * @see org.ihtsdo.otf.snomed.service.ConceptLookupService#getConcepts(java.util.List)
     */
    @Override
    @Cacheable(value = { "concepts" })
    public Map<String, Concept> getConcepts(Set<String> conceptIds) throws ConceptServiceException {

        LOGGER.debug("getting concepts details for {}", conceptIds);

        Map<String, Concept> concepts = new HashMap<String, Concept>();

        TitanGraph g = null;
        try {

            g = factory.getReadOnlyGraph();

            /**/
            List<String> idLst = new ArrayList<String>();
            idLst.addAll(conceptIds);

            int length = idLst.size() / 1024;
            int to = idLst.size() > 1024 ? 1024 : conceptIds.size();
            int from = 0;
            for (int i = 0; i < length + 1; i++) {

                LOGGER.debug("getting concept description from {} to {} ", from, to);
                List<String> subList = idLst.subList(from, to);

                String ids = org.apache.commons.lang.StringUtils.join(subList, " OR ");
                Iterable<Result<Vertex>> vs = g.indexQuery("concept", "v.sctid:" + ids).vertices();
                for (Result<Vertex> r : vs) {

                    Vertex v = r.getElement();

                    Object sctid = v.getProperty(Properties.sctid.toString());
                    Object label = v.getProperty(Properties.title.toString());
                    if (sctid != null && label != null && idLst.contains(sctid.toString())) {

                        Concept c = convertToConcept(v);
                        concepts.put(sctid.toString(), c);
                    }

                }

                //to run next loop if required
                from = to > idLst.size() ? idLst.size() : to;
                to = (to + 1024) > idLst.size() ? idLst.size() : to + 1024;

            }

            RefsetGraphFactory.commit(g);

        } catch (Exception e) {

            LOGGER.error("Error duing concept details for concept map fetch", e);
            RefsetGraphFactory.rollback(g);

            throw new ConceptServiceException(e);

        } finally {

            RefsetGraphFactory.shutdown(g);

        }

        LOGGER.debug("returning total {} concepts ", concepts.size());

        return Collections.unmodifiableMap(concepts);
    }

    /* (non-Javadoc)
     * @see org.ihtsdo.otf.snomed.service.ConceptLookupService#getConcept(java.lang.String)
     */
    @Override
    @Cacheable(value = { "concept" })
    public Concept getConcept(String conceptId) throws ConceptServiceException, EntityNotFoundException {

        LOGGER.debug("getting concept details for {} ", conceptId);

        if (StringUtils.isEmpty(conceptId)) {

            throw new EntityNotFoundException(String.format("Invalid concept id", conceptId));
        }

        TitanGraph g = null;

        try {

            g = factory.getReadOnlyGraph();

            Iterable<Vertex> vs = g.getVertices(Properties.sctid.toString(), conceptId);

            for (Vertex v : vs) {

                Concept c = convertToConcept(v);
                RefsetGraphFactory.commit(g);

                return c;

            }

            RefsetGraphFactory.commit(g);

        } catch (Exception e) {

            LOGGER.error("Error duing concept details fetch", e);
            RefsetGraphFactory.rollback(g);

            throw new ConceptServiceException(e);

        } finally {

            RefsetGraphFactory.shutdown(g);
        }

        throw new EntityNotFoundException(String.format("Invalid concept id", conceptId));

    }

    /* (non-Javadoc)
     * @see org.ihtsdo.otf.snomed.service.ConceptLookupService#getConceptIds(int, int)
     */
    @Override
    @Cacheable(value = { "conceptIds" })
    public Set<String> getConceptIds(int offset, int limit) throws ConceptServiceException {
        LOGGER.debug("getting concept ids with offset {} and limit {} ", offset, limit);

        TreeSet<String> conceptIds = new TreeSet<String>();

        TitanGraph g = null;
        try {

            g = factory.getReadOnlyGraph();

            Iterable<Result<Vertex>> vs = g.indexQuery("concept", "v.sctid:*").offset(offset).limit(limit)
                    .vertices();

            for (Result<Vertex> v : vs) {

                String sctid = v.getElement().getProperty(Properties.sctid.toString());

                if (!StringUtils.isEmpty(sctid)) {

                    LOGGER.trace("Adding sctid {} to concept id list ", sctid);

                    conceptIds.add(sctid);

                }

            }

            RefsetGraphFactory.commit(g);

        } catch (Exception e) {

            LOGGER.error("Error duing concept ids fetch ", e);
            RefsetGraphFactory.rollback(g);

            throw new ConceptServiceException(e);

        } finally {

            RefsetGraphFactory.shutdown(g);

        }
        LOGGER.debug("returning total {} concept ids ", conceptIds.size());

        return Collections.unmodifiableSortedSet(conceptIds);
    }

    /* (non-Javadoc)
     * @see org.ihtsdo.otf.snomed.service.ConceptLookupService#getTypes(String)
     */
    @Override
    public Map<String, String> getTypes(String id) throws ConceptServiceException {

        LOGGER.debug("getting types for given id {}", id);

        Map<String, String> types = new HashMap<String, String>();

        TitanGraph g = null;
        try {

            g = factory.getReadOnlyGraph();

        } catch (Exception e) {

            LOGGER.error("Error duing concept details for concept map fetch", e);

            throw new ConceptServiceException(e);

        } finally {

            RefsetGraphFactory.shutdown(g);

        }

        LOGGER.debug("returning total {} types ", types.size());

        return Collections.unmodifiableMap(types);
    }

    /**returns referenceComponentDescription for given referenceComponentId
     * @param referenceComponentId
     * @return
     * @throws RefsetGraphAccessException
     */
    @Override
    @Cacheable(value = { "referenceComponentDescription" })
    public String getMemberDescription(String referenceComponentId) throws RefsetGraphAccessException {

        LOGGER.debug("getting member description for {} ", referenceComponentId);

        String label = "";

        if (StringUtils.isEmpty(referenceComponentId)) {

            return label;
        }

        TitanGraph g = null;
        try {

            g = factory.getReadOnlyGraph();

            Iterable<Result<Vertex>> vs = g.indexQuery("concept", "v.sctid:" + referenceComponentId).vertices();
            for (Result<Vertex> r : vs) {

                Vertex v = r.getElement();

                label = v.getProperty(Properties.title.toString());
                break;

            }

            RefsetGraphFactory.commit(g);

        } catch (Exception e) {

            RefsetGraphFactory.rollback(g);

            LOGGER.error("Error duing concept details fetch", e);

            throw new RefsetGraphAccessException(e.getMessage(), e);

        } finally {

            RefsetGraphFactory.shutdown(g);

        }

        return label;
    }

    /** Returns {@link Map} of referenceComponentId as key and their description as value
     * @param referenceComponentId
     * @return
     * @throws RefsetGraphAccessException
     */
    @Override
    @Cacheable(value = { "referenceComponentDescriptions" })
    public Map<String, String> getMembersDescription(List<String> rcIds) throws RefsetGraphAccessException {

        LOGGER.trace("getting members description for {} ", rcIds);

        Map<String, String> descMap = new HashMap<String, String>();

        if (rcIds == null || rcIds.isEmpty()) {

            return descMap;
        }

        TitanGraph g = null;
        try {

            g = factory.getReadOnlyGraph();

            //max OR clause can be 1024 so send in 1024 at max in one call
            int length = rcIds.size() / 1024;
            int to = rcIds.size() > 1024 ? 1024 : rcIds.size();
            int from = 0;
            for (int i = 0; i < length + 1; i++) {

                LOGGER.debug("getting members description from {} to {} ", from, to);

                List<String> subList = rcIds.subList(from, to);

                String ids = org.apache.commons.lang.StringUtils.join(subList, " OR ");
                Iterable<Result<Vertex>> vs = g.indexQuery("concept", "v.sctid:" + ids).vertices();
                for (Result<Vertex> r : vs) {

                    Vertex v = r.getElement();

                    Object sctid = v.getProperty(Properties.sctid.toString());
                    Object label = v.getProperty(Properties.title.toString());
                    if (sctid != null && label != null && rcIds.contains(sctid.toString())) {

                        descMap.put(sctid.toString(), label.toString());

                    }

                }

                //to run next loop if required
                from = to > rcIds.size() ? rcIds.size() : to;
                to = (to + 1024) > rcIds.size() ? rcIds.size() : to + 1024;

            }

            RefsetGraphFactory.commit(g);

        } catch (Exception e) {

            RefsetGraphFactory.rollback(g);

            LOGGER.error("Error duing concept details fetch", e);

            throw new RefsetGraphAccessException(e.getMessage(), e);

        } finally {

            RefsetGraphFactory.shutdown(g);

        }

        return descMap;
    }

    /* (non-Javadoc)
     * @see org.ihtsdo.otf.snomed.service.ConceptLookupService#getConcepts(java.util.List)
     */
    @Override
    @Cacheable(value = { "conceptsHistory" })
    public Map<String, ChangeRecord<Concept>> getConceptHistory(Set<String> conceptIds)
            throws ConceptServiceException {

        LOGGER.debug("getting concepts details for {}", conceptIds);

        Map<String, ChangeRecord<Concept>> concepts = new HashMap<String, ChangeRecord<Concept>>();

        TitanGraph g = null;
        try {

            g = factory.getReadOnlyGraph();

            /**/
            List<String> idLst = new ArrayList<String>();
            idLst.addAll(conceptIds);

            int length = idLst.size() / 1024;
            int to = idLst.size() > 1024 ? 1024 : conceptIds.size();
            int from = 0;
            for (int i = 0; i < length + 1; i++) {

                LOGGER.debug("getting concept description from {} to {} ", from, to);
                List<String> subList = idLst.subList(from, to);

                String ids = org.apache.commons.lang.StringUtils.join(subList, " OR ");
                Iterable<Result<Vertex>> vs = g.indexQuery("concept", "v.sctid:" + ids).vertices();
                for (Result<Vertex> r : vs) {

                    Vertex v = r.getElement();

                    Object sctid = v.getProperty(Properties.sctid.toString());
                    Object label = v.getProperty(Properties.title.toString());
                    if (sctid != null && label != null && idLst.contains(sctid.toString())) {
                        ChangeRecord<Concept> cr = new ChangeRecord<Concept>();
                        List<Concept> cLst = new ArrayList<Concept>();
                        //TODO more work required
                        Concept c = convertToConcept(v);
                        cLst.add(c);

                        concepts.put(sctid.toString(), cr);
                    }

                }

                //to run next loop if required
                from = to > idLst.size() ? idLst.size() : to;
                to = (to + 1024) > idLst.size() ? idLst.size() : to + 1024;

            }

            RefsetGraphFactory.commit(g);

        } catch (Exception e) {

            LOGGER.error("Error duing concept details for concept map fetch", e);
            RefsetGraphFactory.rollback(g);

            throw new ConceptServiceException(e);

        } finally {

            RefsetGraphFactory.shutdown(g);

        }

        LOGGER.debug("returning total {} concepts ", concepts.size());

        return Collections.unmodifiableMap(concepts);
    }

    /* (non-Javadoc)
     * @see org.ihtsdo.otf.snomed.service.ConceptLookupService#getConcept(java.lang.String)
     */
    @Override
    @Cacheable(value = { "conceptHistory" })
    public ChangeRecord<Concept> getConceptHistory(String conceptId)
            throws ConceptServiceException, EntityNotFoundException {

        LOGGER.debug("getting concept details and history for {} ", conceptId);

        if (StringUtils.isEmpty(conceptId)) {

            throw new EntityNotFoundException(String.format("Invalid concept id", conceptId));
        }

        TitanGraph g = null;

        try {

            g = factory.getReadOnlyGraph();
            Iterable<Vertex> vs = g.getVertices(Properties.sctid.toString(), conceptId);

            for (Vertex v : vs) {

                ChangeRecord<Concept> cr = getHistory(v);
                return cr;
            }

            RefsetGraphFactory.commit(g);
        } catch (Exception e) {

            LOGGER.error("Error duing concept details fetch", e);
            RefsetGraphFactory.rollback(g);

            throw new ConceptServiceException(e);

        } finally {

            RefsetGraphFactory.shutdown(g);
        }

        throw new EntityNotFoundException(
                String.format("Concept details not available for given concept id %s", conceptId));

    }

    /**
     * @param v
     * @return
     */
    private ChangeRecord<Concept> getHistory(Vertex v) {
        ChangeRecord<Concept> cr = new ChangeRecord<Concept>();

        if (v != null) {

            Concept c = convertToConcept(v);
            cr.getRecords().add(c);
            Iterable<Vertex> vHcs = v.getVertices(Direction.OUT, EdgeLabel.hasState.toString());

            Map<DateTime, String> descriptions = getHistoryDescription(v);

            //now populate all history concept and use above map to populate title
            for (Vertex vHc : vHcs) {

                Concept hc = convertToConcept(vHc);

                DateTime et = hc.getEffectiveTime();
                LOGGER.debug("Effective time {} ", et);

                if (descriptions.containsKey(et)) {

                    hc.setLabel(descriptions.get(hc.getEffectiveTime()));
                }

                cr.getRecords().add(hc);

            }
        }

        return cr;
    }

    /**
     * @param v
     * @return
     */
    private Map<DateTime, String> getHistoryDescription(Vertex v) {
        Map<DateTime, String> desc = new HashMap<DateTime, String>();

        if (v != null) {

            Iterable<Vertex> vDs = v.getVertices(Direction.OUT, EdgeLabel.hasDescription.toString());
            for (Vertex vD : vDs) {
                //only interested in fsn description not synonyms or any other type of description .
                //so check description vertex which has 900000000000003001 typedid for history label.
                Object typeId = vD.getProperty(Properties.typeId.toString());
                if ("900000000000003001".equals(typeId)) {

                    Iterable<Vertex> vHDs = vD.getVertices(Direction.OUT, EdgeLabel.hasState.toString());

                    for (Vertex vHD : vHDs) {

                        Object et = vHD.getProperty(Properties.effectiveTime.toString());
                        Object title = vHD.getProperty(Properties.title.toString());

                        LOGGER.debug("Effective time {} & title {} ", et, title);

                        if (et != null && title != null) {

                            desc.put(new DateTime(et), title.toString());

                        }
                    }

                }
            }
        }

        LOGGER.debug("Total {} history description records", desc.size());

        return desc;
    }

    /**
     * @param factory the factory to set
     */
    @Resource(name = "snomedGraphFactory")
    public void setFactory(RefsetGraphFactory factory) {

        this.factory = factory;
    }

    private Concept convertToConcept(Vertex v) {
        if (v == null) {

            return null;
        }

        Set<String> keys = v.getPropertyKeys();
        Concept c = new Concept();

        if (keys.contains(Properties.effectiveTime.toString())) {

            Long effectiveTime = v.getProperty(Properties.effectiveTime.toString());
            c.setEffectiveTime(new DateTime(effectiveTime));
        }

        if (keys.contains(Properties.moduleId.toString())) {

            String moduleId = v.getProperty(Properties.moduleId.toString());
            c.setModuleId(moduleId);

        }

        if (keys.contains(Properties.sctid.toString())) {

            String sctid = v.getProperty(Properties.sctid.toString());

            c.setId(sctid);

        }

        if (keys.contains(Properties.status.toString())) {

            String status = v.getProperty(Properties.status.toString());
            boolean active = "1".equals(status) ? true : false;
            c.setActive(active);

        }

        if (keys.contains(Properties.title.toString())) {

            String title = v.getProperty(Properties.title.toString());

            c.setLabel(title);

        }

        return c;
    }

}