/** * */ package org.ihtsdo.otf.refset.graph.gao; import static org.ihtsdo.otf.refset.domain.RGC.DESC; import static org.ihtsdo.otf.refset.domain.RGC.END; import static org.ihtsdo.otf.refset.domain.RGC.ID; import static org.ihtsdo.otf.refset.domain.RGC.PUBLISHED; import static org.ihtsdo.otf.refset.domain.RGC.TYPE; import static org.ihtsdo.otf.refset.domain.RGC.SCTID; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.annotation.Resource; import org.ihtsdo.otf.refset.domain.Member; import org.ihtsdo.otf.refset.domain.MetaData; import org.ihtsdo.otf.refset.domain.Refset; 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.schema.GRefset; import org.ihtsdo.otf.snomed.domain.Properties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Repository; import org.springframework.util.StringUtils; import com.google.common.collect.Iterables; import com.thinkaurelius.titan.core.TitanGraph; import com.thinkaurelius.titan.core.TitanIndexQuery.Result; import com.tinkerpop.blueprints.Edge; import com.tinkerpop.blueprints.Vertex; import com.tinkerpop.frames.FramedGraph; import com.tinkerpop.frames.FramedGraphFactory; import com.tinkerpop.frames.FramedTransactionalGraph; import com.tinkerpop.gremlin.java.GremlinPipeline; /**Refset Graph Access component to retrieve {@link Refset}s and its {@link Member} * */ @Repository public class RefsetGAO { private static final Logger LOGGER = LoggerFactory.getLogger(RefsetGAO.class); private RefsetGraphFactory rgFactory;//refset graph factory private RefsetGraphFactory sgFactory;//snomed graph factory private static FramedGraphFactory fgf = new FramedGraphFactory(); /**Retrieves a {@link Refset} vertex for given {@link Refset#getId()} * @param id * @param tg * @return * @throws RefsetGraphAccessException * @throws EntityNotFoundException */ protected Vertex getRefsetVertex(String id, FramedTransactionalGraph<TitanGraph> tg) throws RefsetGraphAccessException, EntityNotFoundException { LOGGER.debug("getRefsetVertex for given refset id {}", id); if (StringUtils.isEmpty(id)) { throw new EntityNotFoundException(); } Vertex rV = null; try { Iterable<Vertex> vr = tg.query().has(TYPE, VertexType.refset.toString()).has(ID, id).limit(1).vertices(); if (vr != null ) { for (Vertex v : vr) { LOGGER.debug("Refset is {} for refset id {}", v, id); rV = v; break; } } } catch (Exception e) { LOGGER.error("Error refset lookup for refset id {}", id, e); throw new RefsetGraphAccessException(e.getMessage(), e); } if(rV == null) throw new EntityNotFoundException("Refset does not exist for given refset id"); return rV; } /**Retrieves a {@link Refset} for a given refsetId * @param id * @return {@link Refset} * @throws RefsetGraphAccessException */ public Refset getRefset(String id) throws RefsetGraphAccessException, EntityNotFoundException { TitanGraph g = null; Refset r = null; try { g = rgFactory.getReadOnlyGraph(); FramedGraph<TitanGraph> tg = fgf.create(g); //TODO upgrade this search with status and effective date Iterable<GRefset> vs = tg.query().has(ID, id).has(TYPE, VertexType.refset.toString()).vertices(GRefset.class);//.has(ID, Compare.EQUAL, id).limit(1).vertices(GRefset.class); for (GRefset v : vs) { r = RefsetConvertor.convert2Refset(v); LOGGER.debug("Refset is {} ", r); r.setMetaData(RefsetConvertor.getMetaData(v.asVertex())); break; } RefsetGraphFactory.commit(tg); if(r == null) throw new EntityNotFoundException("No Refset found for given id "); List<Member> ms = r.getMembers(); populateMemberDescription(ms); } catch(EntityNotFoundException e) { RefsetGraphFactory.rollback(g); LOGGER.error("entity not found for given refset id {}", id, e); throw e; } catch (Exception e) { RefsetGraphFactory.rollback(g); LOGGER.error("Error getting refset for", id, e); throw new RefsetGraphAccessException(e.getMessage(), e); } finally { RefsetGraphFactory.shutdown(g); } return r; } /** * @param ms * @throws RefsetGraphAccessException */ private void populateMemberDescription(List<Member> ms) throws RefsetGraphAccessException { if (ms == null || ms.isEmpty()) { return; } List<String> rcIds = new ArrayList<String>(); for (Member member : ms) { rcIds.add(member.getReferencedComponentId()); } Map<String, String> descriptions = getMembersDescription(rcIds); for (Member m : ms) { if(descriptions.containsKey(m.getReferencedComponentId())) { m.setDescription(descriptions.get(m.getReferencedComponentId())); } } } /** * @param published * @return * @throws RefsetGraphAccessException */ public List<Refset> getRefSets(boolean published) throws RefsetGraphAccessException { TitanGraph g = null; List<Refset> refsets = new ArrayList<Refset>(); try { g = rgFactory.getReadOnlyGraph(); FramedGraph<TitanGraph> fg = fgf.create(g); //TODO upgrade this search with status and effective date Iterable<GRefset> vs; if (published) { vs = fg.getVertices(PUBLISHED, 1, GRefset.class); } else { Iterable<GRefset> publishedRefset = fg.getVertices(PUBLISHED, 1, GRefset.class); //fg.query().has(PUBLISHED, Compare.EQUAL, true).limit(20).vertices(GRefset.class);//fg.getVertices(PUBLISHED, true, GRefset.class);//g.getRelationType(name); (GRefset.class, new Object[]{ published }); Iterable<GRefset> unPublishedRefset = fg.getVertices(PUBLISHED, 0, GRefset.class); //fg.query().has(PUBLISHED, Compare.EQUAL, false).limit(20).vertices(GRefset.class);// fg.getVertices(PUBLISHED, false, GRefset.class);//g.getRelationType(name); (GRefset.class, new Object[]{ published }); vs = Iterables.concat(publishedRefset, unPublishedRefset); } refsets = RefsetConvertor.getRefsets(vs); RefsetGraphFactory.commit(g); } catch (Exception e) { RefsetGraphFactory.rollback(g); LOGGER.error("Error during graph interaction", e); throw new RefsetGraphAccessException(e.getMessage(), e); } finally { RefsetGraphFactory.shutdown(g); } return refsets; } /** Utility method to retrieve meta information of a graph object * for a given record id * @param rId * @return * @throws RefsetGraphAccessException * @throws EntityNotFoundException */ public MetaData getMetaData(Object rId) throws RefsetGraphAccessException, EntityNotFoundException { TitanGraph g = null; MetaData md = null; try { g = rgFactory.getReadOnlyGraph(); Vertex e = g.getVertex(rId); if( e != null) { md = new MetaData(); md.setId(e.getId()); md.setType(e.getClass().getSimpleName()); //Integer version = e.getProperty("@Version"); //md.setVersion(version); } RefsetGraphFactory.commit(g); } catch (Exception e) { RefsetGraphFactory.rollback(g); LOGGER.error("Error during graph interaction ", e); throw new RefsetGraphAccessException(e.getMessage(), e); } finally { RefsetGraphFactory.shutdown(g); } if( md == null ) throw new EntityNotFoundException(String.format("No record available for given record %s", rId)); return md; } /** * @param referenceComponentId * @return * @throws RefsetGraphAccessException */ protected 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 = sgFactory.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; } /**validates a given description if it exist in the system * @return if given description exist then true otherwise false * @throws RefsetGraphAccessException */ public boolean isDescriptionExist(String description) throws RefsetGraphAccessException { TitanGraph g = null; boolean isExist = false; try { g = rgFactory.getReadOnlyGraph(); //TODO upgrade this search with status and effective date Iterable<Vertex> vs = g.query().has(DESC, description).has(TYPE, VertexType.refset.toString()).limit(1).vertices(); if (vs != null && vs.iterator() != null && vs.iterator().hasNext()) { isExist = true; } RefsetGraphFactory.commit(g); } catch (Exception e) { RefsetGraphFactory.rollback(g); LOGGER.error("Error checking refset description for {}", description, e); throw new RefsetGraphAccessException(e.getMessage(), e); } finally { RefsetGraphFactory.shutdown(g); } return isExist; } /** * @param factory the factory to set */ @Resource(name = "refsetGraphFactory") public void setRGFactory(RefsetGraphFactory factory) { this.rgFactory = factory; } /** * @param factory the factory to set */ @Resource(name = "snomedGraphFactory") public void setSGFactory(RefsetGraphFactory factory) { this.sgFactory = factory; } /** * @param refsetId * @param page * @param size * @return * @throws RefsetGraphAccessException * @throws EntityNotFoundException */ public Refset getRefset(String refsetId, Integer from, Integer to) throws RefsetGraphAccessException, EntityNotFoundException { LOGGER.debug("Getting refset members from {}, to {}", from, to); TitanGraph g = null; Refset r = null; try { g = rgFactory.getReadOnlyGraph(); FramedGraph<TitanGraph> tg = fgf.create(g); Iterable<GRefset> vRs = tg.query().has(ID, refsetId).has(TYPE, VertexType.refset.toString()).limit(1).vertices(GRefset.class); if (!vRs.iterator().hasNext()) { throw new EntityNotFoundException("Refset does not exist for given refset id " + refsetId); } GRefset vR = vRs.iterator().next(); r = RefsetConvertor.getRefset(vR); //get required members as per range GremlinPipeline<Vertex, Edge> pipe = new GremlinPipeline<Vertex, Edge>(); pipe.start(vR.asVertex()).inE(EdgeLabel.members.toString()).has(END, Long.MAX_VALUE).range(from, to); List<Edge> ls = pipe.toList(); if (r != null) { r.setMembers(RefsetConvertor.getMembers(ls)); } else { throw new EntityNotFoundException("Refset does not exist for given refset id " + refsetId); } RefsetGraphFactory.commit(g); //populate descriptions populateMemberDescription(r.getMembers()); LOGGER.debug("Returning refset {} ", r); } catch (Exception e) { RefsetGraphFactory.rollback(g); LOGGER.error("Error getting refset for {}", refsetId, e); throw new RefsetGraphAccessException(e.getMessage(), e); } finally { RefsetGraphFactory.shutdown(g); } return r; } /** * @param refSetId * @return * @throws RefsetGraphAccessException */ public Refset getRefsetHeader(String refSetId) throws RefsetGraphAccessException { LOGGER.debug("getRefsetHeader for {}", refSetId); TitanGraph g = null; Refset r = null; try { g = rgFactory.getReadOnlyGraph(); FramedGraph<TitanGraph> tg = fgf.create(g); Iterable<GRefset> vRs = tg.query().has(ID, refSetId).has(TYPE, VertexType.refset.toString()).limit(1).vertices(GRefset.class); if (!vRs.iterator().hasNext()) { throw new EntityNotFoundException("Refset does not exist for given refset id " + refSetId); } GRefset vR = vRs.iterator().next(); GremlinPipeline<Vertex, Long> pipe = new GremlinPipeline<Vertex, Long>(); long totalNoOfMembers = pipe.start(vR.asVertex()).inE(EdgeLabel.members.toString()).has(END, Long.MAX_VALUE).count(); LOGGER.debug("total members {}", totalNoOfMembers); r = RefsetConvertor.getRefset(vR); r.setTotalNoOfMembers(totalNoOfMembers); LOGGER.debug("Returning refset {} ", r); RefsetGraphFactory.commit(g); } catch (Exception e) { RefsetGraphFactory.rollback(g); LOGGER.error("Error getting refset for {}", refSetId, e); throw new RefsetGraphAccessException(e.getMessage(), e); } finally { RefsetGraphFactory.shutdown(g); } return r; } public List<Refset> getRefSets(boolean published, int from, int to) throws RefsetGraphAccessException { TitanGraph g = null; List<Refset> refsets = Collections.emptyList(); try { g = rgFactory.getReadOnlyGraph(); GremlinPipeline<Vertex, Vertex> pipe = new GremlinPipeline<Vertex, Vertex>(g); if (published) { pipe.V().has(PUBLISHED, 1).has(TYPE, VertexType.refset.toString()).range(from, to); } else { pipe.V(TYPE, VertexType.refset.toString()).range(from, to); } List<Vertex> vs = pipe.toList(); FramedGraph<TitanGraph> fg = fgf.create(g); List<GRefset> ls = new ArrayList<GRefset>(); for (Vertex rV : vs) { GRefset gR = fg.getVertex(rV.getId(), GRefset.class); GremlinPipeline<Vertex, Long> mPipe = new GremlinPipeline<Vertex, Long>(); long noOfMembers = mPipe.start(rV).inE(EdgeLabel.members.toString()).has(END, Long.MAX_VALUE).count(); gR.setNoOfMembers(noOfMembers); ls.add(gR); } refsets = RefsetConvertor.getRefsets(ls); } catch (Exception e) { RefsetGraphFactory.rollback(g); LOGGER.error("Error getting refsets for status {}", published, e); throw new RefsetGraphAccessException(e.getMessage(), e); } finally { RefsetGraphFactory.shutdown(g); } LOGGER.debug("Returning {} refsets ", (refsets != null ? refsets.size() : 0)); return refsets; } /** Returns {@link Map} of referenceComponentId as key and their description as value * @param referenceComponentId * @return * @throws RefsetGraphAccessException */ protected 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 = sgFactory.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; } /**Retrieves a {@link Refset} vertex for given {@link Refset#getSctId()} * @param sctId - SCTID of a refset * @param tg {@link TitanGraph} * @return boolean false if this sctId does not exist otherwise true * @throws RefsetGraphAccessException * @throws EntityNotFoundException */ protected boolean isSctIdExist(String sctId, TitanGraph tg) throws RefsetGraphAccessException, EntityNotFoundException { LOGGER.debug("isSctIdExist for given refset sct id {}", sctId); boolean isSctIdExist = false; if (StringUtils.isEmpty(sctId)) { return isSctIdExist; } try { Iterable<Vertex> vRs = tg.query().has(TYPE, VertexType.refset.toString()).has(SCTID, sctId).limit(1).vertices(); if ( vRs.iterator().hasNext() ) { isSctIdExist = true; LOGGER.debug("Refset exist for given sctid {}", sctId); } } catch (Exception e) { LOGGER.error("Error refset lookup for refset sctid {}", sctId, e); throw new RefsetGraphAccessException(e.getMessage(), e); } return isSctIdExist; } }