package fi.otavanopisto.pyramus.dao.modules; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceException; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Join; import javax.persistence.criteria.Root; import org.apache.commons.lang.StringUtils; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.queryparser.classic.ParseException; import org.apache.lucene.queryparser.classic.QueryParser; import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.Sort; import org.apache.lucene.search.SortField; import org.hibernate.search.jpa.FullTextEntityManager; import org.hibernate.search.jpa.FullTextQuery; import org.hibernate.search.jpa.Search; import fi.otavanopisto.pyramus.dao.DAOFactory; import fi.otavanopisto.pyramus.dao.PyramusEntityDAO; import fi.otavanopisto.pyramus.dao.projects.ProjectDAO; import fi.otavanopisto.pyramus.domainmodel.base.CourseBase; import fi.otavanopisto.pyramus.domainmodel.base.CourseBase_; import fi.otavanopisto.pyramus.domainmodel.base.CourseEducationType; import fi.otavanopisto.pyramus.domainmodel.base.CourseEducationType_; import fi.otavanopisto.pyramus.domainmodel.base.Curriculum; import fi.otavanopisto.pyramus.domainmodel.base.EducationSubtype; import fi.otavanopisto.pyramus.domainmodel.base.EducationType; import fi.otavanopisto.pyramus.domainmodel.base.EducationalLength; import fi.otavanopisto.pyramus.domainmodel.base.EducationalTimeUnit; import fi.otavanopisto.pyramus.domainmodel.base.Subject; import fi.otavanopisto.pyramus.domainmodel.base.Tag; import fi.otavanopisto.pyramus.domainmodel.modules.Module; import fi.otavanopisto.pyramus.domainmodel.modules.Module_; import fi.otavanopisto.pyramus.domainmodel.projects.Project; import fi.otavanopisto.pyramus.domainmodel.projects.ProjectModule; import fi.otavanopisto.pyramus.domainmodel.users.User; import fi.otavanopisto.pyramus.persistence.search.SearchResult; @Stateless public class ModuleDAO extends PyramusEntityDAO<Module> { public Module create(String name, Subject subject, Integer courseNumber, Double moduleLength, EducationalTimeUnit moduleLengthTimeUnit, String description, Long maxParticipantCount, User creatingUser) { EntityManager entityManager = getEntityManager(); Date now = new Date(System.currentTimeMillis()); EducationalLength educationalLength = new EducationalLength(); educationalLength.setUnit(moduleLengthTimeUnit); educationalLength.setUnits(moduleLength); Module module = new Module(); module.setName(name); module.setDescription(description); module.setSubject(subject); module.setCourseNumber(courseNumber); module.setCourseLength(educationalLength); module.setMaxParticipantCount(maxParticipantCount); module.setCreator(creatingUser); module.setCreated(now); module.setLastModifier(creatingUser); module.setLastModified(now); entityManager.persist(module); return module; } public List<Module> listBySubject(Subject subject) { EntityManager entityManager = getEntityManager(); CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); CriteriaQuery<Module> criteria = criteriaBuilder.createQuery(Module.class); Root<Module> root = criteria.from(Module.class); criteria.select(root); criteria.where( criteriaBuilder.equal(root.get(Module_.subject), subject) ); return entityManager.createQuery(criteria).getResultList(); } public List<Module> listBySubjectAndCourseNumber(Subject subject, Integer courseNumber) { EntityManager entityManager = getEntityManager(); CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); CriteriaQuery<Module> criteria = criteriaBuilder.createQuery(Module.class); Root<Module> root = criteria.from(Module.class); criteria.select(root); criteria.where( criteriaBuilder.and( criteriaBuilder.equal(root.get(Module_.subject), subject), criteriaBuilder.equal(root.get(Module_.courseNumber), courseNumber) ) ); return entityManager.createQuery(criteria).getResultList(); } public Module updateTags(Module module, Set<Tag> tags) { EntityManager entityManager = getEntityManager(); module.setTags(tags); entityManager.persist(module); return module; } public Module updateCurriculums(Module module, Set<Curriculum> curriculums) { EntityManager entityManager = getEntityManager(); module.setCurriculums(curriculums); entityManager.persist(module); return module; } public Module update(Module module, String name, Subject subject, Integer courseNumber, Double length, EducationalTimeUnit lengthTimeUnit, String description, Long maxParticipantCount, User user) { EntityManager entityManager = getEntityManager(); Date now = new Date(System.currentTimeMillis()); EducationalLength educationalLength = module.getCourseLength(); if (educationalLength == null) { educationalLength = new EducationalLength(); } educationalLength.setUnit(lengthTimeUnit); educationalLength.setUnits(length); module.setName(name); module.setDescription(description); module.setSubject(subject); module.setCourseNumber(courseNumber); module.setCourseLength(educationalLength); module.setLastModifier(user); module.setLastModified(now); module.setMaxParticipantCount(maxParticipantCount); entityManager.persist(module); return module; } @SuppressWarnings("unchecked") public SearchResult<Module> searchModulesBasic(int resultsPerPage, int page, String text) { int firstResult = page * resultsPerPage; StringBuilder queryBuilder = new StringBuilder(); if (!StringUtils.isBlank(text)) { queryBuilder.append("+("); addTokenizedSearchCriteria(queryBuilder, "name", text, false); addTokenizedSearchCriteria(queryBuilder, "tags.text", text, false); addTokenizedSearchCriteria(queryBuilder, "description", text, false); addTokenizedSearchCriteria(queryBuilder, "moduleComponents.name", text, false); addTokenizedSearchCriteria(queryBuilder, "moduleComponents.description", text, false); queryBuilder.append(")"); } EntityManager entityManager = getEntityManager(); FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager); QueryParser parser = new QueryParser("", new StandardAnalyzer()); String queryString = queryBuilder.toString(); Query luceneQuery; try { if (StringUtils.isBlank(queryString)) { luceneQuery = new MatchAllDocsQuery(); } else { luceneQuery = parser.parse(queryString); } FullTextQuery query = (FullTextQuery) fullTextEntityManager.createFullTextQuery(luceneQuery, Module.class) .setSort(new Sort(new SortField[] { SortField.FIELD_SCORE, new SortField("nameSortable", SortField.Type.STRING) })).setFirstResult(firstResult) .setMaxResults(resultsPerPage); query.enableFullTextFilter("ArchivedModule").setParameter("archived", Boolean.FALSE); int hits = query.getResultSize(); int pages = hits / resultsPerPage; if (hits % resultsPerPage > 0) { pages++; } int lastResult = Math.min(firstResult + resultsPerPage, hits) - 1; return new SearchResult<>(page, pages, hits, firstResult, lastResult, query.getResultList()); } catch (ParseException e) { throw new PersistenceException(e); } } public SearchResult<Module> searchModules(int resultsPerPage, int page, String projectName, String name, String tags, String description, String componentName, String componentDescription, Long ownerId, boolean filterArchived) { // Search with null Subject and null EducationSubtype return searchModules(resultsPerPage, page, projectName, name, tags, description, componentName, componentDescription, ownerId, null, null, null, null, filterArchived); } @SuppressWarnings("unchecked") public SearchResult<Module> searchModules(int resultsPerPage, int page, String projectName, String name, String tags, String description, String componentName, String componentDescription, Long ownerId, Subject subject, EducationType educationType, EducationSubtype educationSubtype, Curriculum curriculum, boolean filterArchived) { int firstResult = page * resultsPerPage; StringBuilder queryBuilder = new StringBuilder(); boolean hasName = !StringUtils.isBlank(name); boolean hasTags = !StringUtils.isBlank(tags); boolean hasDescription = !StringUtils.isBlank(description); boolean hasComponentName = !StringUtils.isBlank(componentName); boolean hasComponentDescription = !StringUtils.isBlank(componentDescription); boolean hasSubject = subject != null; boolean hasEduType = educationType != null; boolean hasEduSubtype = educationSubtype != null; boolean hasCurriculum = curriculum != null; if (hasName || hasTags || hasDescription || hasComponentName || hasComponentDescription || hasSubject || hasEduType || hasEduSubtype || hasCurriculum) { queryBuilder.append("+("); if (hasName) addTokenizedSearchCriteria(queryBuilder, "name", name, false); if (hasTags) addTokenizedSearchCriteria(queryBuilder, "tags.text", tags, false); if (hasDescription) addTokenizedSearchCriteria(queryBuilder, "description", description, false); if (hasComponentName) addTokenizedSearchCriteria(queryBuilder, "moduleComponents.name", componentName, false); if (hasComponentDescription) addTokenizedSearchCriteria(queryBuilder, "moduleComponents.description", componentDescription, false); if (hasSubject) addTokenizedSearchCriteria(queryBuilder, "subject.id", subject.getId().toString(), true); if (hasCurriculum) addTokenizedSearchCriteria(queryBuilder, "curriculum.id", curriculum.getId().toString(), true); if (hasEduType) addTokenizedSearchCriteria(queryBuilder, "courseEducationTypes.educationType.id", educationType.getId().toString(), true); if (hasEduSubtype) addTokenizedSearchCriteria(queryBuilder, "courseEducationTypes.courseEducationSubtypes.educationSubtype.id", educationSubtype.getId().toString(), true); queryBuilder.append(")"); } // If project text is given, only include modules that are in project(s) having the given name // (only the first ten matching projects, though, to prevent the search from becoming too gigantic...) Set<Long> moduleIds = new HashSet<>(); if (!StringUtils.isBlank(projectName)) { ProjectDAO projectDAO = DAOFactory.getInstance().getProjectDAO(); SearchResult<Project> searchResults = projectDAO.searchProjectsBasic(10, 0, projectName); List<Project> projects = searchResults.getResults(); for (Project project : projects) { List<ProjectModule> projectModules = project.getProjectModules(); for (ProjectModule projectModule : projectModules) { moduleIds.add(projectModule.getModule().getId()); } } if (!moduleIds.isEmpty()) { queryBuilder.append(" +("); for (Long moduleId : moduleIds) { queryBuilder.append(" id: " + moduleId); } queryBuilder.append(")"); } else { // Search condition by project name didn't yield any projects, so there cannot be any results return new SearchResult<>(0, 0, 0, 0, 0, new ArrayList<Module>()); } } EntityManager entityManager = getEntityManager(); FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager); try { if (ownerId != null && ownerId > 0) { queryBuilder.append(" +creator.id: ").append(ownerId); } QueryParser parser = new QueryParser("", new StandardAnalyzer()); String queryString = queryBuilder.toString(); Query luceneQuery; if (StringUtils.isBlank(queryString)) { luceneQuery = new MatchAllDocsQuery(); } else { luceneQuery = parser.parse(queryString); } FullTextQuery query = (FullTextQuery) fullTextEntityManager.createFullTextQuery(luceneQuery, Module.class) .setSort(new Sort(new SortField[] { SortField.FIELD_SCORE, new SortField("nameSortable", SortField.Type.STRING) })).setFirstResult(firstResult) .setMaxResults(resultsPerPage); if (filterArchived) query.enableFullTextFilter("ArchivedModule").setParameter("archived", Boolean.FALSE); int hits = query.getResultSize(); int pages = hits / resultsPerPage; if (hits % resultsPerPage > 0) { pages++; } int lastResult = Math.min(firstResult + resultsPerPage, hits) - 1; return new SearchResult<>(page, pages, hits, firstResult, lastResult, query.getResultList()); } catch (ParseException e) { throw new PersistenceException(e); } } public List<Module> listByEducationType(EducationType educationType) { // return s.createCriteria(CourseEducationType.class) // .add(Restrictions.in("courseBase.id", s.createCriteria(Module.class).setProjection(Projections.id()).list())) // .add(Restrictions.eq("educationType", educationType)).setProjection(Projections.property("courseBase")).list(); EntityManager entityManager = getEntityManager(); CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); CriteriaQuery<Module> criteria = criteriaBuilder.createQuery(Module.class); Root<Module> root = criteria.from(Module.class); Root<CourseEducationType> eduType = criteria.from(CourseEducationType.class); Join<CourseEducationType, CourseBase> eduTypeCourseBase = eduType.join(CourseEducationType_.courseBase); criteria.select(root); criteria.where( criteriaBuilder.and( criteriaBuilder.equal(root.get(Module_.id), eduTypeCourseBase.get(CourseBase_.id)), criteriaBuilder.equal(eduType.get(CourseEducationType_.educationType), educationType) )); return entityManager.createQuery(criteria).getResultList(); } }