Java tutorial
/** * 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; } }