/** * Implements operations for managing the document table of the aidr_predict DB * * @author Koushik */ package qa.qcri.aidr.dbmanager.ejb.remote.facade.imp; import java.util.ArrayList; import java.util.List; import javax.ejb.EJB; import javax.ejb.Stateless; import org.apache.log4j.Logger; import org.hibernate.Criteria; import org.hibernate.Hibernate; import org.hibernate.HibernateException; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.criterion.Criterion; import org.hibernate.criterion.Projection; import org.hibernate.criterion.Projections; import org.hibernate.criterion.Restrictions; import org.hibernate.sql.JoinType; import qa.qcri.aidr.common.exception.PropertyNotSetException; import qa.qcri.aidr.dbmanager.dto.CollectionDTO; import qa.qcri.aidr.dbmanager.dto.DocumentDTO; import qa.qcri.aidr.dbmanager.dto.NominalLabelDTO; import qa.qcri.aidr.dbmanager.ejb.local.facade.impl.CoreDBServiceFacadeImp; import qa.qcri.aidr.dbmanager.ejb.remote.facade.CollectionResourceFacade; import qa.qcri.aidr.dbmanager.ejb.remote.facade.DocumentResourceFacade; import qa.qcri.aidr.dbmanager.ejb.remote.facade.NominalLabelResourceFacade; import qa.qcri.aidr.dbmanager.entities.task.Document; @Stateless(name="DocumentResourceFacadeImp") public class DocumentResourceFacadeImp extends CoreDBServiceFacadeImp<Document, Long> implements DocumentResourceFacade { private Logger logger = Logger.getLogger("db-manager-log"); @EJB CollectionResourceFacade crisisEJB; @EJB NominalLabelResourceFacade nominalLabelEJB; public DocumentResourceFacadeImp() { super(Document.class); } @Override public void updateHasHumanLabel(DocumentDTO document) { Document doc = (Document) getByCriteria(Restrictions.eq("documentId", document.getDocumentID())); if (doc != null) { doc.setHasHumanLabels(true); update(doc); } } @Override public List<DocumentDTO> getDocumentCollectionForNominalLabel(Criterion criterion) throws PropertyNotSetException { String aliasTable = "documentNominalLabels"; String aliasTableKey = "documentNominalLabels.id"; String[] orderBy = {"documentId"}; List<Document> fetchedList = getByCriteriaWithInnerJoinByOrder(criterion, "ASC", orderBy, null, aliasTable, Restrictions.isNotEmpty(aliasTableKey)); if (fetchedList != null && !fetchedList.isEmpty()) { List<DocumentDTO> dtoList = new ArrayList<DocumentDTO>(); for (Document doc: fetchedList) { dtoList.add(new DocumentDTO(doc)); } return dtoList; } return null; } @Override public int deleteNoLabelDocument(DocumentDTO document) { if (null == document) { return 0; } logger.info("Received request for : " + document.getDocumentID()); int deleteCount = 0; if (!document.getHasHumanLabels()) { try { //delete(document); String hql = "DELETE FROM document WHERE document.documentID = :documentID AND !document.hasHumanLabels"; Session session = getCurrentSession(); Query collectionDeleteQuery = session.createSQLQuery(hql); try { collectionDeleteQuery.setParameter("documentID", document.getDocumentID()); deleteCount = collectionDeleteQuery.executeUpdate(); logger.info("deleted count = " + deleteCount); } catch (Exception e) { logger.error("deletion query failed, document: " + document.getDocumentID()); return 0; } logger.info("deletion success, deleted count = " + deleteCount); session.flush(); return 1; } catch (Exception e) { logger.error("Deletion query failed"); return 0; } } logger.info("Document has label. Not deleting."); return 0; } @Override public int deleteNoLabelDocument(List<DocumentDTO> collection) { int deleteCount = 0; if (collection != null && !collection.isEmpty()) { //Session session = getCurrentSession(); try { //Transaction tx = session.beginTransaction(); for (DocumentDTO d: collection) { deleteCount += deleteNoLabelDocument(d); } //tx.commit(); logger.info("deleted count = " + deleteCount); } catch (Exception e) { logger.error("Collection deletion query failed"); } } return deleteCount; } @Override public int deleteUnassignedDocument(DocumentDTO document) { String hql = "DELETE d FROM aidr_predict.document d LEFT JOIN aidr_predict.task_assignment t " + "ON d.documentID = t.documentID WHERE (d.documentID = :documentID " + "AND t.documentID IS NULL AND !d.hasHumanLabels)"; if (!document.getHasHumanLabels()) { try { Query query = getCurrentSession().createSQLQuery(hql); query.setParameter("documentID", document.getDocumentID()); int result = query.executeUpdate(); return result; } catch (Exception e) { logger.error("Deletion query failed"); return 0; } } return 0; } @Override public int deleteUnassignedDocument(Long documentID) { String hql = "DELETE d FROM aidr_predict.document d LEFT JOIN aidr_predict.task_assignment t " + "ON d.documentID = t.documentID WHERE (d.documentID = :documentID " + "AND t.documentID IS NULL AND !d.hasHumanLabels)"; try { Query query = getCurrentSession().createSQLQuery(hql); query.setParameter("documentID", documentID); int result = query.executeUpdate(); return result; } catch (Exception e) { logger.error("Deletion query failed: " + e); return 0; } } @Override public int deleteUnassignedDocumentCollection(List<Long> documentIDList) { int deleteCount = 0; if (documentIDList != null && !documentIDList.isEmpty()) { logger.info("[deleteUnassignedDocumentCollection] Size of docList to delete: " + documentIDList.size()); Session session = getCurrentSession(); try { Transaction tx = session.beginTransaction(); for (Long documentID: documentIDList) { deleteCount += deleteUnassignedDocument(documentID); } tx.commit(); } catch (Exception e) { logger.error("[deleteUnassignedDocumentCollection] Collection deletion query failed"); logger.error("Exception", e); } } return deleteCount; } /** * Query very specific to deleting stale tasks only */ @Override public int deleteStaleDocuments(String joinType, String joinTable, String joinColumn, String sortOrder, String[] orderBy, final String maxTaskAge, final String scanInterval) { logger.info("received request: " + joinType + ", " + joinTable + ", " + joinColumn + ", " + maxTaskAge + ", " + scanInterval); int deleteCount = 0; Session session = getCurrentSession(); StringBuffer hql = new StringBuffer("DELETE d FROM aidr_predict.document d "); if (joinType.equalsIgnoreCase("LEFT JOIN") || joinType.equalsIgnoreCase("LEFT_JOIN")) { hql.append(" LEFT JOIN ").append(joinTable).append(" t "); hql.append(" ON d.").append(joinColumn).append(" = t.").append(joinColumn) .append(" WHERE ") .append("(!d.hasHumanLabels AND t.documentID IS NULL AND TIMESTAMPDIFF(") .append(getMetric(scanInterval)) .append(", d.receivedAt, now()) > "); } else if (joinType.equalsIgnoreCase("JOIN")) { hql.append(" JOIN ").append(joinTable).append(" t "); hql.append(" ON d.").append(joinColumn).append(" = t.").append(joinColumn) .append(" WHERE ") .append("(!d.hasHumanLabels && TIMESTAMPDIFF(") .append(getMetric(scanInterval)) .append(", t.assignedAt, now()) > "); } hql.append(" :task_expiry_age) "); if (orderBy != null) { hql.append(" ORDER BY "); for (int i = 0; i< orderBy.length - 1; i++) { hql.append(orderBy[i]).append(", "); } hql.append(orderBy[orderBy.length-1]).append(" "); if (sortOrder != null) { hql.append(sortOrder.toUpperCase()).append(" ; "); } } Query deleteQuery = session.createSQLQuery(hql.toString()); deleteQuery.setParameter("task_expiry_age", Integer.parseInt(getTimeValue(maxTaskAge))); logger.info("Constructed query: " + deleteQuery.getQueryString()); try { deleteCount = deleteQuery.executeUpdate(); logger.info("[deleteStaleDocuments] number of deleted records = " + deleteCount); } catch (Exception e) { logger.error("Exception in executing SQL delete stale docs query"); } return deleteCount; } /** * * @param timeString * @return duration in milliseconds. Negative indicates an invalid parse result */ @SuppressWarnings("unused") private long parseTime(final String timeString) { long duration = -1; assert timeString != null; float value = Float.parseFloat(timeString.substring(0, timeString.length()-1)); if (value > 0) { String suffix = timeString.substring(timeString.length() - 1, timeString.length()); if (suffix != null) { if (suffix.equalsIgnoreCase("s")) duration = Math.round(value * 1000); else if (suffix.equalsIgnoreCase("m")) duration = Math.round(value * 60 * 1000) ; else if (suffix.equalsIgnoreCase("h")) duration = Math.round(value * 60 * 60 * 1000); else if (suffix.equalsIgnoreCase("d")) duration = Math.round(value * 60 * 60 * 24 * 1000); else duration = Math.round(value * 60 * 1000); // default is minutes } else duration = Math.round(value * 60 * 1000); // default is minutes } return duration; } private String getTimeValue(final String timeString) { assert timeString != null; return timeString.substring(0, timeString.length()-1); } private String getMetric(final String timeString) { assert timeString != null; String metric = "HOUR"; // default String suffix = timeString.substring(timeString.length() - 1, timeString.length()); if (suffix != null) { if (suffix.equalsIgnoreCase("s")) metric = "SECOND"; else if (suffix.equalsIgnoreCase("m")) metric = "MINUTE"; else if (suffix.equalsIgnoreCase("h")) metric = "HOUR"; else if (suffix.equalsIgnoreCase("d")) metric = "DAY"; } return metric; } @Override public DocumentDTO addDocument(DocumentDTO doc) { try { Document d = doc.toEntity(); em.persist(d); em.flush(); em.refresh(d); return new DocumentDTO(d); } catch (Exception e) { logger.error("Error in addDocument.", e); return null; } } @Override public DocumentDTO editDocument(DocumentDTO doc) throws PropertyNotSetException { try { Document d = doc.toEntity(); Document oldDoc = getById(d.getDocumentId()); if (oldDoc != null) { oldDoc = em.merge(d); return oldDoc != null ? new DocumentDTO(oldDoc) : null; } else { throw new RuntimeException("Not found"); } } catch (Exception e) { logger.error("Exception in merging/updating document: " + doc.getDocumentID(), e); } return null; } @Override public Integer deleteDocument(DocumentDTO doc) { try { Document managed = em.merge(doc.toEntity()); em.remove(managed); } catch (Exception e) { logger.warn("Warning! Couldn't delete document with ID : " + doc.getDocumentID()); return 0; } return 1; } @Override public List<DocumentDTO> findByCriteria(String columnName, Object value) throws PropertyNotSetException { List<Document> list = getAllByCriteria(Restrictions.eq(columnName,value)); List<DocumentDTO> dtoList = new ArrayList<DocumentDTO>(); if (list != null && !list.isEmpty()) { for (Document c: list) { dtoList.add(new DocumentDTO(c)); } } return dtoList; } @Override public DocumentDTO findDocumentByID(Long id) throws PropertyNotSetException { Document doc = getById(id); if (doc != null) { DocumentDTO dto = new DocumentDTO(doc); return dto; } else { return null; } } @Override public List<DocumentDTO> findDocumentsByCrisisID(Long crisisId) throws PropertyNotSetException { List<DocumentDTO> dtoList = findByCriteria("collection.id", crisisId); return dtoList; } @Override public DocumentDTO getDocumentWithAllFieldsByID(Long id) throws PropertyNotSetException { Document doc = getById(id); if (doc != null) { Hibernate.initialize(doc.getCrisis()); Hibernate.initialize(doc.getDocumentNominalLabels()); Hibernate.initialize(doc.getTaskAssignments()); DocumentDTO dto = new DocumentDTO(doc); return dto; } else { return null; } } @Override public boolean isDocumentExists(Long id) throws PropertyNotSetException { DocumentDTO dto = findDocumentByID(id); return dto != null ? true : false; } @Override public List<DocumentDTO> getAllDocuments() throws PropertyNotSetException { List<DocumentDTO> dtoList = new ArrayList<DocumentDTO>(); List<Document> list = getAll(); if (list != null && !list.isEmpty()) { for (Document doc : list) { DocumentDTO dto = new DocumentDTO(doc); dtoList.add(dto); } } logger.error("Done creating DTO list, size = " + dtoList.size()); return dtoList; } @Override public List<DocumentDTO> findLabeledDocumentsByCrisisID(Long crisisId) throws PropertyNotSetException { Criterion criterion = Restrictions.conjunction() .add(Restrictions.eq("collection.id",crisisId)) .add(Restrictions.eq("hasHumanLabels", true)); List<DocumentDTO> dtoList = new ArrayList<DocumentDTO>(); List<Document> list = this.getAllByCriteria(criterion); if (list != null && !list.isEmpty()) { for (Document doc : list) { DocumentDTO dto = new DocumentDTO(doc); dtoList.add(dto); } } logger.info("Done creating DTO list, size = " + dtoList.size()); return dtoList; } @Override public List<DocumentDTO> findUnLabeledDocumentsByCrisisID(Long crisisId) throws PropertyNotSetException { Criterion criterion = Restrictions.conjunction() .add(Restrictions.eq("collection.id",crisisId)) .add(Restrictions.eq("hasHumanLabels", false)); List<DocumentDTO> dtoList = new ArrayList<DocumentDTO>(); List<Document> list = this.getAllByCriteria(criterion); if (list != null && !list.isEmpty()) { for (Document doc : list) { DocumentDTO dto = new DocumentDTO(doc); dtoList.add(dto); } } logger.info("Done creating DTO list, size = " + dtoList.size()); return dtoList; } @Override public Integer getDocumentCountForNominalLabelAndCrisis(Long nominalLabelID, String crisisCode) { if (nominalLabelID != null) { String aliasTable = "documentNominalLabels"; String aliasTableKeyField = "documentNominalLabels.id.nominalLabelId"; String[] orderBy = {"documentId"}; Criteria criteria = null; try { CollectionDTO cdto = crisisEJB.getCrisisByCode(crisisCode); Criterion criterion = Restrictions.conjunction() .add(Restrictions.eq("collection.id",cdto.getCrisisID())) .add(Restrictions.eq("hasHumanLabels", true)); Criterion aliasCriterion = Restrictions.eq(aliasTableKeyField, nominalLabelID); // get just the documentIDs Projection projection = Projections.property("documentId"); //List<Document> docList = this.getByCriteriaWithInnerJoinByOrder(criterion, "DESC", orderBy, null, aliasTable, aliasCriterion); criteria = createCriteria(criterion, "DESC", orderBy, null, aliasTable, aliasCriterion, new Projection[] {projection}, JoinType.LEFT_OUTER_JOIN); List<Long> docIDList = criteria.list(); if (docIDList != null && !docIDList.isEmpty()) { return docIDList.size(); } } catch (Exception e) { logger.error("getDocumentCountForNominalLabelAndCrisis failed, criteria = " + criteria.toString(), e); return 0; } } return 0; } @Override public List<DocumentDTO> getDocumentCollectionWithNominalLabelData(Long nominalLabelID) throws Exception { List<DocumentDTO> dtoList = new ArrayList<DocumentDTO>(); if (nominalLabelID != null) { String aliasTable = "documentNominalLabels"; String aliasTableKeyField = "documentNominalLabels.id.nominalLabelId"; String[] orderBy = {"documentId"}; Criterion criterion = Restrictions.eq("hasHumanLabels", true); Criterion aliasCriterion = Restrictions.eq(aliasTableKeyField, nominalLabelID); List<Document> docList = this.getByCriteriaWithInnerJoinByOrder(criterion, "DESC", orderBy, null, aliasTable, aliasCriterion); if (docList != null && !docList.isEmpty()) { logger.info("[getDocumentCollectionWithNominalLabelData] Fetched size = " + docList.size()); NominalLabelDTO nominalLabel = nominalLabelEJB.getNominalLabelByID(nominalLabelID); for (Document doc: docList) { DocumentDTO dto = new DocumentDTO(doc); dto.setNominalLabelDTO(nominalLabel); dtoList.add(dto); } logger.info("[getDocumentCollectionWithNominalLabelData] Done creating DTO list, size = " + dtoList.size()); } } return dtoList; } @Override public Integer getUnlabeledDocumentsCountByCrisisID(Long crisisId) throws PropertyNotSetException { Criterion criterion = Restrictions.conjunction() .add(Restrictions.eq("collection.id",crisisId)) .add(Restrictions.eq("hasHumanLabels", false)); List<Document> documentList = this.getAllByCriteria(criterion); return documentList != null ? Integer.valueOf(documentList.size()) : 0; } @Override public boolean deleteDocuments(List<DocumentDTO> documents){ try { for (DocumentDTO documentDTO : documents) { deleteDocument(documentDTO); } return true; } catch (Exception e) { logger.error("Error in deleting document."); return false; } } @Override public List<Long> getUnassignedDocumentIDsByCrisisID(Long crisisID, Integer count) { List<Long> docIDList = new ArrayList<Long>(); Criteria criteria = null; try { String aliasTable = "taskAssignments"; String order = "ASC"; String aliasTableKey = "taskAssignments.id.documentId"; String[] orderBy = {"valueAsTrainingSample", "documentId"}; Criterion criterion = Restrictions.conjunction() .add(Restrictions.eq("collection.id",crisisID)) .add(Restrictions.eq("hasHumanLabels",false)); // get just the documentIDs Projection projection = Projections.property("documentId"); Criterion aliasCriterion = (Restrictions.isNull(aliasTableKey)); criteria = createCriteria(criterion, order, orderBy, count, aliasTable, aliasCriterion, new Projection[] {projection}, JoinType.LEFT_OUTER_JOIN); docIDList = criteria.list(); return docIDList; } catch (Exception e) { logger.error("getByCriteriaWithAliasByOrder failed, criteria = " + criteria.toString(), e); throw new HibernateException("getByCriteriaWithAliasByOrder failed, criteria = " + criteria.toString()); } } @Override public List<DocumentDTO> getDocumentForNominalLabelAndCrisis(List<Long> nominalLabelID, Long crisisId) { List<DocumentDTO> dtoList = new ArrayList<DocumentDTO>(); if (nominalLabelID != null) { String aliasTable = "documentNominalLabels"; String aliasTableKeyField = "documentNominalLabels.id.nominalLabelId"; Criteria criteria = null; try { Criterion criterion = Restrictions.conjunction() .add(Restrictions.eq("collection.id", crisisId)) .add(Restrictions.eq("hasHumanLabels", true)); Criterion aliasCriterion = Restrictions.in(aliasTableKeyField, nominalLabelID); // get just the documentIDs Projection projection = Projections.property("documentId"); //List<Document> docList = this.getByCriteriaWithInnerJoinByOrder(criterion, "DESC", orderBy, null, aliasTable, aliasCriterion); criteria = createCriteria(criterion, null, null, null, aliasTable, aliasCriterion, null, JoinType.INNER_JOIN); List<Document> docList = criteria.list(); if (docList != null && !docList.isEmpty()) { for (Document doc : docList) { DocumentDTO dto = new DocumentDTO(doc); dtoList.add(dto); } } } catch (Exception e) { logger.error("getDocumentCountForNominalLabelAndCrisis failed, criteria = " + criteria.toString(), e); } } return dtoList; } }