+\r
+ /* (non-Javadoc)\r
+ * @see eu.etaxonomy.cdm.api.service.ITaxonService#findByDescriptionElementFullText(java.lang.Class, java.lang.String, eu.etaxonomy.cdm.model.taxon.Classification, java.util.List, java.util.List, boolean, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)\r
+ */\r
+ @Override\r
+ public Pager<SearchResult<TaxonBase>> findByDescriptionElementFullText(\r
+ Class<? extends DescriptionElementBase> clazz, String queryString,\r
+ Classification classification, List<Feature> features, List<Language> languages,\r
+ boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {\r
+\r
+\r
+ LuceneSearch luceneSearch = prepareByDescriptionElementFullTextSearch(clazz, queryString, classification, features, languages, highlightFragments);\r
+\r
+ // --- execute search\r
+ TopGroupsWithMaxScore topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);\r
+\r
+ Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();\r
+ idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");\r
+\r
+ // --- initialize taxa, highlight matches ....\r
+ ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());\r
+ @SuppressWarnings("rawtypes")\r
+ List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(\r
+ topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);\r
+\r
+ int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0;\r
+ return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, totalHits, pageSize, searchResults);\r
+\r
+ }\r
+\r
+\r
+ @Override\r
+ public Pager<SearchResult<TaxonBase>> findByEverythingFullText(String queryString,\r
+ Classification classification, List<Language> languages, boolean highlightFragments,\r
+ Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {\r
+\r
+ LuceneSearch luceneSearchByDescriptionElement = prepareByDescriptionElementFullTextSearch(null, queryString, classification, null, languages, highlightFragments);\r
+ LuceneSearch luceneSearchByTaxonBase = prepareFindByFullTextSearch(null, queryString, classification, languages, highlightFragments);\r
+\r
+ LuceneMultiSearch multiSearch = new LuceneMultiSearch(luceneSearchByDescriptionElement, luceneSearchByTaxonBase);\r
+\r
+ // --- execute search\r
+ TopGroupsWithMaxScore topDocsResultSet = multiSearch.executeSearch(pageSize, pageNumber);\r
+\r
+ // --- initialize taxa, highlight matches ....\r
+ ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(multiSearch, multiSearch.getQuery());\r
+\r
+ Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();\r
+ idFieldMap.put(CdmBaseType.TAXON, "id");\r
+ idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");\r
+\r
+ List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(\r
+ topDocsResultSet, multiSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);\r
+\r
+ int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupedHitCount : 0;\r
+ return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, totalHits, pageSize, searchResults);\r
+\r
+ }\r
+\r
+\r
+ /**\r
+ * @param clazz\r
+ * @param queryString\r
+ * @param classification\r
+ * @param features\r
+ * @param languages\r
+ * @param highlightFragments\r
+ * @param directorySelectClass\r
+ * @return\r
+ */\r
+ protected LuceneSearch prepareByDescriptionElementFullTextSearch(Class<? extends CdmBase> clazz, String queryString, Classification classification, List<Feature> features,\r
+ List<Language> languages, boolean highlightFragments) {\r
+ BooleanQuery finalQuery = new BooleanQuery();\r
+ BooleanQuery textQuery = new BooleanQuery();\r
+\r
+ LuceneSearch luceneSearch = new LuceneSearch(getSession(), DescriptionElementBase.class);\r
+ QueryFactory queryFactory = new QueryFactory(luceneSearch);\r
+\r
+ SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("inDescription.taxon.titleCache__sort", false)};\r
+ luceneSearch.setSortFields(sortFields);\r
+\r
+ // ---- search criteria\r
+ luceneSearch.setClazz(clazz);\r
+ textQuery.add(queryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);\r
+\r
+ // common name\r
+ Query nameQuery;\r
+ if(languages == null || languages.size() == 0){\r
+ nameQuery = queryFactory.newTermQuery("name", queryString);\r
+ } else {\r
+ nameQuery = new BooleanQuery();\r
+ BooleanQuery languageSubQuery = new BooleanQuery();\r
+ for(Language lang : languages){\r
+ languageSubQuery.add(queryFactory.newTermQuery("language.uuid", lang.getUuid().toString()), Occur.SHOULD);\r
+ }\r
+ ((BooleanQuery) nameQuery).add(queryFactory.newTermQuery("name", queryString), Occur.MUST);\r
+ ((BooleanQuery) nameQuery).add(languageSubQuery, Occur.MUST);\r
+ }\r
+ textQuery.add(nameQuery, Occur.SHOULD);\r
+\r
+\r
+ // text field from TextData\r
+ textQuery.add(queryFactory.newMultilanguageTextQuery("text", queryString, languages), Occur.SHOULD);\r
+\r
+ // --- TermBase fields - by representation ----\r
+ // state field from CategoricalData\r
+ textQuery.add(queryFactory.newDefinedTermQuery("states.state", queryString, languages), Occur.SHOULD);\r
+\r
+ // state field from CategoricalData\r
+ textQuery.add(queryFactory.newDefinedTermQuery("states.modifyingText", queryString, languages), Occur.SHOULD);\r
+\r
+ // area field from Distribution\r
+ textQuery.add(queryFactory.newDefinedTermQuery("area", queryString, languages), Occur.SHOULD);\r
+\r
+ // status field from Distribution\r
+ textQuery.add(queryFactory.newDefinedTermQuery("status", queryString, languages), Occur.SHOULD);\r
+\r
+ finalQuery.add(textQuery, Occur.MUST);\r
+ // --- classification ----\r
+\r
+ if(classification != null){\r
+ finalQuery.add(queryFactory.newEntityIdQuery("inDescription.taxon.taxonNodes.classification.id", classification), Occur.MUST);\r
+ }\r
+\r
+ // --- IdentifieableEntity fields - by uuid\r
+ if(features != null && features.size() > 0 ){\r
+ finalQuery.add(queryFactory.newEntityUuidQuery("feature.uuid", features), Occur.MUST);\r
+ }\r
+\r
+ // the description must be associated with a taxon\r
+ finalQuery.add(queryFactory.newIsNotNullQuery("inDescription.taxon.id"), Occur.MUST);\r
+\r
+ luceneSearch.setQuery(finalQuery);\r
+\r
+ if(highlightFragments){\r
+ luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());\r
+ }\r
+ return luceneSearch;\r
+ }\r
+\r
+ /**\r
+ * DefinedTerm representations and MultilanguageString maps are stored in the Lucene index by the {@link DefinedTermBaseClassBridge}\r
+ * and {@link MultilanguageTextFieldBridge } in a consistent way. One field per language and also in one additional field for all languages.\r
+ * This method is a convenient means to retrieve a Lucene query string for such the fields.\r
+ *\r
+ * @param name name of the term field as in the Lucene index. Must be field created by {@link DefinedTermBaseClassBridge}\r
+ * or {@link MultilanguageTextFieldBridge }\r
+ * @param languages the languages to search for exclusively. Can be <code>null</code> to search in all languages\r
+ * @param stringBuilder a StringBuilder to be reused, if <code>null</code> a new StringBuilder will be instantiated and is returned\r
+ * @return the StringBuilder given a parameter or a new one if the stringBuilder parameter was null.\r
+ *\r
+ * TODO move to utiliy class !!!!!!!!\r
+ */\r
+ private StringBuilder appendLocalizedFieldQuery(String name, List<Language> languages, StringBuilder stringBuilder) {\r
+\r
+ if(stringBuilder == null){\r
+ stringBuilder = new StringBuilder();\r
+ }\r
+ if(languages == null || languages.size() == 0){\r
+ stringBuilder.append(name + ".ALL:(%1$s) ");\r
+ } else {\r
+ for(Language lang : languages){\r
+ stringBuilder.append(name + "." + lang.getUuid().toString() + ":(%1$s) ");\r
+ }\r
+ }\r
+ return stringBuilder;\r
+ }\r
+\r
+ @Override\r