/** * */ package org.ihtsdo.otf.refset.graph.gao; import static org.ihtsdo.otf.refset.domain.RGC.*; import java.util.ArrayList; 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.EntityAlreadyExistException; 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.GMember; import org.ihtsdo.otf.refset.graph.schema.GRefset; import org.ihtsdo.otf.refset.service.upload.Rf2Record; import org.joda.time.DateTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import com.thinkaurelius.titan.core.TitanGraph; import com.tinkerpop.blueprints.Direction; import com.tinkerpop.blueprints.Edge; import com.tinkerpop.blueprints.Vertex; import com.tinkerpop.blueprints.util.wrappers.event.EventGraph; import com.tinkerpop.frames.FramedGraphFactory; import com.tinkerpop.frames.FramedTransactionalGraph; /**Graph Access component to do CRUD operation on underlying Refset graph * Operation in this class supports * 1. New Refset Creation * 2. Deletion of a refset * 3. Update an existing refset * */ @Repository public class RefsetAdminGAO { private static final Logger LOGGER = LoggerFactory.getLogger(RefsetAdminGAO.class); private RefsetGraphFactory factory; private MemberGAO mGao; private RefsetGAO rGao; private static FramedGraphFactory fgf = new FramedGraphFactory(); /** * @param r a {@link Refset} with or without members * @throws RefsetGraphAccessException * @throws EntityAlreadyExistException * @throws EntityNotFoundException */ public MetaData addRefset(Refset r) throws RefsetGraphAccessException, EntityAlreadyExistException { LOGGER.debug("Adding refset {}", r); TitanGraph g = null; MetaData md = r.getMetaData(); EventGraph<TitanGraph> tg = null; try { tg = factory.getEventGraph(); if (rGao.isSctIdExist(r.getSctId(), tg.getBaseGraph())) { throw new EntityAlreadyExistException("Refset with same sctid already exist"); } final Vertex rV = addRefsetNode(r, fgf.create(tg.getBaseGraph())); /*if members exist then add members*/ List<Member> members = r.getMembers(); /*populate descriptions*/ List<String> rcIds = new ArrayList<String>(); for (Member member : members) { rcIds.add(member.getReferencedComponentId()); } Map<String, String> descriptions = rGao.getMembersDescription(rcIds); int i = 0; if( !CollectionUtils.isEmpty(members) ) { for (Member m : members) { if(descriptions.containsKey(m.getReferencedComponentId())) { m.setDescription(descriptions.get(m.getReferencedComponentId())); } Vertex mV = mGao.addMemberNode(m, tg, r.getUuid()); LOGGER.debug("Adding relationship member is part of refset as edge {}, member index {}", mV.getId(), i++); /*Add this member to refset*/ Edge e = tg.addEdge(null, mV, rV, "members"); e.setProperty(REFERENCE_COMPONENT_ID, m.getReferencedComponentId()); long start = r.getEffectiveTime() != null ? r.getEffectiveTime().getMillis() : new DateTime().getMillis(); e.setProperty(START, start); e.setProperty(END, Long.MAX_VALUE); LOGGER.debug("Added relationship as edge from {} to {}", mV.getId(), rV.getId()); } } else { LOGGER.debug("No member available for this refset to add"); } LOGGER.debug("Commiting"); md = RefsetConvertor.getMetaData(rV); RefsetGraphFactory.commit(tg); } catch (EntityAlreadyExistException e) { RefsetGraphFactory.rollback(tg); LOGGER.error("Error during graph interaction", e); throw e; } catch (Exception e) { RefsetGraphFactory.rollback(tg); LOGGER.error("Error during graph interaction", e); throw new RefsetGraphAccessException(e.getMessage(), e); } finally { RefsetGraphFactory.shutdown(g); } return md; } /** * @param r {@link Refset} * @param tg {@link FramedTransactionalGraph} * @return id of {@link Refset} node * @throws RefsetGraphAccessException * @throws EntityNotFoundException */ private Vertex addRefsetNode(Refset r, FramedTransactionalGraph<TitanGraph> tg) throws RefsetGraphAccessException { LOGGER.debug("Adding refset node {}", r); Vertex rV; try { rV = rGao.getRefsetVertex(r.getUuid(), tg); LOGGER.debug("Refset {} already exist, not adding"); } catch (EntityNotFoundException e) { LOGGER.debug("Refset does not exist, adding {}", r.toString()); TitanGraph g = tg.getBaseGraph(); Vertex vR = g.addVertexWithLabel(g.getVertexLabel("GRefset")); GRefset gr = tg.getVertex(vR.getId(), GRefset.class); if (r.getCreated() == null) { r.setCreated(new DateTime()); } gr.setCreated(r.getCreated().getMillis()); gr.setCreatedBy(r.getCreatedBy()); gr.setDescription(r.getDescription()); if (r.getEffectiveTime() != null) { gr.setEffectiveTime(r.getEffectiveTime().getMillis()); } gr.setId(r.getUuid()); gr.setLanguageCode(r.getLanguageCode()); gr.setModuleId(r.getModuleId()); Integer publishedFlag = r.isPublished() ? 1 : 0; gr.setPublished(publishedFlag); if (r.getPublishedDate() != null) { gr.setPublishedDate(r.getPublishedDate().getMillis()); } gr.setSuperRefsetTypeId(r.getSuperRefsetTypeId()); gr.setComponentTypeId(r.getComponentTypeId()); gr.setTypeId(r.getTypeId()); Integer activeFlag = r.isActive() ? 1 : 0; gr.setActive(activeFlag); gr.setModifiedBy(r.getModifiedBy()); gr.setModifiedDate(new DateTime().getMillis()); DateTime ert = r.getExpectedReleaseDate(); if( ert != null) { gr.setExpectedPublishDate(ert.getMillis()); } if (!StringUtils.isEmpty(r.getSctId())) { gr.setSctId(r.getSctId()); } gr.setType(VertexType.refset.toString()); LOGGER.debug("Added Refset as vertex to graph {}", gr.getId()); rV = gr.asVertex(); } LOGGER.debug("Refset vertex is {} ", rV); return rV; } /** Removes a {@link Refset} if it is not yet published * or update as inactive in graph * @param user * @param r {@link Refset} * @throws RefsetGraphAccessException * @throws EntityNotFoundException */ public void removeRefset(String refsetId, String user) throws RefsetGraphAccessException, EntityNotFoundException { LOGGER.debug("removeRefset {} ", refsetId); if (StringUtils.isEmpty(refsetId)) { throw new EntityNotFoundException(); } EventGraph<TitanGraph> g = null; try { g = factory.getEventGraph(); g.addListener(new RefsetHeaderChangeListener(g.getBaseGraph(), user)); Vertex refset = rGao.getRefsetVertex(refsetId, fgf.create(g.getBaseGraph())); // removing this check as delete is needed for any state /**Integer published = refset.getProperty(PUBLISHED); if (published == 1) { LOGGER.debug("Not removing only making it inactive {} ", refsetId); refset.setProperty(ACTIVE, 0); } else { g.removeVertex(refset); }*/ g.removeVertex(refset); RefsetGraphFactory.commit(g); } catch(EntityNotFoundException e) { LOGGER.error("Error during graph interaction", e); RefsetGraphFactory.rollback(g); throw e; } catch (Exception e) { RefsetGraphFactory.rollback(g); LOGGER.error("Error during graph interaction", e); throw new RefsetGraphAccessException(e.getMessage(), e); } finally { RefsetGraphFactory.shutdown(g); } } /** * @param r a {@link Refset} with or without members * @throws RefsetGraphAccessException * @throws EntityNotFoundException */ public MetaData updateRefset(Refset r) throws RefsetGraphAccessException, EntityNotFoundException { LOGGER.debug("Updating refset {}", r); EventGraph<TitanGraph> g = null; MetaData md = r.getMetaData(); try { g = factory.getEventGraph(); final Vertex rV = updateRefsetNode(r, g); LOGGER.debug("Updating members"); /*if members exist then update members*/ List<Member> members = r.getMembers(); if( !CollectionUtils.isEmpty(members) ) { for (Member m : members) { LOGGER.debug("Updating member {}", m); mGao.updateMemberNode(m, g); } } else { LOGGER.debug("No member available for this refset to update"); } md = RefsetConvertor.getMetaData(rV); LOGGER.debug("Commiting updated refset"); RefsetGraphFactory.commit(g); } catch(EntityNotFoundException e) { LOGGER.error("Error during graph interaction", e); RefsetGraphFactory.rollback(g); throw e; } catch (Exception e) { RefsetGraphFactory.rollback(g); LOGGER.error("Error during graph interaction", e); throw new RefsetGraphAccessException(e.getMessage(), e); } finally { RefsetGraphFactory.shutdown(g); } return md; } /**Update an existing {@link Refset} node. But does not commit yet * @param r {@link Refset} * @param tg {@link TitanGraph} * @return id of {@link Refset} node * @throws RefsetGraphAccessException * @throws EntityNotFoundException */ private Vertex updateRefsetNode(Refset r, EventGraph<TitanGraph> g) throws RefsetGraphAccessException, EntityNotFoundException { LOGGER.debug("updateRefsetNode {}", r); Object rVId = rGao.getRefsetVertex(r.getUuid(), fgf.create(g.getBaseGraph())); g.addListener(new RefsetHeaderChangeListener(g.getBaseGraph(), r.getModifiedBy())); Vertex rV = g.getVertex(rVId);//, GRefset.class); if(rV == null) { throw new EntityNotFoundException("Can not find given refset to update"); } //no update is allowed in inactive refset. Only allowed if refset is being made active simultaneously if (Integer.valueOf(0).equals(rV.getProperty(ACTIVE)) && !r.isActive()) { throw new EntityNotFoundException("No update is allowed for inactive refset. Re-activate and try again"); } String desc = r.getDescription(); if (!StringUtils.isEmpty(desc)) { //rV.setDescription(r.getDescription()); rV.setProperty(DESC, desc); } if (r.getEffectiveTime() != null) { //rV.setEffectiveTime(r.getEffectiveTime().getMillis()); rV.setProperty(EFFECTIVE_DATE, r.getEffectiveTime().getMillis()); } String lang = r.getLanguageCode(); if (!StringUtils.isEmpty(lang)) { //rV.setLanguageCode(lang); rV.setProperty(LANG_CODE, lang); } String moduleId = r.getModuleId(); if (!StringUtils.isEmpty(moduleId)) { //rV.setModuleId(moduleId); rV.setProperty(MODULE_ID, moduleId); } Integer publishedFlag = r.isPublished() ? 1 : 0; //rV.setPublished(publishedFlag); rV.setProperty(PUBLISHED, publishedFlag); if (r.getPublishedDate() != null) { // rV.setPublishedDate(r.getPublishedDate().getMillis()); rV.setProperty(PUBLISHED_DATE, r.getPublishedDate().getMillis()); } String superRefsetTypeId = r.getSuperRefsetTypeId(); if (!StringUtils.isEmpty(superRefsetTypeId)) { ///rV.setSuperRefsetTypeId(superRefsetTypeId); rV.setProperty(SUPER_REFSET_TYPE_ID, r.getSuperRefsetTypeId()); } String compTypeId = r.getComponentTypeId(); if (!StringUtils.isEmpty(compTypeId)) { //rV.setComponentTypeId(compTypeId); rV.setProperty(MEMBER_TYPE_ID, compTypeId); } Integer activeFlag = r.isActive() ? 1 : 0; //rV.setActive(activeFlag); rV.setProperty(ACTIVE, activeFlag); String typeId = r.getTypeId(); if (!StringUtils.isEmpty(typeId)) { //rV.setTypeId(typeId); rV.setProperty(TYPE_ID, typeId); } //rV.setModifiedBy(r.getModifiedBy()); //rV.setModifiedDate(new DateTime().getMillis()); rV.setProperty(MODIFIED_BY, r.getModifiedBy()); rV.setProperty(MODIFIED_DATE, new DateTime().getMillis()); DateTime ert = r.getExpectedReleaseDate(); if (ert != null) { //rV.setExpectedReleaseDate(ert.getMillis()); rV.setProperty(EXPECTED_PUBLISH_DATE, ert.getMillis()); } if (!StringUtils.isEmpty(r.getSctId())) { //rV.setSctdId(r.getSctId()); rV.setProperty(SCTID, r.getSctId()); } LOGGER.debug("updateRefsetNode {} finished", rV); return rV;//.asVertex(); } /**Adds member and refset and their history.Used when importing a RF2 file * @param rf2rLst * @param refsetId * @return * @throws EntityNotFoundException * @throws RefsetGraphAccessException */ public Map<String, String> addMembers(List<Rf2Record> rf2rLst, String refsetId, String user) throws EntityNotFoundException, RefsetGraphAccessException { Map<String, String> outcome = new HashMap<String, String>(); EventGraph<TitanGraph> g = factory.getEventGraph(); g.addListener(new Rf2ImportMemberChangeListener(g.getBaseGraph(), user)); g.addListener(new EffectiveTimeChangeListener(g.getBaseGraph(), user)); try { Vertex rV = rGao.getRefsetVertex(refsetId, fgf.create(g.getBaseGraph())); Map<String, String> descriptions = populateDescription(rf2rLst); Map<String, Vertex> processed = new HashMap<String, Vertex>(); for (Rf2Record r : rf2rLst) { GRefset gr = fgf.create(g).frame(rV, GRefset.class); if ( StringUtils.isEmpty(r.getRefsetId()) || !(r.getRefsetId().equals(gr.getId()) || r.getRefsetId().equals(gr.getSctId())) || r.getEffectiveTime() == null) { String error = String.format("Member does not have valid refset id - %s", r.getRefsetId()); outcome.put(r.getReferencedComponentId(), error); continue; } if (StringUtils.isEmpty(descriptions.containsKey(r.getReferencedComponentId()))) { outcome.put(r.getReferencedComponentId(), "Unknown referenced component"); continue; } if(!processed.containsKey(r.getReferencedComponentId())) { //add this member Vertex vM = g.getBaseGraph().addVertexWithLabel(g.getBaseGraph().getVertexLabel("GMember")); GMember mg = fgf.create(g).getVertex(vM.getId(), GMember.class); addMemberProperties(r, mg, VertexType.member); LOGGER.debug("Added Member as vertex to graph", mg.getId()); Edge e = fgf.create(g).addEdge(null, mg.asVertex(), rV, "members"); e.setProperty(REFERENCE_COMPONENT_ID, r.getReferencedComponentId()); e.setProperty(START, r.getEffectiveTime().getMillis()); e.setProperty(END, Long.MAX_VALUE); processed.put(r.getReferencedComponentId(), vM); } else { //do a check if an existing member exist with some other state and lesser effective time Vertex vExistingMember = processed.get(r.getReferencedComponentId()); Member m = RefsetConvertor.getMember(vExistingMember); if (m.getEffectiveTime().getMillis() == r.getEffectiveTime().getMillis() && (m.isActive() ? "1" : "0").equals(r.getActive())) { LOGGER.trace("Not adding this record as it already exist {}", r.getId()); //same record so do not re-import outcome.put(r.getReferencedComponentId(), "Already exist, not imported again"); break; } else if (m.getEffectiveTime().getMillis() < r.getEffectiveTime().getMillis()) { //update a existing member vertex with start date GMember mg = fgf.create(g).getVertex(vExistingMember.getId(), GMember.class); addMemberProperties(r, mg, VertexType.member); LOGGER.trace("Updated Member vertex with new properties and new state {}", mg.getId()); Iterable<Edge> eMs = vExistingMember.getEdges(Direction.OUT, EdgeLabel.members.toString()); for (Edge edge : eMs) { edge.setProperty(REFERENCE_COMPONENT_ID, r.getReferencedComponentId()); edge.setProperty(START, r.getEffectiveTime().getMillis()); edge.setProperty(END, Long.MAX_VALUE); } } else if (m.getEffectiveTime().getMillis() > r.getEffectiveTime().getMillis()) { //add a new member vertex with Long.MAX_VALUE end date Vertex vM = g.getBaseGraph().addVertexWithLabel(g.getBaseGraph().getVertexLabel("GMember")); //GMember mg = fgf.create(g).getVertex(vM.getId(), GMember.class); GMember mg = fgf.create(g).getVertex(vM.getId(), GMember.class); addMemberProperties(r, mg, VertexType.hMember); LOGGER.trace("Added Member as vertex to graph with new state {}", mg.getId()); //Edge e = fgf.create(g).addEdge(null, mg.asVertex(), rV, EdgeLabel.hasState.toString()); Edge e = vExistingMember.addEdge(EdgeLabel.hasState.toString(), vM); e.setProperty(REFERENCE_COMPONENT_ID, r.getReferencedComponentId()); e.setProperty(START, r.getEffectiveTime().getMillis()); e.setProperty(END, r.getEffectiveTime().getMillis()); } } } outcome.put("All members", "Success"); RefsetGraphFactory.commit(g); } catch(EntityNotFoundException e) { throw e; } catch (Exception e) { RefsetGraphFactory.rollback(g); LOGGER.error("Error during graph interaction", e); throw new RefsetGraphAccessException(e.getMessage(), e); } finally { RefsetGraphFactory.shutdown(g); } return outcome; } /** * @param rf2rLst * @return * @throws RefsetGraphAccessException */ private Map<String, String> populateDescription(List<Rf2Record> rf2rLst) throws RefsetGraphAccessException { Map<String, String> descriptions = new HashMap<String, String>(); if (rf2rLst == null || rf2rLst.isEmpty()) { return descriptions; } List<String> rcIds = new ArrayList<String>(); for (Rf2Record record : rf2rLst) { rcIds.add(record.getReferencedComponentId()); } descriptions = rGao.getMembersDescription(rcIds); return descriptions; } private GMember addMemberProperties(Rf2Record r, GMember mg, VertexType type) { Integer activeFlag = "1".equals(r.getActive()) ? 1 : 0; mg.setActive(activeFlag); mg.setId(r.getId()); DateTime et = r.getEffectiveTime(); mg.setEffectiveTime(et.getMillis()); mg.setModifiedBy(r.getModifiedBy()); mg.setCreateBy(r.getCreatedBy()); mg.setCreated(new DateTime().getMillis()); mg.setModifiedDate(new DateTime().getMillis()); mg.setModuleId(r.getModuleId()); //everything in RF2 is published already hence make it published mg.setPublished(1); mg.setType(type.toString()); return mg; } /** * @param factory the factory to set */ @Resource(name = "refsetGraphFactory") public void setFactory(RefsetGraphFactory factory) { this.factory = factory; } /** * @param mGao the mGao to set */ @Autowired public void setMemberGao(MemberGAO mGao) { this.mGao = mGao; } /** * @param rGao the rGao to set */ @Autowired public void setRefsetGao(RefsetGAO rGao) { this.rGao = rGao; } }