1 /*****************************************************************************
5 * Creation date 05.10.2012
8 *****************************************************************************/
10 package org.splat.service;
13 import java.io.IOException;
14 import java.util.ArrayList;
15 import java.util.Iterator;
16 import java.util.List;
18 import org.apache.log4j.Logger;
19 import org.apache.lucene.index.Term;
20 import org.apache.lucene.search.BooleanClause;
21 import org.apache.lucene.search.BooleanFilter;
22 import org.apache.lucene.search.BooleanQuery;
23 import org.apache.lucene.search.FilterClause;
24 import org.apache.lucene.search.IndexSearcher;
25 import org.apache.lucene.search.ScoreDoc;
26 import org.apache.lucene.search.Sort;
27 import org.apache.lucene.search.SortField;
28 import org.apache.lucene.search.TermQuery;
29 import org.apache.lucene.search.TermsFilter;
30 import org.apache.lucene.search.TopFieldDocs;
31 import org.apache.lucene.store.Directory;
32 import org.apache.lucene.store.FSDirectory;
33 import org.hibernate.Criteria;
34 import org.hibernate.Hibernate;
35 import org.hibernate.criterion.DetachedCriteria;
36 import org.hibernate.criterion.Disjunction;
37 import org.hibernate.criterion.Junction;
38 import org.hibernate.criterion.Order;
39 import org.hibernate.criterion.Restrictions;
40 import org.hibernate.type.Type;
41 import org.splat.dal.bo.kernel.User;
42 import org.splat.dal.bo.som.KnowledgeElement;
43 import org.splat.dal.bo.som.KnowledgeElementType;
44 import org.splat.dal.bo.som.ProgressState;
45 import org.splat.dal.bo.som.Scenario;
46 import org.splat.dal.bo.som.SimulationContext;
47 import org.splat.dal.bo.som.Study;
48 import org.splat.dal.bo.som.Visibility;
49 import org.splat.dal.dao.som.StudyDAO;
50 import org.splat.service.dto.ImportedStudyDTO;
51 import org.splat.service.dto.Proxy;
52 import org.splat.service.dto.StudyDTO;
53 import org.splat.service.dto.StudySearchFilterDTO;
54 import org.splat.service.technical.IndexService;
55 import org.splat.service.technical.IndexServiceImpl;
56 import org.splat.service.technical.RepositoryService;
57 import org.splat.util.BeanHelper;
58 import org.springframework.transaction.annotation.Transactional;
61 * Search service implementation.
63 * @author <a href="mailto:roman.kozlov@opencascade.com">Roman Kozlov (RKV)</a>
65 public class SearchServiceImpl implements SearchService {
70 public final static Logger LOG = Logger
71 .getLogger(org.splat.service.SearchServiceImpl.class);
74 * Injected repository service.
76 private RepositoryService _repositoryService;
78 * Injected index service.
80 private IndexService _indexService;
82 * Injected study service.
84 private StudyService _studyService;
88 private StudyDAO _studyDAO;
91 * Get a list of studies which are currently not presented in the lucene index.
93 * @return list of ImportedStudy DTO
95 @Transactional(readOnly = true)
96 public List<ImportedStudyDTO> selectStudies() {
97 List<ImportedStudyDTO> table = new ArrayList<ImportedStudyDTO>();
98 Study.Properties sprop = new Study.Properties();
99 List<Study> dbStudies = getStudyDAO().getAll();
101 for (Study aStudy : dbStudies) {
104 if (selectStudiesWhere(
105 sprop.setReference(aStudy.getReference())).size() != 0) {
106 // If this study was already indexed and found in the lucene index
107 // then skip it to avoid adding to the index it again.
110 } catch (Exception error) {
113 // Add the study to the list of studies which are
114 // currently not presented in the lucene index.
115 table.add(BeanHelper.copyBean(aStudy, ImportedStudyDTO.class));
121 * Refresh lucene index for studies.
124 * list of studies id's
126 @Transactional(readOnly = true)
128 public void reindexStudies(final String[] ridlist) {
129 for (int i = 0; i < ridlist.length; i++) {
130 long index = Long.valueOf(ridlist[i].trim());
131 Study study = getStudyService().selectStudy(index);
139 * @see org.splat.service.SearchService#selectKnowledgeElementsWhere(org.splat.dal.bo.som.KnowledgeElement.Properties[])
141 public List<Proxy> selectKnowledgeElementsWhere(
142 final KnowledgeElement.Properties... kprop) {
143 List<Proxy> result = new ArrayList<Proxy>();
147 // Creation of the Lucene query
148 File indir = getRepositoryService().getRepositoryIndexDirectory();
149 Directory index = FSDirectory.open(indir);
150 IndexSearcher searcher = new IndexSearcher(index, true);
151 BooleanQuery fulquery = new BooleanQuery();
153 for (int i = 0; i < kprop.length; i++) {
154 BooleanQuery query = new BooleanQuery();
155 Term input; // Supposed initialized below at least by the visibility
157 Visibility area = kprop[i].getVisibility(); // Visibility
159 input = new Term("area");
160 query.add(new TermQuery(input.createTerm(area.toString())),
161 BooleanClause.Occur.MUST);
163 ProgressState state = kprop[i].getProgressState(); // State
165 input = new Term("state");
167 new TermQuery(input.createTerm(state.toString())),
168 BooleanClause.Occur.MUST);
170 String refid = kprop[i].getReference(); // Reference
172 input = new Term("ref");
173 query.add(new TermQuery(input.createTerm(refid)),
174 BooleanClause.Occur.MUST);
176 KnowledgeElementType type = kprop[i].getType(); // Type
178 input = new Term("type");
179 query.add(new TermQuery(input.createTerm(type.getName())),
180 BooleanClause.Occur.MUST);
182 User manager = kprop[i].getAuthor(); // Author
183 if (manager != null) {
184 input = new Term("author");
185 query.add(new TermQuery(input
186 .createTerm(manager.toString())),
187 BooleanClause.Occur.MUST);
189 User actor = kprop[i].getActor(); // Contributor, Reviewer or Approver of the owner study
191 input = new Term("actor");
193 new TermQuery(input.createTerm(actor.toString())),
194 BooleanClause.Occur.MUST);
196 String title = kprop[i].getTitle(); // Title
198 input = new Term("contents");
199 BooleanQuery critext = new BooleanQuery();
200 String operator = "AND"; // Future user input
201 BooleanClause.Occur clause = BooleanClause.Occur.MUST;
202 if (operator.equals("OR")) {
203 clause = BooleanClause.Occur.SHOULD;
205 String[] word = title.split(" ");
206 for (int j = 0; j < word.length; j++) {
207 critext.add(new TermQuery(input.createTerm(word[j])),
210 query.add(critext, BooleanClause.Occur.MUST);
212 List<SimulationContext> context = kprop[i]
213 .getSimulationContexts();
214 if (context != null && context.size() > 0) {
215 BooleanQuery critext = new BooleanQuery();
216 for (Iterator<SimulationContext> j = context.iterator(); j
218 SimulationContext seltext = j.next();
219 input = new Term(String.valueOf(seltext.getType()
221 critext.add(new TermQuery(input.createTerm(seltext
222 .getValue())), BooleanClause.Occur.MUST);
224 query.add(critext, BooleanClause.Occur.MUST);
226 fulquery.add(query, BooleanClause.Occur.SHOULD);
228 if (LOG.isInfoEnabled()) {
229 LOG.info("Searching knowledges by Lucene query \""
230 + fulquery.toString() + "\".");
232 // Creation of the knowledge filter
233 BooleanFilter filter = new BooleanFilter();
234 TermsFilter select = new TermsFilter();
235 Term mytype = new Term("class");
236 select.addTerm(mytype.createTerm("KnowledgeElement"));
237 filter.add(new FilterClause(select, BooleanClause.Occur.SHOULD));
239 // Creation of the sort criteria
240 Sort sort = new Sort(new SortField("title", SortField.STRING));
243 TopFieldDocs found = searcher.search(fulquery, filter, hitsize,
246 if (found.totalHits < 1) {
247 return result; // No study found
250 // Construction of the result list
251 ScoreDoc[] hits = found.scoreDocs;
252 for (int i = 0; i < hits.length; i++) {
253 result.add(new IndexServiceImpl.ObjectProxy(searcher
257 } catch (Exception error) {
258 LOG.error("Error during Lucene search, reason:", error);
266 * @see org.splat.service.SearchService#selectStudiesWhere(org.splat.dal.bo.som.Study.Properties[])
268 public List<Proxy> selectStudiesWhere(final StudySearchFilterDTO filter) {
269 List<Proxy> result = new ArrayList<Proxy>();
271 DetachedCriteria query = DetachedCriteria
272 .forClass(Study.class, "study");
274 // Creation of the query
275 Junction topJunction = initQuery(filter);
277 String title = filter.getWords(); // Title
278 if (title != null && (!title.isEmpty())) {
279 // Look for given words in study titles
281 if (filter.isMatchAllCriteria()) { // AND
282 critext = Restrictions.conjunction();
284 critext = Restrictions.disjunction();
287 String[] word = title.split(" ");
288 for (int j = 0; j < word.length; j++) {
289 critext.add(Restrictions.like("title", "%" + word[j] + "%"));
291 topJunction.add(critext);
294 List<SimulationContext> context = filter.getSimContexts();
295 if (context != null && (!context.isEmpty())) {
296 // Get only studies which have given contexts
297 query.createAlias("contex", "ctx", Criteria.INNER_JOIN);
299 if (filter.isMatchAllContexts()) { // AND
300 critctx = Restrictions.conjunction();
302 critctx = Restrictions.disjunction();
304 for (SimulationContext seltext : context) {
305 // (simctxType = seltext.getType() AND simctxValue = seltext.getValue())
306 critctx.add(Restrictions.and(Restrictions.eq("ctx.value",
307 seltext.getValue()), Restrictions.eq("ctx.type",
308 seltext.getType())));
311 topJunction.add(critctx);
314 query.add(topJunction);
316 query.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
317 // Creation of the sort criteria
318 query.addOrder(Order.asc("title"));
320 if (LOG.isInfoEnabled()) {
321 LOG.info("Searching studies: \"" + query.toString() + "\".");
325 List<Study> found = getStudyDAO().getFilteredList(query);
327 // Construction of the result list
328 for (Study std : found) {
329 result.add(new StudyDTO(std.getIndex(), std.getReference(), std
330 .getProgressState(), std.getTitle(), std.getAuthor()
337 * Initialize query with base criteria.
341 * @return top junction of the search filter
343 private Junction initQuery(final StudySearchFilterDTO filter) {
344 Junction topJunction;
345 if (filter.isMatchAllCriteria()) { // AND
346 topJunction = Restrictions.conjunction();
348 topJunction = Restrictions.disjunction();
350 if (!StudySearchFilterDTO.ANY_STATE.equals(filter.getState())) {
351 ProgressState state = ProgressState.valueOf(filter.getState()); // State
353 topJunction.add(Restrictions.eq("state", state));
356 String refid = filter.getReference(); // Reference
357 if (refid != null && !refid.isEmpty()) {
358 topJunction.add(Restrictions.eq("sid", refid));
361 addDatesCriteria(topJunction, filter);
363 long authorId = Long.valueOf(filter.getAuthor());
364 if (authorId > 0) { // Author
365 topJunction.add(Restrictions.eq("manager.rid", authorId));
368 long actorId = filter.getConnectedUserId(); // Contributor, Reviewer or Approver
370 // User is loggen in - show public studies and studies where he is participating
371 Disjunction orCrit = Restrictions.disjunction();
375 /* If the user is a validation cycle participant */
378 "{alias}.rid in (select vcrel.owner from cycle_rel vcrel inner join cycle vc on vcrel.refer = vc.rid where {alias}.rid = vcrel.owner AND (vc.publisher = ? OR vc.reviewer = ? OR vc.approver = ? OR vc.signatory = ?) group by vcrel.owner)",
379 new Object[] { actorId,
388 /* If the user is contributor */
391 "{alias}.rid in (select rel.owner from contributor_rel rel where {alias}.rid = rel.owner AND rel.refer = ?)",
392 actorId, Hibernate.LONG))
394 /* If the user is author */
395 Restrictions.eq("study.manager.rid", actorId)).add(
396 /* If the study is public */
397 Restrictions.eq("study.visibility",
398 Visibility.PUBLIC)));
400 // User is not logged in - show only public studies
401 topJunction.add(Restrictions.eq("visibility", Visibility.PUBLIC));
407 * Add search criteria by dates to the junction filter condition.
410 * the junction filter condition
414 private void addDatesCriteria(final Junction topJunction,
415 final StudySearchFilterDTO filter) {
416 // Filter by creation date
417 if (filter.getCreatedAfter() != null) {
418 topJunction.add(Restrictions
419 .gt("credate", filter.getCreatedAfter()));
421 if (filter.getCreatedBefore() != null) {
422 topJunction.add(Restrictions.lt("credate", filter
423 .getCreatedBefore()));
425 // Filter by modification date
426 if (filter.getUpdatedAfter() != null) {
427 topJunction.add(Restrictions
428 .gt("lasdate", filter.getUpdatedAfter()));
430 if (filter.getUpdatedBefore() != null) {
431 topJunction.add(Restrictions.lt("lasdate", filter
432 .getUpdatedBefore()));
440 * @see org.splat.service.SearchService#selectStudiesWhere(org.splat.dal.bo.som.Study.Properties[])
443 public List<Proxy> selectStudiesWhere(final Study.Properties... sprop) {
444 List<Proxy> result = new ArrayList<Proxy>();
448 // Creation of the Lucene query
449 File indir = getRepositoryService().getRepositoryIndexDirectory();
450 Directory index = FSDirectory.open(indir);
451 IndexSearcher searcher = new IndexSearcher(index, true);
452 BooleanQuery fulquery = new BooleanQuery();
454 for (int i = 0; i < sprop.length; i++) {
455 BooleanQuery query = new BooleanQuery();
456 Term input; // Supposed initialized below at least by the visibility
458 Visibility area = sprop[i].getVisibility(); // Visibility
460 input = new Term("area");
461 query.add(new TermQuery(input.createTerm(area.toString())),
462 BooleanClause.Occur.MUST);
464 ProgressState state = sprop[i].getProgressState(); // State
466 input = new Term("state");
467 if (state == ProgressState.inPROGRESS) {
468 BooleanQuery cristate = new BooleanQuery();
469 cristate.add(new TermQuery(input.createTerm("inWORK")),
470 BooleanClause.Occur.SHOULD);
472 new TermQuery(input.createTerm("inDRAFT")),
473 BooleanClause.Occur.SHOULD);
475 new TermQuery(input.createTerm("inCHECK")),
476 BooleanClause.Occur.SHOULD);
477 query.add(cristate, BooleanClause.Occur.MUST);
479 query.add(new TermQuery(input.createTerm(state
480 .toString())), BooleanClause.Occur.MUST);
483 String refid = sprop[i].getReference(); // Reference
485 input = new Term("ref");
486 query.add(new TermQuery(input.createTerm(refid)),
487 BooleanClause.Occur.MUST);
489 User manager = sprop[i].getManager(); // Author
490 if (manager != null) {
491 input = new Term("author");
492 query.add(new TermQuery(input
493 .createTerm(manager.toString())),
494 BooleanClause.Occur.MUST);
496 User actor = sprop[i].getActor(); // Contributor, Reviewer or Approver
498 input = new Term("actor");
500 new TermQuery(input.createTerm(actor.toString())),
501 BooleanClause.Occur.MUST);
503 String title = sprop[i].getTitle(); // Title
505 input = new Term("contents");
506 BooleanQuery critext = new BooleanQuery();
507 String operator = "AND"; // Future user input
508 BooleanClause.Occur clause = BooleanClause.Occur.MUST;
509 if (operator.equals("OR")) {
510 clause = BooleanClause.Occur.SHOULD;
512 String[] word = title.split(" ");
513 for (int j = 0; j < word.length; j++) {
514 critext.add(new TermQuery(input.createTerm(word[j])),
517 query.add(critext, BooleanClause.Occur.MUST);
519 List<SimulationContext> context = sprop[i]
520 .getSimulationContexts();
521 if (context != null && context.size() > 0) {
522 BooleanQuery critext = new BooleanQuery();
523 for (Iterator<SimulationContext> j = context.iterator(); j
525 SimulationContext seltext = j.next();
526 input = new Term(String.valueOf(seltext.getType()
528 critext.add(new TermQuery(input.createTerm(seltext
529 .getValue())), BooleanClause.Occur.MUST);
531 query.add(critext, BooleanClause.Occur.MUST);
533 fulquery.add(query, BooleanClause.Occur.SHOULD);
535 if (LOG.isInfoEnabled()) {
536 LOG.info("Searching studies by Lucene query \""
537 + fulquery.toString() + "\".");
539 // Creation of the studies filter
540 BooleanFilter filter = new BooleanFilter();
541 TermsFilter select = new TermsFilter();
542 Term mytype = new Term("class");
543 select.addTerm(mytype.createTerm("Study"));
544 filter.add(new FilterClause(select, BooleanClause.Occur.SHOULD));
546 // Creation of the sort criteria
547 Sort sort = new Sort(new SortField("title", SortField.STRING));
550 TopFieldDocs found = searcher.search(fulquery, filter, hitsize,
553 if (found.totalHits < 1) {
554 return result; // No study found
557 // Construction of the result list
558 ScoreDoc[] hits = found.scoreDocs;
559 for (int i = 0; i < hits.length; i++) {
560 result.add(new IndexServiceImpl.ObjectProxy(searcher
564 } catch (Exception error) {
565 LOG.error("Error during Lucene search, reason:", error);
573 * @see org.splat.service.SearchService#indexStudy(org.splat.dal.bo.som.Study)
576 public void indexStudy(final Study study) {
577 LOG.debug("Index study: id=" + study.getRid() + "; reference="
578 + study.getReference());
580 Study.Properties sprop = new Study.Properties();
581 List<Proxy> index = selectStudiesWhere(sprop.setReference(study
584 if (index.size() != 0) {
585 LOG.debug("The given study is already indexed.");
586 return; // The given study is already indexed
589 IndexService lucin = getIndex();
590 Scenario[] scenes = study.getScenarii();
592 LOG.debug("Number of study " + study.getReference() + " actors: "
593 + study.getActor().size());
595 if (study.getProgressState() != ProgressState.inWORK) {
596 for (int i = 0; i < scenes.length; i++) {
597 List<KnowledgeElement> list = scenes[i]
598 .getAllKnowledgeElements();
599 for (Iterator<KnowledgeElement> j = list.iterator(); j
602 LOG.debug("Knowlegge added: id=" + j.next().getIndex());
606 } catch (Exception error) {
607 LOG.error("Unable to index the study '" + study.getIndex()
608 + "', reason:", error);
613 * Get lucene index handler. Create the index if it is not exist.
615 * @return IndexService
616 * @throws IOException
617 * if error when creating a new lucene index
619 private IndexService getIndex() throws IOException {
620 IndexService lucin = getIndexService();
621 if (!lucin.exists()) {
622 lucin.create(); // Happens when re-indexing all studies
628 * Get the repositoryService.
630 * @return the repositoryService
632 public RepositoryService getRepositoryService() {
633 return _repositoryService;
637 * Set the repositoryService.
639 * @param repositoryService
640 * the repositoryService to set
642 public void setRepositoryService(final RepositoryService repositoryService) {
643 _repositoryService = repositoryService;
647 * Get the indexService.
649 * @return the indexService
651 public IndexService getIndexService() {
652 return _indexService;
656 * Set the indexService.
658 * @param indexService
659 * the indexService to set
661 public void setIndexService(final IndexService indexService) {
662 _indexService = indexService;
666 * Get the studyService.
668 * @return the studyService
670 public StudyService getStudyService() {
671 return _studyService;
675 * Set the studyService.
677 * @param studyService
678 * the studyService to set
680 public void setStudyService(final StudyService studyService) {
681 _studyService = studyService;
687 * @return the studyDAO
689 public StudyDAO getStudyDAO() {
697 * the studyDAO to set
699 public void setStudyDAO(final StudyDAO studyDAO) {
700 _studyDAO = studyDAO;