14 |
14 |
import java.util.List;
|
15 |
15 |
import java.util.Map;
|
16 |
16 |
import java.util.Set;
|
|
17 |
import java.util.StringTokenizer;
|
17 |
18 |
import java.util.TreeMap;
|
18 |
19 |
import java.util.UUID;
|
19 |
20 |
|
... | ... | |
23 |
24 |
import java.util.Hashtable;
|
24 |
25 |
|
25 |
26 |
import org.apache.log4j.Level;
|
|
27 |
import org.apache.lucene.document.Document;
|
26 |
28 |
import org.apache.lucene.queryParser.ParseException;
|
27 |
29 |
import org.joda.time.DateTime;
|
28 |
30 |
import org.joda.time.format.DateTimeFormat;
|
... | ... | |
42 |
44 |
import eu.etaxonomy.cdm.api.service.INameService;
|
43 |
45 |
import eu.etaxonomy.cdm.api.service.ITaxonService;
|
44 |
46 |
import eu.etaxonomy.cdm.api.service.ITermService;
|
|
47 |
import eu.etaxonomy.cdm.api.service.search.DocumentSearchResult;
|
45 |
48 |
import eu.etaxonomy.cdm.api.service.search.SearchResult;
|
46 |
49 |
import eu.etaxonomy.cdm.common.DocUtils;
|
47 |
50 |
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
|
|
51 |
import eu.etaxonomy.cdm.hibernate.search.AcceptedTaxonBridge;
|
48 |
52 |
|
49 |
53 |
import eu.etaxonomy.cdm.remote.controller.BaseController;
|
50 |
54 |
import eu.etaxonomy.cdm.remote.dto.common.ErrorResponse;
|
... | ... | |
108 |
112 |
|
109 |
113 |
/** Default name search type */
|
110 |
114 |
public static final String DEFAULT_SEARCH_TYPE = NAME_SEARCH;
|
|
115 |
|
|
116 |
/** Default max number of hits for the exact name search */
|
|
117 |
public static final String DEFAULT_MAX_NB_FOR_EXACT_SEARCH = "100";
|
111 |
118 |
|
112 |
119 |
/** Classifcation 'default' key */
|
113 |
120 |
public static final String CLASSIFICATION_DEFAULT = "default";
|
... | ... | |
294 |
301 |
@RequestMapping(value = { "" }, method = RequestMethod.GET, params = {"query"})
|
295 |
302 |
public ModelAndView doGetNameSearch(@RequestParam(value = "query", required = true) String[] queries,
|
296 |
303 |
HttpServletRequest request, HttpServletResponse response) throws IOException {
|
297 |
|
return doGetNameSearch(queries, DEFAULT_SEARCH_TYPE, request, response);
|
|
304 |
return doGetNameSearch(queries, DEFAULT_SEARCH_TYPE, DEFAULT_MAX_NB_FOR_EXACT_SEARCH, request, response);
|
298 |
305 |
}
|
299 |
306 |
|
300 |
307 |
/**
|
... | ... | |
307 |
314 |
* URI: <b>/{datasource-name}/name_catalogue</b>
|
308 |
315 |
*
|
309 |
316 |
* @param query
|
310 |
|
* The scientific name pattern(s) to query for. The query can
|
311 |
|
* contain wildcard characters ('*'). The query can be
|
312 |
|
* performed with no wildcard or with the wildcard at the
|
313 |
|
* begin and / or end depending on the search pattern.
|
|
317 |
* The scientific name pattern(s) to query for. The query can
|
|
318 |
* contain wildcard characters ('*'). The query can be
|
|
319 |
* performed with no wildcard or with the wildcard at the
|
|
320 |
* begin and / or end depending on the search pattern.
|
314 |
321 |
* @param type
|
315 |
|
* The type of name to query. This be either
|
316 |
|
* "name" : scientific name corresponding to 'name cache' in CDM or
|
317 |
|
* "title" : complete name corresponding to 'title cache' in CDM
|
|
322 |
* The type of name to query. This be either
|
|
323 |
* "name" : scientific name corresponding to 'name cache' in CDM or
|
|
324 |
* "title" : complete name corresponding to 'title cache' in CDM
|
|
325 |
* @param hits
|
|
326 |
* Maximum number of responses to be returned.
|
318 |
327 |
* @param request Http servlet request.
|
319 |
328 |
* @param response Http servlet response.
|
320 |
329 |
* @return a List of {@link NameSearch} objects each corresponding to a
|
... | ... | |
325 |
334 |
@RequestMapping(value = { "" }, method = RequestMethod.GET, params = {"query", "type"})
|
326 |
335 |
public ModelAndView doGetNameSearch(@RequestParam(value = "query", required = true) String[] queries,
|
327 |
336 |
@RequestParam(value = "type", required = false, defaultValue = DEFAULT_SEARCH_TYPE) String searchType,
|
|
337 |
@RequestParam(value = "hits", required = false, defaultValue = DEFAULT_MAX_NB_FOR_EXACT_SEARCH) String hits,
|
328 |
338 |
HttpServletRequest request, HttpServletResponse response) throws IOException {
|
329 |
339 |
ModelAndView mv = new ModelAndView();
|
330 |
340 |
List<RemoteResponse> nsList = new ArrayList<RemoteResponse>();
|
331 |
341 |
|
332 |
|
// if search type is not known then return error
|
333 |
|
if (!searchType.equals(NAME_SEARCH) && !searchType.equals(TITLE_SEARCH)) {
|
334 |
|
ErrorResponse er = new ErrorResponse();
|
335 |
|
er.setErrorMessage("searchType parameter can only be set as" + NAME_SEARCH + " or "
|
336 |
|
+ TITLE_SEARCH);
|
337 |
|
mv.addObject(er);
|
|
342 |
int h = 100;
|
|
343 |
try {
|
|
344 |
h = Integer.parseInt(hits);
|
|
345 |
} catch(NumberFormatException nfe) {
|
|
346 |
ErrorResponse er = new ErrorResponse();
|
|
347 |
er.setErrorMessage("hits parameter is not a number");
|
|
348 |
mv.addObject(er);
|
338 |
349 |
return mv;
|
339 |
350 |
}
|
340 |
|
|
|
351 |
|
341 |
352 |
// search through each query
|
342 |
353 |
for (String query : queries) {
|
343 |
|
|
|
354 |
if(query.equals("")) {
|
|
355 |
ErrorResponse er = new ErrorResponse();
|
|
356 |
er.setErrorMessage("Empty query field");
|
|
357 |
nsList.add(er);
|
|
358 |
continue;
|
|
359 |
}
|
|
360 |
// remove wildcards if any
|
344 |
361 |
String queryWOWildcards = getQueryWithoutWildCards(query);
|
345 |
|
MatchMode mm = getMatchModeFromQuery(query);
|
346 |
|
logger.info("doGetNameSearch()" + request.getRequestURI() + " for query \"" + query
|
347 |
|
+ "\" without wild cards : " + queryWOWildcards + " and match mode : " + mm);
|
348 |
|
List<NonViralName> nameList = new ArrayList<NonViralName>();
|
349 |
|
|
350 |
|
// if "name" search then find by name cache
|
351 |
|
if (searchType.equals(NAME_SEARCH)) {
|
352 |
|
nameList = (List<NonViralName>) service.findNamesByNameCache(queryWOWildcards, mm,
|
353 |
|
NAME_SEARCH_INIT_STRATEGY);
|
354 |
|
}
|
355 |
|
|
356 |
|
//if "title" search then find by title cache
|
357 |
|
if (searchType.equals(TITLE_SEARCH)) {
|
358 |
|
nameList = (List<NonViralName>) service.findNamesByTitleCache(queryWOWildcards, mm,
|
359 |
|
NAME_SEARCH_INIT_STRATEGY);
|
|
362 |
// convert first char to upper case
|
|
363 |
char[] stringArray = queryWOWildcards.toCharArray();
|
|
364 |
stringArray[0] = Character.toUpperCase(stringArray[0]);
|
|
365 |
queryWOWildcards = new String(stringArray);
|
|
366 |
|
|
367 |
boolean wc = false;
|
|
368 |
|
|
369 |
if(getMatchModeFromQuery(query) == MatchMode.BEGINNING) {
|
|
370 |
wc = true;
|
360 |
371 |
}
|
|
372 |
logger.info("doGetNameSearch()" + request.getRequestURI() + " for query \"" + query);
|
|
373 |
|
|
374 |
List<DocumentSearchResult> nameSearchList = new ArrayList<DocumentSearchResult>();
|
|
375 |
try {
|
|
376 |
nameSearchList = service.findByNameExactSearch(
|
|
377 |
queryWOWildcards,
|
|
378 |
wc,
|
|
379 |
null,
|
|
380 |
false,
|
|
381 |
h);
|
|
382 |
} catch (ParseException e) {
|
|
383 |
// TODO Auto-generated catch block
|
|
384 |
//e.printStackTrace();
|
|
385 |
ErrorResponse er = new ErrorResponse();
|
|
386 |
er.setErrorMessage("Could not parse name : " + query);
|
|
387 |
nsList.add(er);
|
|
388 |
continue;
|
|
389 |
}
|
|
390 |
|
361 |
391 |
|
362 |
392 |
// if search is successful then get related information , else return error
|
363 |
|
if (nameList == null || !nameList.isEmpty()) {
|
|
393 |
if (nameSearchList == null || !nameSearchList.isEmpty()) {
|
364 |
394 |
NameSearch ns = new NameSearch();
|
365 |
395 |
ns.setRequest(query);
|
366 |
396 |
|
367 |
|
for (NonViralName nvn : nameList) {
|
|
397 |
for (DocumentSearchResult searchResult : nameSearchList) {
|
|
398 |
for(Document doc : searchResult.getDocs()) {
|
|
399 |
|
368 |
400 |
// we need to retrieve both taxon uuid of name queried and
|
369 |
401 |
// the corresponding accepted taxa.
|
370 |
402 |
// reason to return accepted taxa also, is to be able to get from
|
371 |
403 |
// scientific name to taxon concept in two web service calls.
|
372 |
|
Set<TaxonBase> tbSet = nvn.getTaxonBases();
|
373 |
|
Set<TaxonBase> accTbSet = new HashSet<TaxonBase>();
|
374 |
|
for (TaxonBase tb : tbSet) {
|
375 |
|
// if synonym then get accepted taxa.
|
376 |
|
if (tb instanceof Synonym) {
|
377 |
|
Synonym synonym = (Synonym) tb;
|
378 |
|
Set<SynonymRelationship> synRelationships = synonym.getSynonymRelations();
|
379 |
|
for (SynonymRelationship sr : synRelationships) {
|
380 |
|
Taxon accTaxon = sr.getAcceptedTaxon();
|
381 |
|
accTbSet.add(accTaxon);
|
382 |
|
}
|
383 |
|
} else {
|
384 |
|
accTbSet.add(tb);
|
385 |
|
}
|
|
404 |
List<String> tbUuidList = new ArrayList<String>();//nvn.getTaxonBases();
|
|
405 |
List<String> accTbUuidList = new ArrayList<String>();
|
|
406 |
String[] tbUuids = doc.getValues("taxonBases.uuid");
|
|
407 |
String[] tbClassNames = doc.getValues("taxonBases.classInfo.name");
|
|
408 |
for(int i=0;i<tbUuids.length;i++) {
|
|
409 |
if(tbClassNames[i].equals("eu.etaxonomy.cdm.model.taxon.Taxon")) {
|
|
410 |
accTbUuidList.add(tbUuids[i]);
|
|
411 |
}
|
386 |
412 |
}
|
387 |
413 |
// update name search object
|
388 |
|
ns.addToResponseList(nvn.getTitleCache(), nvn.getNameCache(), 0, nvn.getUuid()
|
389 |
|
.toString(), tbSet, accTbSet);
|
|
414 |
ns.addToResponseList(doc.getValues("titleCache")[0],
|
|
415 |
doc.getValues("nameCache")[0],
|
|
416 |
searchResult.getMaxScore(),
|
|
417 |
doc.getValues("uuid")[0].toString(),
|
|
418 |
doc.getValues("taxonBases.uuid"),
|
|
419 |
mergeSynAccTaxonUuids(doc.getValues("taxonBases.accTaxon.uuids")));
|
|
420 |
}
|
390 |
421 |
}
|
391 |
422 |
nsList.add(ns);
|
392 |
423 |
|
393 |
424 |
} else {
|
394 |
425 |
ErrorResponse er = new ErrorResponse();
|
395 |
|
er.setErrorMessage("No Taxon Name for given query : " + query);
|
|
426 |
er.setErrorMessage("No Taxon Name matches : " + query + ", for given accuracy");
|
396 |
427 |
nsList.add(er);
|
397 |
428 |
}
|
398 |
429 |
}
|
... | ... | |
461 |
492 |
*/
|
462 |
493 |
@RequestMapping(value = { "fuzzy" }, method = RequestMethod.GET, params = {"query"})
|
463 |
494 |
public ModelAndView doGetNameFuzzySearch(@RequestParam(value = "query", required = true) String[] queries,
|
464 |
|
@RequestParam(value = "accuracy", required = false, defaultValue = "0.5") String accuracy,
|
|
495 |
@RequestParam(value = "accuracy", required = false, defaultValue = "0.6") String accuracy,
|
465 |
496 |
@RequestParam(value = "hits", required = false, defaultValue = "10") String hits,
|
466 |
497 |
HttpServletRequest request, HttpServletResponse response) throws IOException {
|
467 |
498 |
ModelAndView mv = new ModelAndView();
|
... | ... | |
500 |
531 |
queryWOWildcards = new String(stringArray);
|
501 |
532 |
logger.info("doGetNameSearch()" + request.getRequestURI() + " for query \"" + queryWOWildcards + " with accuracy " + accuracy);
|
502 |
533 |
//List<NonViralName> nameList = new ArrayList<NonViralName>();
|
503 |
|
List<SearchResult<TaxonNameBase>> nameSearchList = new ArrayList<SearchResult<TaxonNameBase>>();
|
|
534 |
List<DocumentSearchResult> nameSearchList = new ArrayList<DocumentSearchResult>();
|
504 |
535 |
try {
|
505 |
536 |
nameSearchList = service.findByNameFuzzySearch(
|
506 |
537 |
queryWOWildcards,
|
507 |
538 |
acc,
|
508 |
539 |
null,
|
509 |
540 |
false,
|
510 |
|
NAME_SEARCH_INIT_STRATEGY,
|
511 |
541 |
h);
|
512 |
542 |
} catch (ParseException e) {
|
513 |
543 |
// TODO Auto-generated catch block
|
... | ... | |
524 |
554 |
NameSearch ns = new NameSearch();
|
525 |
555 |
ns.setRequest(query);
|
526 |
556 |
|
527 |
|
for (SearchResult searchResult : nameSearchList) {
|
528 |
|
NonViralName nvn = HibernateProxyHelper.deproxy(searchResult.getEntity(), NonViralName.class);
|
|
557 |
for (DocumentSearchResult searchResult : nameSearchList) {
|
|
558 |
for(Document doc : searchResult.getDocs()) {
|
529 |
559 |
|
530 |
560 |
// we need to retrieve both taxon uuid of name queried and
|
531 |
561 |
// the corresponding accepted taxa.
|
532 |
562 |
// reason to return accepted taxa also, is to be able to get from
|
533 |
563 |
// scientific name to taxon concept in two web service calls.
|
534 |
|
Set<TaxonBase> tbSet = nvn.getTaxonBases();
|
535 |
|
Set<TaxonBase> accTbSet = new HashSet<TaxonBase>();
|
536 |
|
for (TaxonBase tb : tbSet) {
|
537 |
|
// if synonym then get accepted taxa.
|
538 |
|
if (tb instanceof Synonym) {
|
539 |
|
Synonym synonym = (Synonym) tb;
|
540 |
|
Set<SynonymRelationship> synRelationships = synonym.getSynonymRelations();
|
541 |
|
for (SynonymRelationship sr : synRelationships) {
|
542 |
|
Taxon accTaxon = sr.getAcceptedTaxon();
|
543 |
|
accTbSet.add(accTaxon);
|
544 |
|
}
|
545 |
|
} else {
|
546 |
|
accTbSet.add(tb);
|
547 |
|
}
|
|
564 |
List<String> tbUuidList = new ArrayList<String>();//nvn.getTaxonBases();
|
|
565 |
List<String> accTbUuidList = new ArrayList<String>();
|
|
566 |
String[] tbUuids = doc.getValues("taxonBases.uuid");
|
|
567 |
String[] tbClassNames = doc.getValues("taxonBases.classInfo.name");
|
|
568 |
for(int i=0;i<tbUuids.length;i++) {
|
|
569 |
if(tbClassNames[i].equals("eu.etaxonomy.cdm.model.taxon.Taxon")) {
|
|
570 |
accTbUuidList.add(tbUuids[i]);
|
|
571 |
}
|
548 |
572 |
}
|
549 |
573 |
// update name search object
|
550 |
|
ns.addToResponseList(nvn.getTitleCache(), nvn.getNameCache(), searchResult.getMaxScore(), nvn.getUuid()
|
551 |
|
.toString(), tbSet, accTbSet);
|
|
574 |
ns.addToResponseList(doc.getValues("titleCache")[0],
|
|
575 |
doc.getValues("nameCache")[0],
|
|
576 |
searchResult.getMaxScore(),
|
|
577 |
doc.getValues("uuid")[0].toString(),
|
|
578 |
doc.getValues("taxonBases.uuid"),
|
|
579 |
mergeSynAccTaxonUuids(doc.getValues("taxonBases.accTaxon.uuids")));
|
|
580 |
}
|
552 |
581 |
}
|
553 |
582 |
nsList.add(ns);
|
554 |
583 |
|
... | ... | |
562 |
591 |
mv.addObject(nsList);
|
563 |
592 |
return mv;
|
564 |
593 |
}
|
565 |
|
|
|
594 |
|
|
595 |
private String[] mergeSynAccTaxonUuids(String[] accTaxonUuids) {
|
|
596 |
List<String> accTaxonUuidList = new ArrayList<String>();
|
|
597 |
for(String accTaxonUuid : accTaxonUuids) {
|
|
598 |
for(String uuidListAsString : accTaxonUuid.split(AcceptedTaxonBridge.ACCEPTED_TAXON_UUID_LIST_SEP)) {
|
|
599 |
accTaxonUuidList.add(uuidListAsString);
|
|
600 |
}
|
|
601 |
}
|
|
602 |
return accTaxonUuidList.toArray(new String[0]);
|
|
603 |
|
|
604 |
}
|
566 |
605 |
/**
|
567 |
606 |
* Returns a documentation page for the Name Information API.
|
568 |
607 |
* <p>
|
... | ... | |
797 |
836 |
String title = syn.getTitleCache();
|
798 |
837 |
TaxonNameBase synnvn = (TaxonNameBase) syn.getName();
|
799 |
838 |
String name = synnvn.getTitleCache();
|
800 |
|
String rank = synnvn.getRank().getTitleCache();
|
|
839 |
String rank = (synnvn.getRank() == null)? "" : synnvn.getRank().getTitleCache();
|
801 |
840 |
String status = SYNONYM_STATUS;
|
802 |
841 |
String relLabel = sr.getType()
|
803 |
842 |
.getInverseRepresentation(Language.DEFAULT())
|
... | ... | |
1068 |
1107 |
ModelAndView mv = new ModelAndView();
|
1069 |
1108 |
List<RemoteResponse> ansList = new ArrayList<RemoteResponse>();
|
1070 |
1109 |
logger.info("doGetAcceptedNameSearch()");
|
|
1110 |
|
1071 |
1111 |
// if search type is not known then return error
|
1072 |
1112 |
if (!searchType.equals(NAME_SEARCH) && !searchType.equals(TITLE_SEARCH)) {
|
1073 |
1113 |
ErrorResponse er = new ErrorResponse();
|
NameCatalogueController : updated controller to improve exact and fuzzy name search to use lucene document only searches
NameSearch : updated dto to accomodate lucene documents