/* * This is eMonocot, a global online biodiversity information resource. * * Copyright © 2011–2015 The Board of Trustees of the Royal Botanic Gardens, Kew and The University of Oxford * * eMonocot is free software: you can redistribute it and/or modify it under the terms of the * GNU Affero General Public License as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * eMonocot is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * The complete text of the GNU Affero General Public License is in the source repository as the file * ‘COPYING’. It is also available from <http://www.gnu.org/licenses/>. */ package org.emonocot.portal.controller; import java.rmi.RemoteException; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.apache.solr.client.solrj.SolrServerException; import org.emonocot.api.CommentService; import org.emonocot.api.OrganisationService; import org.emonocot.api.ResourceService; import org.emonocot.api.SearchableObjectService; import org.emonocot.api.TypeAndSpecimenService; import org.emonocot.api.UserService; import org.emonocot.api.autocomplete.Match; import org.emonocot.model.SearchableObject; import org.emonocot.model.TypeAndSpecimen; import org.emonocot.pager.CellSet; import org.emonocot.pager.Cube; import org.emonocot.pager.Dimension; import org.emonocot.pager.FacetName; import org.emonocot.pager.Page; import org.emonocot.portal.controller.form.NcbiDto; import org.emonocot.portal.format.annotation.FacetRequestFormat; import org.emonocot.portal.view.geojson.Feature; import org.emonocot.portal.view.geojson.FeatureCollection; import org.emonocot.portal.ws.ncbi.NcbiService; import org.restdoc.api.GlobalHeader; import org.restdoc.api.MethodDefinition; import org.restdoc.api.ParamValidation; import org.restdoc.api.ResponseDefinition; import org.restdoc.api.RestDoc; import org.restdoc.api.RestResource; import org.restdoc.api.Schema; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.servlet.ModelAndView; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.util.JSONPObject; import com.fasterxml.jackson.module.jsonSchema.factories.SchemaFactoryWrapper; /** * * @author ben * */ @Controller public class SearchController { private static Logger queryLog = LoggerFactory.getLogger("query"); private static Logger logger = LoggerFactory .getLogger(SearchController.class); private SearchableObjectService searchableObjectService; private CommentService commentService; private OrganisationService organisationService; private ResourceService resourceService; private TypeAndSpecimenService typeAndSpecimenService; private UserService userService; private NcbiService ncbiService; private ObjectMapper objectMapper; @Autowired public void setSearchableObjectService(SearchableObjectService searchableObjectService) { this.searchableObjectService = searchableObjectService; } @Autowired public void setCommentService(CommentService commentService) { this.commentService = commentService; } @Autowired public void setOrganisationService(OrganisationService organisationService) { this.organisationService = organisationService; } @Autowired public void setResourceService(ResourceService resourceService) { this.resourceService = resourceService; } @Autowired public void setUserService(UserService userService) { this.userService = userService; } @Autowired public void setObjectMapper(ObjectMapper objectMapper) { this.objectMapper = objectMapper; } @Autowired public void setTypeAndSpecimenService(TypeAndSpecimenService typeAndSpecimenService) { this.typeAndSpecimenService = typeAndSpecimenService; } @Autowired public void setNcbiService(NcbiService ncbiService) { this.ncbiService = ncbiService; } /** * @param query * @param start * @param limit * @param spatial * @param responseFacets * @param sort * @param selectedFacets * @return */ private Page<? extends SearchableObject> runQuery(String query, Integer start, Integer limit, String spatial, String[] responseFacets, Map<String, String> facetPrefixes, String sort, Map<String, String> selectedFacets) throws SolrServerException { Page<? extends SearchableObject> result = searchableObjectService .search(query, spatial, limit, start, responseFacets, facetPrefixes, selectedFacets, sort, "taxon-with-image"); queryLog.info("Query: \'{}\', start: {}, limit: {}," + "facet: [{}], {} results", new Object[] { query, start, limit, selectedFacets, result.getSize() }); result.putParam("query", query); return result; } /** * * @param view * Set the view name * @param className * Set the class name * @return the default limit */ private String setView(String view, String className) { if (view == null || view == "") { return null; } else if (view.equals("grid")){ if (className == null || className == "") { return null; } else if (className.equals("org.emonocot.model.Image")){ return "grid"; } else { return null; } } return view; } /** * * @param query * Set the query * @param limit * Limit the number of returned results * @param start * Set the offset * @param facets * The facets to set * @param view * Set the view * @param model * Set the model * * @return a model and view */ @RequestMapping(value = "/search", method = RequestMethod.GET, produces = {"text/html", "*/*"}) public String search( @RequestParam(value = "query", required = false) String query, @RequestParam(value = "limit", required = false, defaultValue = "24") Integer limit, @RequestParam(value = "start", required = false, defaultValue = "0") Integer start, @RequestParam(value = "facet", required = false) @FacetRequestFormat List<FacetRequest> facets, @RequestParam(value = "sort", required = false) String sort, @RequestParam(value = "view", required = false) String view, Model model) throws SolrServerException { Map<String, String> selectedFacets = null; if (facets != null && !facets.isEmpty()) { selectedFacets = new HashMap<String, String>(); for (FacetRequest facetRequest : facets) { selectedFacets.put(facetRequest.getFacet(), facetRequest.getSelected()); } logger.debug(selectedFacets.size() + " facets have been selected from " + facets.size() + " available"); } else { logger.debug("There were no facets available to select from"); } if(selectedFacets != null && !selectedFacets.isEmpty()){ for(String key : selectedFacets.keySet()){ String escapeSpaceFacet = selectedFacets.get(key).replace(" ","\\ ").replace("\\\\", "\\"); selectedFacets.put(key, escapeSpaceFacet); } } // Decide which facets to return List<String> responseFacetList = new ArrayList<String>(); Map<String, String> facetPrefixes = new HashMap<String, String>(); responseFacetList.add("base.class_s"); if(selectedFacets == null) { responseFacetList.add(FacetName.FAMILY.getSolrField()); } else { int taxFacetIdx = 1; //Start from FacetName.FAMILY for (; taxFacetIdx < FacetName.taxonomyFacets.length; taxFacetIdx++) { FacetName fn = FacetName.taxonomyFacets[taxFacetIdx]; if(!responseFacetList.contains(fn.getSolrField())){ responseFacetList.add(fn.getSolrField()); } if(!selectedFacets.containsKey(fn.getSolrField())) { break; } } for(; taxFacetIdx < FacetName.taxonomyFacets.length; ++taxFacetIdx) { selectedFacets.remove(FacetName.taxonomyFacets[taxFacetIdx].getSolrField()); } } responseFacetList.add("taxon.distribution_TDWG_0_ss"); responseFacetList.add("taxon.measurement_or_fact_threatStatus_txt"); responseFacetList.add("taxon.measurement_or_fact_Lifeform_txt"); responseFacetList.add("taxon.measurement_or_fact_Habitat_txt"); responseFacetList.add("taxon.taxon_rank_s"); responseFacetList.add("taxon.taxonomic_status_s"); responseFacetList.add("searchable.sources_ss"); String className = null; if (selectedFacets == null) { logger.debug("No selected facets, setting default response facets"); } else { if (selectedFacets.containsKey("base.class_s")) { className = selectedFacets.get("base.class_s"); } if (selectedFacets.containsKey("taxon.distribution_TDWG_0_ss")) { logger.debug("Adding region facet"); responseFacetList.add("taxon.distribution_TDWG_1_ss"); facetPrefixes.put("taxon.distribution_TDWG_1_ss", selectedFacets.get("taxon.distribution_TDWG_0_ss") + "_"); } else { selectedFacets.remove("taxon.distribution_TDWG_1_ss"); } } String[] responseFacets = new String[] {}; responseFacets = responseFacetList.toArray(responseFacets); view = setView(view,className); //limit = setLimit(view, className); // Run the search Page<? extends SearchableObject> result = runQuery(query, start, limit, null, responseFacets, facetPrefixes, sort, selectedFacets); result.putParam("view", view); result.setSort(sort); model.addAttribute("result", result); return "search"; } @RequestMapping(value = "/search", method = RequestMethod.GET, produces = "application/json") public ResponseEntity<Page> searchAPI( @RequestParam(value = "query", required = false) String query, @RequestParam(value = "limit", required = false, defaultValue = "24") Integer limit, @RequestParam(value = "start", required = false, defaultValue = "0") Integer start, @RequestParam(value = "facet", required = false) @FacetRequestFormat List<FacetRequest> facets, @RequestParam(value = "x1", required = false) Double x1, @RequestParam(value = "y1", required = false) Double y1, @RequestParam(value = "x2", required = false) Double x2, @RequestParam(value = "y2", required = false) Double y2, @RequestParam(value = "sort", required = false) String sort, Model model) throws SolrServerException { spatial(query,x1, y1, x2, y2, null, limit,start,facets,sort,null,model); return new ResponseEntity<Page>((Page) model.asMap().get("result"),HttpStatus.OK); } @RequestMapping(value = "/search", method = RequestMethod.GET, produces = "application/javascript") public ResponseEntity<JSONPObject> searchAPIJSONP( @RequestParam(value = "query", required = false) String query, @RequestParam(value = "limit", required = false, defaultValue = "24") Integer limit, @RequestParam(value = "start", required = false, defaultValue = "0") Integer start, @RequestParam(value = "facet", required = false) @FacetRequestFormat List<FacetRequest> facets, @RequestParam(value = "x1", required = false) Double x1, @RequestParam(value = "y1", required = false) Double y1, @RequestParam(value = "x2", required = false) Double x2, @RequestParam(value = "y2", required = false) Double y2, @RequestParam(value = "sort", required = false) String sort, @RequestParam(value = "callback", required = true) String callback, Model model) throws SolrServerException { spatial(query,x1, y1, x2, y2, null, limit,start,facets,sort,null,model); return new ResponseEntity<JSONPObject>(new JSONPObject(callback,(Page) model.asMap().get("result")),HttpStatus.OK); } /** * * @param query * Set the query * @param limit * Limit the number of returned results * @param start * Set the offset * @param facets * The facets to set * @param x1 * the first latitude * @param x2 * the second latitude * @param y1 * the first longitude * @param y2 * the second longitude * @param view * Set the view * @param model * Set the model * * @return a model and view */ @RequestMapping(value = "/spatial", method = RequestMethod.GET, produces = {"text/html", "*/*"}) public String spatial( @RequestParam(value = "query", required = false) String query, @RequestParam(value = "x1", required = false) Double x1, @RequestParam(value = "y1", required = false) Double y1, @RequestParam(value = "x2", required = false) Double x2, @RequestParam(value = "y2", required = false) Double y2, @RequestParam(value = "featureId", required = false) String featureId, @RequestParam(value = "limit", required = false, defaultValue = "24") Integer limit, @RequestParam(value = "start", required = false, defaultValue = "0") Integer start, @RequestParam(value = "facet", required = false) @FacetRequestFormat List<FacetRequest> facets, @RequestParam(value = "sort", required = false) String sort, @RequestParam(value = "view", required = false) String view, Model model) throws SolrServerException { String spatial = null; DecimalFormat decimalFormat = new DecimalFormat("###0.0"); if (x1 != null && y1 != null && x2 != null && y2 != null && (x1 != 0.0 && y1 != 0.0 && x2 != 0.0 && x2 != 0.0 && y2 != 0.0)) { spatial = "{!join to=taxon.distribution_ss from=location.tdwg_code_s}geo:\"Intersects(" + decimalFormat.format(x1) + " " + decimalFormat.format(y1) + " " + decimalFormat.format(x2) + " " + decimalFormat.format(y2) + ")\""; } Map<String, String> selectedFacets = null; if (facets != null && !facets.isEmpty()) { selectedFacets = new HashMap<String, String>(); for (FacetRequest facetRequest : facets) { selectedFacets.put(facetRequest.getFacet(), facetRequest.getSelected()); } logger.debug(selectedFacets.size() + " facets have been selected from " + facets.size() + " available"); } else { logger.debug("There were no facets available to select from"); } // Decide which facets to return List<String> responseFacetList = new ArrayList<String>(); responseFacetList.add("base.class_s"); if(selectedFacets == null) { responseFacetList.add(FacetName.FAMILY.getSolrField()); } else { int taxFacetIdx = 1; //Start from FacetName.FAMILY for (; taxFacetIdx < FacetName.taxonomyFacets.length; taxFacetIdx++) { FacetName fn = FacetName.taxonomyFacets[taxFacetIdx]; if(!responseFacetList.contains(fn.getSolrField())){ responseFacetList.add(fn.getSolrField()); } if(!selectedFacets.containsKey(fn.getSolrField())) { break; } } for(; taxFacetIdx < FacetName.taxonomyFacets.length; ++taxFacetIdx) { selectedFacets.remove(FacetName.taxonomyFacets[taxFacetIdx].getSolrField()); } } responseFacetList.add("taxon.measurement_or_fact_threatStatus_txt"); responseFacetList.add("taxon.measurement_or_fact_Lifeform_txt"); responseFacetList.add("taxon.measurement_or_fact_Habitat_txt"); responseFacetList.add("taxon.taxon_rank_s"); responseFacetList.add("taxon.taxonomic_status_s"); responseFacetList.add("searchable.sources_ss"); String className = null; if (selectedFacets == null) { logger.debug("No selected facets, setting default response facets"); } else { if (selectedFacets.containsKey("base.class_s")) { className = selectedFacets.get("base.class_s"); } if (selectedFacets.containsKey("taxon.distribution_TDWG_0_ss")) { logger.debug("Removing continent facet"); responseFacetList.remove("taxon.distribution_TDWG_0_ss"); } } String[] responseFacets = new String[] {}; responseFacets = responseFacetList.toArray(responseFacets); view = setView(view,className); //limit = setLimit(view, className); // Run the search Page<? extends SearchableObject> result = runQuery(query, start, limit, spatial, responseFacets, null, sort, selectedFacets); if (spatial != null) { result.putParam("x1", x1); result.putParam("y1", y1); result.putParam("x2", x2); result.putParam("y2", y2); } if (!StringUtils.isEmpty(featureId)) { result.putParam("featureId", featureId); } result.putParam("view", view); result.setSort(sort); model.addAttribute("result", result); return "spatial"; } @RequestMapping(value = "/visualise", method = RequestMethod.GET, produces = "text/html") public String visualise( Model uiModel, @RequestParam(value = "rows", required = false) String rows, @RequestParam(value = "firstRow", required = false, defaultValue = "0") Integer firstRow, @RequestParam(value = "maxRows", required = false, defaultValue = "10") Integer maxRows, @RequestParam(value = "cols", required = false) String cols, @RequestParam(value = "firstCol", required = false, defaultValue = "0") Integer firstCol, @RequestParam(value = "maxCols", required = false, defaultValue = "5") Integer maxCols, @RequestParam(value = "facet", required = false) @FacetRequestFormat List<FacetRequest> facets, @RequestParam(value = "view", required = false, defaultValue = "bar") String view) throws Exception { Map<String, String> selectedFacets = null; if (facets != null && !facets.isEmpty()) { selectedFacets = new HashMap<String, String>(); for (FacetRequest facetRequest : facets) { selectedFacets.put(facetRequest.getFacet(), facetRequest.getSelected()); } } List<String> facetList = new ArrayList<String>(); if (selectedFacets == null) { facetList.add(FacetName.FAMILY.getSolrField()); } else { int taxFacetIdx = 1; // Start from FacetName.FAMILY for (; taxFacetIdx < FacetName.taxonomyFacets.length; taxFacetIdx++) { FacetName fn = FacetName.taxonomyFacets[taxFacetIdx]; if (!facetList.contains(fn.getSolrField())) { facetList.add(fn.getSolrField()); } if (!selectedFacets.containsKey(fn.getSolrField())) { break; } } for (; taxFacetIdx < FacetName.taxonomyFacets.length; ++taxFacetIdx) { selectedFacets.remove(FacetName.taxonomyFacets[taxFacetIdx] .getSolrField()); } } facetList.add("taxon.distribution_TDWG_0_ss"); facetList.add("taxon.taxon_rank_s"); facetList.add("taxon.taxonomic_status_s"); facetList.add("searchable.sources_ss"); facetList.add("taxon.measurement_or_fact_threatStatus_txt"); facetList.add("taxon.measurement_or_fact_Lifeform_txt"); facetList.add("taxon.measurement_or_fact_Habitat_txt"); Cube cube = new Cube(selectedFacets); cube.setDefaultLevel("taxon.order_s"); Dimension taxonomy = new Dimension("taxonomy"); cube.addDimension(taxonomy); taxonomy.addLevel("taxon.order_s", false); taxonomy.addLevel("taxon.family_ss", false); taxonomy.addLevel("taxon.subfamily_ss", false); taxonomy.addLevel("taxon.tribe_ss", false); taxonomy.addLevel("taxon.subtribe_ss", false); taxonomy.addLevel("taxon.genus_ss", false); Dimension distribution = new Dimension("distribution"); cube.addDimension(distribution); distribution.addLevel("taxon.distribution_TDWG_0_ss", true); distribution.addLevel("taxon.distribution_TDWG_1_ss", true); distribution.addLevel("taxon.distribution_TDWG_2_ss", true); Dimension taxonRank = new Dimension("taxonRank"); cube.addDimension(taxonRank); taxonRank.addLevel("taxon.taxon_rank_s", false); Dimension taxonomicStatus = new Dimension("taxonomicStatus"); cube.addDimension(taxonomicStatus); taxonomicStatus.addLevel("taxon.taxonomic_status_s", false); Dimension lifeForm = new Dimension("lifeForm"); cube.addDimension(lifeForm); lifeForm.addLevel("taxon.measurement_or_fact_Lifeform_txt", false); Dimension habitat = new Dimension("habitat"); cube.addDimension(habitat); habitat.addLevel("taxon.measurement_or_fact_Habitat_txt", false); Dimension conservationStatus = new Dimension("conservationStatus"); cube.addDimension(conservationStatus); conservationStatus.addLevel( "taxon.measurement_or_fact_threatStatus_txt", false); Dimension withDescriptions = new Dimension("hasDescriptions"); cube.addDimension(withDescriptions); withDescriptions.addLevel("taxon.descriptions_not_empty_b", false); Dimension withImages = new Dimension("hasImages"); cube.addDimension(withImages); withImages.addLevel("taxon.images_not_empty_b", false); CellSet cellSet = searchableObjectService.analyse(rows, cols, firstCol, maxCols, firstRow, maxRows, selectedFacets, facetList.toArray(new String[facetList.size()]), cube); uiModel.addAttribute("cellSet", cellSet); uiModel.addAttribute("view", view); return "visualise"; } /** * @param term * The term to search for * @return A list of terms to serialize */ @RequestMapping(value = "/autocomplete", method = RequestMethod.GET, produces = "application/json") public @ResponseBody List<Match> autocomplete(@RequestParam(required = true) String term) throws SolrServerException { return searchableObjectService.autocomplete(term, 10, null); } @RequestMapping(value = "/autocomplete/comment", method = RequestMethod.GET, produces = "application/json") public @ResponseBody List<Match> autocompleteComments(@RequestParam(required = true) String term) throws SolrServerException { return commentService.autocomplete(term, 10, null); } @RequestMapping(value = "/autocomplete/user", method = RequestMethod.GET, produces = "application/json") public @ResponseBody List<Match> autocompleteUsers(@RequestParam(required = true) String term) throws SolrServerException { return userService.autocomplete(term, 10, null); } @RequestMapping(value = "/autocomplete/organisation", method = RequestMethod.GET, produces = "application/json") public @ResponseBody List<Match> autocompleteOrganisations(@RequestParam(required = true) String term) throws SolrServerException { return organisationService.autocomplete(term, 10, null); } @RequestMapping(value = "/autocomplete/resource", method = RequestMethod.GET, produces = "application/json") public @ResponseBody List<Match> autocompleteResources(@RequestParam(required = true) String term) throws SolrServerException { return resourceService.autocomplete(term, 10, null); } @ExceptionHandler(SolrServerException.class) @ResponseStatus(value = HttpStatus.SERVICE_UNAVAILABLE) public ModelAndView handleObjectNotFoundException(SolrServerException sse) { ModelAndView modelAndView = new ModelAndView("serviceUnavailable"); modelAndView.addObject("exception", sse); return modelAndView; } @RequestMapping(method = RequestMethod.OPTIONS, produces = "application/json") public ResponseEntity<RestDoc> optionsResource() throws JsonMappingException { RestDoc restDoc = new RestDoc(); HashMap<String,Schema> schemas = new HashMap<String,Schema>(); Schema pagerSchema = new Schema(); SchemaFactoryWrapper pageVisitor = new SchemaFactoryWrapper(); objectMapper.acceptJsonFormatVisitor(objectMapper.constructType(Page.class), pageVisitor); pagerSchema.setSchema(pageVisitor.finalSchema()); schemas.put("http://e-monocot.org#page", pagerSchema); restDoc.setSchemas(schemas); GlobalHeader headers = new GlobalHeader(); headers.request("Content-Type","Must be set to application/json",true); headers.request("Authorization","Supports HTTP Basic. Users may also use their api key",false); restDoc.setHeaders(headers); ParamValidation integerParam = new ParamValidation(); integerParam.setType("match"); integerParam.setPattern("\\d+"); ParamValidation apikeyParam = new ParamValidation(); apikeyParam.setType("match"); apikeyParam.setPattern("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"); ParamValidation stringParam = new ParamValidation(); stringParam.setType("match"); stringParam.setPattern("[0-9a-f]+"); ParamValidation doubleParam = new ParamValidation(); doubleParam.setType("match"); doubleParam.setPattern("[0-9]+\\.[0.9]+"); Set<RestResource> resources = new HashSet<RestResource>(); RestResource searchForObjects = new RestResource(); searchForObjects.setId("Search"); searchForObjects.setPath("/search{?query,x1,y1,x2,y2,facet,limit,start,sort,callback,apikey,fetch}"); searchForObjects.param("limit", "The maximum number of resources to return", integerParam); searchForObjects.param("start", "The number of pages (of size _limit_) offset from the beginning of the recordset", integerParam); searchForObjects.param("apikey", "The apikey of the user account making the request", apikeyParam); searchForObjects.param("callback", "The name of the callback function used to wrap the JSON response", stringParam); searchForObjects.param("x1", "The southerly extent of the bounding box (uses WGS84 Coordinate reference system). Only documents with distributions within the bounding box will be returned", doubleParam); searchForObjects.param("y1", "The westerly extent of the bounding box (uses WGS84 Coordinate reference system). Only documents with distributions within the bounding box will be returned", doubleParam); searchForObjects.param("x2", "The northerly extent of the bounding box (uses WGS84 Coordinate reference system). Only documents with distributions within the bounding box will be returned", doubleParam); searchForObjects.param("y2", "The easterly extent of the bounding box (uses WGS84 Coordinate reference system). Only documents with distributions within the bounding box will be returned", doubleParam); searchForObjects.param("query", "A free-text query string. Only documents matching the query string will be returned", stringParam); searchForObjects.param("facet", "Only return documents which match a particular filter, in the form {fieldName}:{fieldValue} where fieldName is from the controlled vocabulary defined by org.emonocot.pager.FacetName.", stringParam); searchForObjects.param("sort", "Sort the result set according to the supplied criteria, in the form {fieldName}_(asc|desc) where fieldName is from the controlled vocabulary defined by org.emonocot.pager.FacetName.", stringParam); searchForObjects.param("fetch", "The name of a valid 'fetch-profile' which will load some or all related objects prior to serialization. Try 'object-page' to return most related objects", stringParam); MethodDefinition searchObjects = new MethodDefinition(); searchObjects.description("Search for resources"); ResponseDefinition searchObjectsResponseDefinition = new ResponseDefinition(); searchObjectsResponseDefinition.type("application/json", "http://e-monocot.org#page"); searchObjectsResponseDefinition.type("application/javascript", "http://e-monocot.org#page"); searchObjects.response(searchObjectsResponseDefinition); searchObjects.statusCode("200", "Successfully searched for resources"); searchForObjects.method("GET", searchObjects); resources.add(searchForObjects); restDoc.setResources(resources); return new ResponseEntity<RestDoc>(restDoc,HttpStatus.OK); } @RequestMapping(value = "/ncbi", method = RequestMethod.GET, consumes = "application/json", produces = "application/json") public ResponseEntity<NcbiDto> ncbi(@RequestParam(value = "query", required = true) String query) { NcbiDto ncbiDto = new NcbiDto(); try { ncbiDto = ncbiService.issueRequest(query); } catch (RemoteException re) { logger.error("Exception using NCBI Service :" + re.getMessage(), re); return new ResponseEntity<NcbiDto>(HttpStatus.INTERNAL_SERVER_ERROR); } return new ResponseEntity<NcbiDto>(ncbiDto,HttpStatus.OK); } @RequestMapping(value = "/geo", method = RequestMethod.GET, consumes = "application/json", produces = "application/json") public ResponseEntity<FeatureCollection> spatial( @RequestParam(value = "query", required = false) String query, @RequestParam(value = "x1", required = false) Double x1, @RequestParam(value = "y1", required = false) Double y1, @RequestParam(value = "x2", required = false) Double x2, @RequestParam(value = "y2", required = false) Double y2, @RequestParam(value = "limit", required = false, defaultValue = "5000") Integer limit, @RequestParam(value = "start", required = false, defaultValue = "0") Integer start, @RequestParam(value = "facet", required = false) @FacetRequestFormat List<FacetRequest> facets) throws SolrServerException { String spatial = null; DecimalFormat decimalFormat = new DecimalFormat("###0.0"); if (x1 != null && y1 != null && x2 != null && y2 != null && (x1 != 0.0 && y1 != 0.0 && x2 != 0.0 && x2 != 0.0 && y2 != 0.0)) { spatial = "geo:\"Intersects(" + decimalFormat.format(x1) + " " + decimalFormat.format(y1) + " " + decimalFormat.format(x2) + " " + decimalFormat.format(y2) + ")\""; } Map<String, String> selectedFacets = new HashMap<String, String>(); if (facets != null && !facets.isEmpty()) { for (FacetRequest facetRequest : facets) { selectedFacets.put(facetRequest.getFacet(), facetRequest.getSelected()); } logger.debug(selectedFacets.size() + " facets have been selected from " + facets.size() + " available"); } else { logger.debug("There were no facets available to select from"); } selectedFacets.put("base.class_s", "org.emonocot.model.TypeAndSpecimen"); // Run the search Page<TypeAndSpecimen> result = typeAndSpecimenQuery(query, start, limit, spatial, new String[] {}, null, null, selectedFacets); FeatureCollection featureCollection = new FeatureCollection(); for(TypeAndSpecimen typeAndSpecimen : result.getRecords()) { featureCollection.getFeatures().add(Feature.fromTypeAndSpecimen(typeAndSpecimen)); } return new ResponseEntity<FeatureCollection>(featureCollection,HttpStatus.OK); } private Page<TypeAndSpecimen> typeAndSpecimenQuery(String query, Integer start, Integer limit, String spatial, String[] responseFacets, Map<String, String> facetPrefixes, String sort, Map<String, String> selectedFacets) throws SolrServerException { Page<TypeAndSpecimen> result = typeAndSpecimenService.search(query, spatial, limit, start, responseFacets,facetPrefixes, selectedFacets, sort, null); result.putParam("query", query); return result; } }