From f74e0c2c855d7c5de2dc5233bcb4d5a34c159629 Mon Sep 17 00:00:00 2001 From: qqmyers Date: Thu, 9 Sep 2021 17:47:56 -0400 Subject: [PATCH 01/19] add bonding box indexing --- conf/solr/8.8.1/schema.xml | 9 ++++ .../iq/dataverse/search/IndexServiceBean.java | 42 +++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/conf/solr/8.8.1/schema.xml b/conf/solr/8.8.1/schema.xml index c6f6cd37cd6..622c4661f6c 100644 --- a/conf/solr/8.8.1/schema.xml +++ b/conf/solr/8.8.1/schema.xml @@ -450,6 +450,9 @@ + + + + + @@ -909,6 +915,9 @@ --> + + diff --git a/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java b/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java index d72e2a7f642..b718a63ed95 100644 --- a/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java @@ -5,6 +5,7 @@ import edu.harvard.iq.dataverse.DataFileTag; import edu.harvard.iq.dataverse.Dataset; import edu.harvard.iq.dataverse.DatasetField; +import edu.harvard.iq.dataverse.DatasetFieldCompoundValue; import edu.harvard.iq.dataverse.DatasetFieldConstant; import edu.harvard.iq.dataverse.DatasetFieldType; import edu.harvard.iq.dataverse.DatasetLinkingServiceBean; @@ -883,6 +884,47 @@ private String addOrUpdateDataset(IndexableDataset indexableDataset, Set d } } } + + //ToDo - define a geom/bbox type solr field and find those instead of just this one + if(dsfType.getName().equals(DatasetFieldConstant.geographicBoundingBox)) { + for (DatasetFieldCompoundValue compoundValue : dsf.getDatasetFieldCompoundValues()) { + String westLon=null; + String eastLon=null; + String northLat=null; + String southLat=null; + for(DatasetField childDsf: compoundValue.getChildDatasetFields()) { + switch (childDsf.getDatasetFieldType().getName()) { + case DatasetFieldConstant.westLongitude: + westLon = childDsf.getRawValue(); + break; + case DatasetFieldConstant.eastLongitude: + eastLon = childDsf.getRawValue(); + break; + case DatasetFieldConstant.northLatitude: + northLat = childDsf.getRawValue(); + break; + case DatasetFieldConstant.southLatitude: + southLat = childDsf.getRawValue(); + break; + } + } + if ((eastLon != null || westLon != null) && (northLat != null || southLat != null)) { + // we have a point or a box, so proceed + if (eastLon == null) { + eastLon = westLon; + } else if (westLon == null) { + westLon = eastLon; + } + if (northLat == null) { + northLat = southLat; + } else if (southLat == null) { + southLat = northLat; + } + //W, E, N, S + solrInputDocument.addField("solr_srpt", "ENVELOPE(" + westLon + "," + eastLon + "," + northLat + "," + southLat + ")"); + } + } + } } } From 1007a6b1f1af2eb59f56e2a2d595139c064f289f Mon Sep 17 00:00:00 2001 From: qqmyers Date: Wed, 14 Sep 2022 13:47:52 -0400 Subject: [PATCH 02/19] Update conf/solr/8.11.1/schema.xml Co-authored-by: Philip Durbin --- conf/solr/8.11.1/schema.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/solr/8.11.1/schema.xml b/conf/solr/8.11.1/schema.xml index 78cb0270532..381d72d2756 100644 --- a/conf/solr/8.11.1/schema.xml +++ b/conf/solr/8.11.1/schema.xml @@ -1110,7 +1110,7 @@ --> - + From b903e2abf5e4527349ec177b063e36f1d216d999 Mon Sep 17 00:00:00 2001 From: qqmyers Date: Thu, 15 Sep 2022 19:19:35 -0400 Subject: [PATCH 03/19] release note and addition to search doc --- doc/release-notes/8239-geospatial-indexing.md | 1 + doc/sphinx-guides/source/api/search.rst | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 doc/release-notes/8239-geospatial-indexing.md diff --git a/doc/release-notes/8239-geospatial-indexing.md b/doc/release-notes/8239-geospatial-indexing.md new file mode 100644 index 00000000000..3e6ba0e7a07 --- /dev/null +++ b/doc/release-notes/8239-geospatial-indexing.md @@ -0,0 +1 @@ +Support for indexing the Geographic Bounding Box fields from the Geospatial metadata block has been added. This allows trusted applications with access to solr to perform geospatial queries to find datasets, e.g. those near a given point. This is also a step towards enabling geospatial queries via the Dataverse API and UI. diff --git a/doc/sphinx-guides/source/api/search.rst b/doc/sphinx-guides/source/api/search.rst index d5e56543fb1..149ad132f79 100755 --- a/doc/sphinx-guides/source/api/search.rst +++ b/doc/sphinx-guides/source/api/search.rst @@ -730,3 +730,17 @@ Output from iteration example CORS + + +Geospatial Indexing +------------------- + +Dataverse indexes the Geospatial Bounding Box field from the Geospatial metadatablock as a solr.BBoxField enabling `Spatial Search`_. This capability is not yet exposed through the Dataverse API or UI but can be accessed by trusted applications with direct solr access. +For example, a query of the form + +.. code-block:: none + + q=*.*&fq={!bbox sfield=solr_srpt}=&pt=10,10&d=5 + + +would find datasets with information near the point latitude=10, longitude=10. From c21082167d31c12354cab32544f5d7efeb100255 Mon Sep 17 00:00:00 2001 From: qqmyers Date: Mon, 19 Sep 2022 13:48:20 -0400 Subject: [PATCH 04/19] add space --- doc/sphinx-guides/source/api/search.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/sphinx-guides/source/api/search.rst b/doc/sphinx-guides/source/api/search.rst index 149ad132f79..fdebfdb8b10 100755 --- a/doc/sphinx-guides/source/api/search.rst +++ b/doc/sphinx-guides/source/api/search.rst @@ -735,7 +735,7 @@ Output from iteration example Geospatial Indexing ------------------- -Dataverse indexes the Geospatial Bounding Box field from the Geospatial metadatablock as a solr.BBoxField enabling `Spatial Search`_. This capability is not yet exposed through the Dataverse API or UI but can be accessed by trusted applications with direct solr access. +Dataverse indexes the Geospatial Bounding Box field from the Geospatial metadatablock as a solr.BBoxField enabling `Spatial Search `_. This capability is not yet exposed through the Dataverse API or UI but can be accessed by trusted applications with direct solr access. For example, a query of the form .. code-block:: none From 0b0c3b937e4eeef584a0313a76d01bc51fe40ea3 Mon Sep 17 00:00:00 2001 From: qqmyers Date: Thu, 6 Oct 2022 14:52:35 -0400 Subject: [PATCH 05/19] add multivalued in schema --- conf/solr/8.11.1/schema.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conf/solr/8.11.1/schema.xml b/conf/solr/8.11.1/schema.xml index 381d72d2756..063ffa9bd29 100644 --- a/conf/solr/8.11.1/schema.xml +++ b/conf/solr/8.11.1/schema.xml @@ -679,9 +679,9 @@ - + - + From cf9b4aee9c5914ff6046fabd188466eb7d3bba1f Mon Sep 17 00:00:00 2001 From: qqmyers Date: Thu, 6 Oct 2022 16:19:56 -0400 Subject: [PATCH 06/19] case matters --- conf/solr/8.11.1/schema.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conf/solr/8.11.1/schema.xml b/conf/solr/8.11.1/schema.xml index 063ffa9bd29..e9fbb35403e 100644 --- a/conf/solr/8.11.1/schema.xml +++ b/conf/solr/8.11.1/schema.xml @@ -679,9 +679,9 @@ - + - + From f542925e24e301b4c9c5336fbfc8f5bbc01c6f79 Mon Sep 17 00:00:00 2001 From: qqmyers Date: Thu, 6 Oct 2022 16:43:24 -0400 Subject: [PATCH 07/19] north < south latitude is an error we may want to test for that, but not here. --- doc/sphinx-guides/source/_static/api/ddi_dataset.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/sphinx-guides/source/_static/api/ddi_dataset.xml b/doc/sphinx-guides/source/_static/api/ddi_dataset.xml index 05eaadc3458..850e6e72ba2 100644 --- a/doc/sphinx-guides/source/_static/api/ddi_dataset.xml +++ b/doc/sphinx-guides/source/_static/api/ddi_dataset.xml @@ -88,8 +88,8 @@ 10 20 - 30 - 40 + 40 + 30 80 From 4f9434e9e09689ea8afcbbf7fb36656b7b77e2ae Mon Sep 17 00:00:00 2001 From: qqmyers Date: Wed, 12 Oct 2022 15:34:45 -0400 Subject: [PATCH 08/19] fix another non-physical box --- doc/sphinx-guides/source/_static/api/ddi_dataset.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/sphinx-guides/source/_static/api/ddi_dataset.xml b/doc/sphinx-guides/source/_static/api/ddi_dataset.xml index 850e6e72ba2..014ebb8c581 100644 --- a/doc/sphinx-guides/source/_static/api/ddi_dataset.xml +++ b/doc/sphinx-guides/source/_static/api/ddi_dataset.xml @@ -92,8 +92,8 @@ 30 - 80 - 70 + 70 + 80 60 50 From 1db095fa8cbb947beddc1792761ed6bebf099db8 Mon Sep 17 00:00:00 2001 From: qqmyers Date: Wed, 12 Oct 2022 16:58:48 -0400 Subject: [PATCH 09/19] handle multiples - make bbox a single surrounding box --- conf/solr/8.11.1/schema.xml | 3 --- .../iq/dataverse/search/IndexServiceBean.java | 22 +++++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/conf/solr/8.11.1/schema.xml b/conf/solr/8.11.1/schema.xml index e9fbb35403e..10f1d8f1f4f 100644 --- a/conf/solr/8.11.1/schema.xml +++ b/conf/solr/8.11.1/schema.xml @@ -647,9 +647,6 @@ - - - diff --git a/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java b/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java index 63412c59b56..766d2a05e6d 100644 --- a/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java @@ -38,6 +38,7 @@ import java.io.IOException; import java.io.InputStream; import java.sql.Timestamp; +import java.text.NumberFormat; import java.text.SimpleDateFormat; import java.time.LocalDate; import java.util.ArrayList; @@ -951,6 +952,10 @@ public SolrInputDocuments toSolrDocs(IndexableDataset indexableDataset, Set Float.parseFloat(westLon)) { + minWestLon=westLon; + } + if(maxEastLon==null || Float.parseFloat(maxEastLon) < Float.parseFloat(eastLon)) { + maxEastLon=eastLon; + } + if(minSouthLat==null || Float.parseFloat(minSouthLat) > Float.parseFloat(southLat)) { + minSouthLat=southLat; + } + if(maxNorthLat==null || Float.parseFloat(maxNorthLat) < Float.parseFloat(northLat)) { + maxNorthLat=northLat; + } //W, E, N, S solrInputDocument.addField("solr_srpt", "ENVELOPE(" + westLon + "," + eastLon + "," + northLat + "," + southLat + ")"); } + //Only one bbox per dataset + //W, E, N, S + solrInputDocument.addField("solr_bbox", "ENVELOPE(" + minWestLon + "," + maxEastLon + "," + maxNorthLat + "," + minSouthLat + ")"); + } } } From 202438af792eef1ff5db5835b5ca350eea366c32 Mon Sep 17 00:00:00 2001 From: qqmyers Date: Wed, 12 Oct 2022 21:50:21 -0400 Subject: [PATCH 10/19] typo --- .../java/edu/harvard/iq/dataverse/search/IndexServiceBean.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java b/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java index 766d2a05e6d..05947ee1224 100644 --- a/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java @@ -1007,7 +1007,7 @@ public SolrInputDocuments toSolrDocs(IndexableDataset indexableDataset, Set Date: Thu, 13 Oct 2022 13:31:39 -0400 Subject: [PATCH 11/19] wrong scope --- .../edu/harvard/iq/dataverse/search/IndexServiceBean.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java b/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java index 05947ee1224..6c4fb3f1332 100644 --- a/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java @@ -1005,11 +1005,10 @@ public SolrInputDocuments toSolrDocs(IndexableDataset indexableDataset, Set Date: Fri, 14 Oct 2022 16:50:21 -0400 Subject: [PATCH 12/19] add geo_point and geo_radius #8239 --- doc/sphinx-guides/source/api/search.rst | 2 ++ .../edu/harvard/iq/dataverse/api/Search.java | 21 ++++++++++++- .../search/SearchIncludeFragment.java | 4 +-- .../dataverse/search/SearchServiceBean.java | 25 +++++++++++++--- .../savedsearch/SavedSearchServiceBean.java | 4 ++- .../iq/dataverse/api/DataversesIT.java | 30 ++++++++++++++++++- 6 files changed, 77 insertions(+), 9 deletions(-) diff --git a/doc/sphinx-guides/source/api/search.rst b/doc/sphinx-guides/source/api/search.rst index fdebfdb8b10..c4e62e05bb7 100755 --- a/doc/sphinx-guides/source/api/search.rst +++ b/doc/sphinx-guides/source/api/search.rst @@ -35,6 +35,8 @@ show_relevance boolean Whether or not to show details of which fields were ma show_facets boolean Whether or not to show facets that can be operated on by the "fq" parameter. False by default. See :ref:`advanced search example `. fq string A filter query on the search term. Multiple "fq" parameters can be used. See :ref:`advanced search example `. show_entity_ids boolean Whether or not to show the database IDs of the search results (for developer use). +geo_point string Latitude and longitude in the form ``geo_point=42.3,-71.1``. +geo_radius string Radial distance in kilometers such as ``geo_radius=5``. metadata_fields string Includes the requested fields for each dataset in the response. Multiple "metadata_fields" parameters can be used to include several fields. The value must be in the form "{metadata_block_name}:{field_name}" to include a specific field from a metadata block (see :ref:`example `) or "{metadata_field_set_name}:\*" to include all the fields for a metadata block (see :ref:`example `). "{field_name}" cannot be a subfield of a compound field. If "{field_name}" is a compound field, all subfields are included. =============== ======= =========== diff --git a/src/main/java/edu/harvard/iq/dataverse/api/Search.java b/src/main/java/edu/harvard/iq/dataverse/api/Search.java index 71cb59ff62a..737fc7d1e20 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Search.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Search.java @@ -72,6 +72,8 @@ public Response search( @QueryParam("show_my_data") boolean showMyData, @QueryParam("query_entities") boolean queryEntities, @QueryParam("metadata_fields") List metadataFields, + @QueryParam("geo_point") String geoPointRequested, + @QueryParam("geo_radius") String geoRadiusRequested, @Context HttpServletResponse response ) { @@ -87,6 +89,8 @@ public Response search( // sanity checking on user-supplied arguments SortBy sortBy; int numResultsPerPage; + String geoPoint; + String geoRadius; List dataverseSubtrees = new ArrayList<>(); try { @@ -119,6 +123,9 @@ public Response search( throw new IOException("Filter is empty, which should never happen, as this allows unfettered searching of our index"); } + geoPoint = getGeoPoint(geoPointRequested); + geoRadius = getGeoRadius(geoRadiusRequested); + } catch (Exception ex) { return error(Response.Status.BAD_REQUEST, ex.getLocalizedMessage()); } @@ -137,7 +144,9 @@ public Response search( paginationStart, dataRelatedToMe, numResultsPerPage, - true //SEK get query entities always for search API additional Dataset Information 6300 12/6/2019 + true, //SEK get query entities always for search API additional Dataset Information 6300 12/6/2019 + geoPoint, + geoRadius ); } catch (SearchException ex) { Throwable cause = ex; @@ -340,4 +349,14 @@ private Dataverse getSubtree(String alias) throws Exception { } } + private String getGeoPoint(String geoPointRequested) { + // TODO add error checking + return geoPointRequested; + } + + private String getGeoRadius(String geoRadiusRequested) { + // TODO add error checking + return geoRadiusRequested; + } + } diff --git a/src/main/java/edu/harvard/iq/dataverse/search/SearchIncludeFragment.java b/src/main/java/edu/harvard/iq/dataverse/search/SearchIncludeFragment.java index 9bb83c88add..2b40347828a 100644 --- a/src/main/java/edu/harvard/iq/dataverse/search/SearchIncludeFragment.java +++ b/src/main/java/edu/harvard/iq/dataverse/search/SearchIncludeFragment.java @@ -355,7 +355,7 @@ The real issue here (https://github.com/IQSS/dataverse/issues/7304) is caused DataverseRequest dataverseRequest = new DataverseRequest(session.getUser(), httpServletRequest); List dataverses = new ArrayList<>(); dataverses.add(dataverse); - solrQueryResponse = searchService.search(dataverseRequest, dataverses, queryToPassToSolr, filterQueriesFinal, sortField, sortOrder.toString(), paginationStart, onlyDataRelatedToMe, numRows, false); + solrQueryResponse = searchService.search(dataverseRequest, dataverses, queryToPassToSolr, filterQueriesFinal, sortField, sortOrder.toString(), paginationStart, onlyDataRelatedToMe, numRows, false, null, null); if (solrQueryResponse.hasError()){ logger.info(solrQueryResponse.getError()); setSolrErrorEncountered(true); @@ -363,7 +363,7 @@ The real issue here (https://github.com/IQSS/dataverse/issues/7304) is caused // This 2nd search() is for populating the "type" ("dataverse", "dataset", "file") facets: -- L.A. // (why exactly do we need it, again?) // To get the counts we display in the types facets particulary for unselected types - SEK 08/25/2021 - solrQueryResponseAllTypes = searchService.search(dataverseRequest, dataverses, queryToPassToSolr, filterQueriesFinalAllTypes, sortField, sortOrder.toString(), paginationStart, onlyDataRelatedToMe, numRows, false); + solrQueryResponseAllTypes = searchService.search(dataverseRequest, dataverses, queryToPassToSolr, filterQueriesFinalAllTypes, sortField, sortOrder.toString(), paginationStart, onlyDataRelatedToMe, numRows, false, null, null); if (solrQueryResponse.hasError()){ logger.info(solrQueryResponse.getError()); setSolrErrorEncountered(true); diff --git a/src/main/java/edu/harvard/iq/dataverse/search/SearchServiceBean.java b/src/main/java/edu/harvard/iq/dataverse/search/SearchServiceBean.java index ca158198204..aee0465ddb1 100644 --- a/src/main/java/edu/harvard/iq/dataverse/search/SearchServiceBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/search/SearchServiceBean.java @@ -100,7 +100,7 @@ public class SearchServiceBean { * @throws SearchException */ public SolrQueryResponse search(DataverseRequest dataverseRequest, List dataverses, String query, List filterQueries, String sortField, String sortOrder, int paginationStart, boolean onlyDatatRelatedToMe, int numResultsPerPage) throws SearchException { - return search(dataverseRequest, dataverses, query, filterQueries, sortField, sortOrder, paginationStart, onlyDatatRelatedToMe, numResultsPerPage, true); + return search(dataverseRequest, dataverses, query, filterQueries, sortField, sortOrder, paginationStart, onlyDatatRelatedToMe, numResultsPerPage, true, null, null); } /** @@ -121,10 +121,24 @@ public SolrQueryResponse search(DataverseRequest dataverseRequest, List dataverses, String query, List filterQueries, String sortField, String sortOrder, int paginationStart, boolean onlyDatatRelatedToMe, int numResultsPerPage, boolean retrieveEntities) throws SearchException { + public SolrQueryResponse search( + DataverseRequest dataverseRequest, + List dataverses, + String query, + List filterQueries, + String sortField, String sortOrder, + int paginationStart, + boolean onlyDatatRelatedToMe, + int numResultsPerPage, + boolean retrieveEntities, + String geoPoint, + String geoRadius + ) throws SearchException { if (paginationStart < 0) { throw new IllegalArgumentException("paginationStart must be 0 or greater"); @@ -204,8 +218,11 @@ public SolrQueryResponse search(DataverseRequest dataverseRequest, List Date: Mon, 24 Oct 2022 20:37:54 -0400 Subject: [PATCH 13/19] move hard coded strings to SearchFields class #8239 --- .../edu/harvard/iq/dataverse/search/IndexServiceBean.java | 4 ++-- .../java/edu/harvard/iq/dataverse/search/SearchFields.java | 5 +++++ .../edu/harvard/iq/dataverse/search/SearchServiceBean.java | 3 ++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java b/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java index 6c4fb3f1332..8bd3f7f443d 100644 --- a/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java @@ -1003,12 +1003,12 @@ public SolrInputDocuments toSolrDocs(IndexableDataset indexableDataset, Set Date: Mon, 24 Oct 2022 22:03:38 -0400 Subject: [PATCH 14/19] add geospatial search test #8239 --- .../harvard/iq/dataverse/api/SearchIT.java | 154 +++++++++++++++++- .../edu/harvard/iq/dataverse/api/UtilIT.java | 22 +++ 2 files changed, 175 insertions(+), 1 deletion(-) diff --git a/src/test/java/edu/harvard/iq/dataverse/api/SearchIT.java b/src/test/java/edu/harvard/iq/dataverse/api/SearchIT.java index 789b60a34e7..0f2c77de717 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/SearchIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/SearchIT.java @@ -36,6 +36,7 @@ import org.junit.After; import static org.junit.Assert.assertNotEquals; import static java.lang.Thread.sleep; +import javax.json.JsonObjectBuilder; public class SearchIT { @@ -1084,7 +1085,158 @@ public void testSubtreePermissions() { .statusCode(OK.getStatusCode()) .body("data.total_count", CoreMatchers.equalTo(1)); } - + + @Test + public void testGeospatialSearch() { + + Response createUser = UtilIT.createRandomUser(); + createUser.prettyPrint(); + String username = UtilIT.getUsernameFromResponse(createUser); + String apiToken = UtilIT.getApiTokenFromResponse(createUser); + + Response createDataverseResponse = UtilIT.createRandomDataverse(apiToken); + createDataverseResponse.prettyPrint(); + String dataverseAlias = UtilIT.getAliasFromResponse(createDataverseResponse); + + Response setMetadataBlocks = UtilIT.setMetadataBlocks(dataverseAlias, Json.createArrayBuilder().add("citation").add("geospatial"), apiToken); + setMetadataBlocks.prettyPrint(); + setMetadataBlocks.then().assertThat().statusCode(OK.getStatusCode()); + + JsonObjectBuilder datasetJson = Json.createObjectBuilder() + .add("datasetVersion", Json.createObjectBuilder() + .add("metadataBlocks", Json.createObjectBuilder() + .add("citation", Json.createObjectBuilder() + .add("fields", Json.createArrayBuilder() + .add(Json.createObjectBuilder() + .add("typeName", "title") + .add("value", "Dataverse HQ") + .add("typeClass", "primitive") + .add("multiple", false) + ) + .add(Json.createObjectBuilder() + .add("value", Json.createArrayBuilder() + .add(Json.createObjectBuilder() + .add("authorName", + Json.createObjectBuilder() + .add("value", "Simpson, Homer") + .add("typeClass", "primitive") + .add("multiple", false) + .add("typeName", "authorName")) + ) + ) + .add("typeClass", "compound") + .add("multiple", true) + .add("typeName", "author") + ) + .add(Json.createObjectBuilder() + .add("value", Json.createArrayBuilder() + .add(Json.createObjectBuilder() + .add("datasetContactEmail", + Json.createObjectBuilder() + .add("value", "hsimpson@mailinator.com") + .add("typeClass", "primitive") + .add("multiple", false) + .add("typeName", "datasetContactEmail")) + ) + ) + .add("typeClass", "compound") + .add("multiple", true) + .add("typeName", "datasetContact") + ) + .add(Json.createObjectBuilder() + .add("value", Json.createArrayBuilder() + .add(Json.createObjectBuilder() + .add("dsDescriptionValue", + Json.createObjectBuilder() + .add("value", "Headquarters for Dataverse.") + .add("typeClass", "primitive") + .add("multiple", false) + .add("typeName", "dsDescriptionValue")) + ) + ) + .add("typeClass", "compound") + .add("multiple", true) + .add("typeName", "dsDescription") + ) + .add(Json.createObjectBuilder() + .add("value", Json.createArrayBuilder() + .add("Other") + ) + .add("typeClass", "controlledVocabulary") + .add("multiple", true) + .add("typeName", "subject") + ) + ) + ) + .add("geospatial", Json.createObjectBuilder() + .add("fields", Json.createArrayBuilder() + .add(Json.createObjectBuilder() + .add("typeName", "geographicBoundingBox") + .add("typeClass", "compound") + .add("multiple", true) + .add("value", Json.createArrayBuilder() + .add(Json.createObjectBuilder() + // The box is roughly on Cambridge, MA + // See https://linestrings.com/bbox/#-71.187346,42.33661,-71.043056,42.409599 + .add("westLongitude", + Json.createObjectBuilder() + .add("value", "-71.187346") + .add("typeClass", "primitive") + .add("multiple", false) + .add("typeName", "westLongitude") + ) + .add("southLongitude", + Json.createObjectBuilder() + .add("value", "42.33661") + .add("typeClass", "primitive") + .add("multiple", false) + .add("typeName", "southLongitude") + ) + .add("eastLongitude", + Json.createObjectBuilder() + .add("value", "-71.043056") + .add("typeClass", "primitive") + .add("multiple", false) + .add("typeName", "eastLongitude") + ) + .add("northLongitude", + Json.createObjectBuilder() + .add("value", "42.409599") + .add("typeClass", "primitive") + .add("multiple", false) + .add("typeName", "northLongitude") + ) + ) + ) + ) + ) + ) + )); + + Response createDatasetResponse = UtilIT.createDataset(dataverseAlias, datasetJson, apiToken); + createDatasetResponse.prettyPrint(); + Integer datasetId = UtilIT.getDatasetIdFromResponse(createDatasetResponse); + String datasetPid = JsonPath.from(createDatasetResponse.getBody().asString()).getString("data.persistentId"); + + // Plymouth rock (41.9580775,-70.6621063) is within 50 km of Cambridge. Hit. + Response search1 = UtilIT.search("id:dataset_" + datasetId + "_draft", apiToken, "&show_entity_ids=true&geo_point=41.9580775,-70.6621063&geo_radius=50"); + search1.prettyPrint(); + search1.then().assertThat() + .body("data.total_count", CoreMatchers.is(1)) + .body("data.count_in_response", CoreMatchers.is(1)) + .body("data.items[0].entity_id", CoreMatchers.is(datasetId)) + .statusCode(OK.getStatusCode()); + + // Plymouth rock (41.9580775,-70.6621063) is not within 1 km of Cambridge. Miss. + Response search2 = UtilIT.search("id:dataset_" + datasetId + "_draft", apiToken, "&geo_point=41.9580775,-70.6621063&geo_radius=1"); + search2.prettyPrint(); + search2.then().assertThat() + .body("data.total_count", CoreMatchers.is(0)) + .body("data.count_in_response", CoreMatchers.is(0)) + .statusCode(OK.getStatusCode()); + + } + @After public void tearDownDataverse() { File treesThumb = new File("scripts/search/data/binary/trees.png.thumb48"); diff --git a/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java b/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java index 7107ee783d7..3bffdaf5188 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java @@ -407,6 +407,20 @@ static Response createDatasetViaNativeApi(String dataverseAlias, String pathToJs return createDatasetResponse; } + static Response createDataset(String dataverseAlias, JsonObjectBuilder datasetJson, String apiToken) { + return createDataset(dataverseAlias, datasetJson.build().toString(), apiToken); + } + + static Response createDataset(String dataverseAlias, String datasetJson, String apiToken) { + System.out.println("creating with " + datasetJson); + Response createDatasetResponse = given() + .header(API_TOKEN_HTTP_HEADER, apiToken) + .body(datasetJson) + .contentType("application/json") + .post("/api/dataverses/" + dataverseAlias + "/datasets"); + return createDatasetResponse; + } + static String getDatasetJson(String pathToJsonFile) { File datasetVersionJson = new File(pathToJsonFile); try { @@ -544,6 +558,14 @@ static Response loadMetadataBlock(String apiToken, byte[] body) { .post("/api/admin/datasetfield/load"); } + static Response setMetadataBlocks(String dataverseAlias, JsonArrayBuilder blocks, String apiToken) { + return given() + .header(API_TOKEN_HTTP_HEADER, apiToken) + .contentType("application/json") + .body(blocks.build().toString()) + .post("/api/dataverses/" + dataverseAlias + "/metadatablocks"); + } + static private String getDatasetXml(String title, String author, String description) { String nullLicense = null; String nullRights = null; From e5187b2af4e1915f5eff4575c6b2ccadd62d150a Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Tue, 25 Oct 2022 14:28:40 -0400 Subject: [PATCH 15/19] Avoid DatasetCreate exception with only one coordinate #8239 With only westLongitude added and the other three coordinates left empty, we were getting the following exception. A null check was added to prevent this. Command [DatasetCreate dataset:132] failed: Exception thrown from bean: javax.ejb.EJBTransactionRolledbackException: Exception thrown from bean: org.apache.solr.client.solrj.impl.HttpSolrClient$RemoteSolrException: Error from server at http://localhost:8983/solr/collection1: ERROR: [doc=dataset_132_draft] Error adding field 'solr_bboxtype'='ENVELOPE(null,null,null,null)' msg=Unable to parse shape given formats "lat,lon", "x y" or as WKT because java.text.ParseException: Expected a number input: ENVELOPE(null,null,null,null) --- .../edu/harvard/iq/dataverse/search/IndexServiceBean.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java b/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java index 8bd3f7f443d..f5a5570c831 100644 --- a/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java @@ -1008,7 +1008,10 @@ public SolrInputDocuments toSolrDocs(IndexableDataset indexableDataset, Set Date: Tue, 25 Oct 2022 16:14:14 -0400 Subject: [PATCH 16/19] rename solr_srpt to geolocation and solr_bboxtype to boundingBox #8239 --- conf/solr/8.11.1/schema.xml | 12 +++++++----- doc/sphinx-guides/source/api/search.rst | 2 +- .../iq/dataverse/search/IndexServiceBean.java | 4 ++-- .../harvard/iq/dataverse/search/SearchFields.java | 6 +++--- .../iq/dataverse/search/SearchServiceBean.java | 2 +- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/conf/solr/8.11.1/schema.xml b/conf/solr/8.11.1/schema.xml index 10f1d8f1f4f..655cf1bc3cc 100644 --- a/conf/solr/8.11.1/schema.xml +++ b/conf/solr/8.11.1/schema.xml @@ -228,6 +228,11 @@ + + + + + - - + @@ -1107,7 +1109,7 @@ --> - + diff --git a/doc/sphinx-guides/source/api/search.rst b/doc/sphinx-guides/source/api/search.rst index c4e62e05bb7..c2311ead089 100755 --- a/doc/sphinx-guides/source/api/search.rst +++ b/doc/sphinx-guides/source/api/search.rst @@ -742,7 +742,7 @@ For example, a query of the form .. code-block:: none - q=*.*&fq={!bbox sfield=solr_srpt}=&pt=10,10&d=5 + q=*.*&fq={!bbox sfield=geolocation}=&pt=10,10&d=5 would find datasets with information near the point latitude=10, longitude=10. diff --git a/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java b/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java index f5a5570c831..4661e9c1cd5 100644 --- a/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java @@ -1003,13 +1003,13 @@ public SolrInputDocuments toSolrDocs(IndexableDataset indexableDataset, Set Date: Wed, 26 Oct 2022 15:41:41 -0400 Subject: [PATCH 17/19] add error checking for geo_point and geo_radius #8239 --- .../edu/harvard/iq/dataverse/api/Search.java | 14 ++++-- .../iq/dataverse/search/SearchUtil.java | 46 ++++++++++++++++++- .../harvard/iq/dataverse/api/SearchIT.java | 38 +++++++++++++-- .../iq/dataverse/search/SearchUtilTest.java | 38 +++++++++++++++ 4 files changed, 127 insertions(+), 9 deletions(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/api/Search.java b/src/main/java/edu/harvard/iq/dataverse/api/Search.java index 737fc7d1e20..cef509b1ec5 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Search.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Search.java @@ -126,6 +126,14 @@ public Response search( geoPoint = getGeoPoint(geoPointRequested); geoRadius = getGeoRadius(geoRadiusRequested); + if (geoPoint != null && geoRadius == null) { + return error(Response.Status.BAD_REQUEST, "If you supply geo_point you must also supply geo_radius."); + } + + if (geoRadius != null && geoPoint == null) { + return error(Response.Status.BAD_REQUEST, "If you supply geo_radius you must also supply geo_point."); + } + } catch (Exception ex) { return error(Response.Status.BAD_REQUEST, ex.getLocalizedMessage()); } @@ -350,13 +358,11 @@ private Dataverse getSubtree(String alias) throws Exception { } private String getGeoPoint(String geoPointRequested) { - // TODO add error checking - return geoPointRequested; + return SearchUtil.getGeoPoint(geoPointRequested); } private String getGeoRadius(String geoRadiusRequested) { - // TODO add error checking - return geoRadiusRequested; + return SearchUtil.getGeoRadius(geoRadiusRequested); } } diff --git a/src/main/java/edu/harvard/iq/dataverse/search/SearchUtil.java b/src/main/java/edu/harvard/iq/dataverse/search/SearchUtil.java index c226d77f885..8a1045a842c 100644 --- a/src/main/java/edu/harvard/iq/dataverse/search/SearchUtil.java +++ b/src/main/java/edu/harvard/iq/dataverse/search/SearchUtil.java @@ -181,5 +181,49 @@ public static String constructQuery(List queryStrings, boolean isAnd, bo return queryBuilder.toString().trim(); } - + + /** + * @return Null if supplied point is null or whitespace. + * @throws IllegalArgumentException If the lat/long is not separated by a + * comma. + * @throws NumberFormatException If the lat/long values are not numbers. + */ + public static String getGeoPoint(String userSuppliedGeoPoint) throws IllegalArgumentException, NumberFormatException { + if (userSuppliedGeoPoint == null || userSuppliedGeoPoint.isBlank()) { + return null; + } + String[] parts = userSuppliedGeoPoint.split(","); + // We'll supply our own errors but Solr gives a decent one: + // "Point must be in 'lat, lon' or 'x y' format: 42.3;-71.1" + if (parts.length != 2) { + String msg = "Must contain a single comma to separate latitude and longitude."; + throw new IllegalArgumentException(msg); + } + float latitude = Float.parseFloat(parts[0]); + float longitude = Float.parseFloat(parts[1]); + return latitude + "," + longitude; + } + + /** + * @return Null if supplied radius is null or whitespace. + * @throws NumberFormatException If the radius is not a positive number. + */ + public static String getGeoRadius(String userSuppliedGeoRadius) throws NumberFormatException { + if (userSuppliedGeoRadius == null || userSuppliedGeoRadius.isBlank()) { + return null; + } + float radius = 0; + try { + radius = Float.parseFloat(userSuppliedGeoRadius); + } catch (NumberFormatException ex) { + String msg = "Non-number radius supplied."; + throw new NumberFormatException(msg); + } + if (radius <= 0) { + String msg = "The supplied radius must be greater than zero."; + throw new NumberFormatException(msg); + } + return userSuppliedGeoRadius; + } + } diff --git a/src/test/java/edu/harvard/iq/dataverse/api/SearchIT.java b/src/test/java/edu/harvard/iq/dataverse/api/SearchIT.java index 0f2c77de717..fc3b911c0a5 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/SearchIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/SearchIT.java @@ -17,6 +17,7 @@ import java.io.UnsupportedEncodingException; import java.util.Base64; import javax.json.JsonArray; +import static javax.ws.rs.core.Response.Status.BAD_REQUEST; import static javax.ws.rs.core.Response.Status.OK; import static javax.ws.rs.core.Response.Status.FORBIDDEN; import org.hamcrest.CoreMatchers; @@ -1222,18 +1223,47 @@ public void testGeospatialSearch() { Response search1 = UtilIT.search("id:dataset_" + datasetId + "_draft", apiToken, "&show_entity_ids=true&geo_point=41.9580775,-70.6621063&geo_radius=50"); search1.prettyPrint(); search1.then().assertThat() + .statusCode(OK.getStatusCode()) .body("data.total_count", CoreMatchers.is(1)) .body("data.count_in_response", CoreMatchers.is(1)) - .body("data.items[0].entity_id", CoreMatchers.is(datasetId)) - .statusCode(OK.getStatusCode()); + .body("data.items[0].entity_id", CoreMatchers.is(datasetId)); // Plymouth rock (41.9580775,-70.6621063) is not within 1 km of Cambridge. Miss. Response search2 = UtilIT.search("id:dataset_" + datasetId + "_draft", apiToken, "&geo_point=41.9580775,-70.6621063&geo_radius=1"); search2.prettyPrint(); search2.then().assertThat() + .statusCode(OK.getStatusCode()) .body("data.total_count", CoreMatchers.is(0)) - .body("data.count_in_response", CoreMatchers.is(0)) - .statusCode(OK.getStatusCode()); + .body("data.count_in_response", CoreMatchers.is(0)); + + } + + @Test + public void testGeospatialSearchInvalid() { + + Response noRadius = UtilIT.search("*", null, "&geo_point=40,60"); + noRadius.prettyPrint(); + noRadius.then().assertThat() + .statusCode(BAD_REQUEST.getStatusCode()) + .body("message", CoreMatchers.equalTo("If you supply geo_point you must also supply geo_radius.")); + + Response noPoint = UtilIT.search("*", null, "&geo_radius=5"); + noPoint.prettyPrint(); + noPoint.then().assertThat() + .statusCode(BAD_REQUEST.getStatusCode()) + .body("message", CoreMatchers.equalTo("If you supply geo_radius you must also supply geo_point.")); + + Response junkPoint = UtilIT.search("*", null, "&geo_point=junk&geo_radius=5"); + junkPoint.prettyPrint(); + junkPoint.then().assertThat() + .statusCode(BAD_REQUEST.getStatusCode()) + .body("message", CoreMatchers.equalTo("Must contain a single comma to separate latitude and longitude.")); + + Response junkRadius = UtilIT.search("*", null, "&geo_point=40,60&geo_radius=junk"); + junkRadius.prettyPrint(); + junkRadius.then().assertThat() + .statusCode(BAD_REQUEST.getStatusCode()) + .body("message", CoreMatchers.equalTo("Non-number radius supplied.")); } diff --git a/src/test/java/edu/harvard/iq/dataverse/search/SearchUtilTest.java b/src/test/java/edu/harvard/iq/dataverse/search/SearchUtilTest.java index 525e03f8302..33f50c9a4c0 100644 --- a/src/test/java/edu/harvard/iq/dataverse/search/SearchUtilTest.java +++ b/src/test/java/edu/harvard/iq/dataverse/search/SearchUtilTest.java @@ -91,4 +91,42 @@ public void testdetermineFinalQuery() { assertEquals("*", SearchUtil.determineFinalQuery("")); assertEquals("foo", SearchUtil.determineFinalQuery("foo")); } + + @Test + public void testGetGeoPoint() { + // valid + assertEquals("42.3,-71.1", SearchUtil.getGeoPoint("42.3,-71.1")); + // user doesn't want geospatial search + assertEquals(null, SearchUtil.getGeoPoint(null)); + // invalid + assertThrows(IllegalArgumentException.class, () -> { + SearchUtil.getGeoRadius("42.3;-71.1"); + }, "Must have a comma."); + assertThrows(IllegalArgumentException.class, () -> { + SearchUtil.getGeoRadius("-71.187346,42.33661,-71.043056,42.409599"); + }, "Must have only one comma."); + assertThrows(IllegalArgumentException.class, () -> { + SearchUtil.getGeoRadius("junk"); + }, "Must have a comma."); + assertThrows(NumberFormatException.class, () -> { + SearchUtil.getGeoRadius("somejunk,morejunk"); + }, "Must be numbers."); + } + + @Test + public void testGetGeoRadius() { + // valid + assertEquals("5", SearchUtil.getGeoRadius("5")); + assertEquals("1.5", SearchUtil.getGeoRadius("1.5")); + // user doesn't want geospatial search + assertEquals(null, SearchUtil.getGeoRadius(null)); + assertEquals(null, SearchUtil.getGeoRadius("")); + // invalid + assertThrows(NumberFormatException.class, () -> { + SearchUtil.getGeoRadius("nonNumber"); + }, "Must be a number."); + assertThrows(NumberFormatException.class, () -> { + SearchUtil.getGeoRadius("-1"); + }, "Must be greater than zero."); + } } From 6e7499e7be3586b1129d9598716e4c2e6ba4b27d Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Wed, 26 Oct 2022 17:34:55 -0400 Subject: [PATCH 18/19] update docs and release note (supported via API) #8239 --- doc/release-notes/8239-geospatial-indexing.md | 6 +++++- doc/sphinx-guides/source/api/search.rst | 18 ++---------------- .../source/user/find-use-data.rst | 7 +++++++ 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/doc/release-notes/8239-geospatial-indexing.md b/doc/release-notes/8239-geospatial-indexing.md index 3e6ba0e7a07..165cb9031ba 100644 --- a/doc/release-notes/8239-geospatial-indexing.md +++ b/doc/release-notes/8239-geospatial-indexing.md @@ -1 +1,5 @@ -Support for indexing the Geographic Bounding Box fields from the Geospatial metadata block has been added. This allows trusted applications with access to solr to perform geospatial queries to find datasets, e.g. those near a given point. This is also a step towards enabling geospatial queries via the Dataverse API and UI. +Support for indexing the "Geographic Bounding Box" fields ("West Longitude", "East Longitude", "North Latitude", and "South Latitude") from the Geospatial metadata block has been added. + +Geospatial search is supported but only via API using two new parameters: `geo_point` and `geo_radius`. + +A Solr schema update is required. diff --git a/doc/sphinx-guides/source/api/search.rst b/doc/sphinx-guides/source/api/search.rst index c2311ead089..b941064f173 100755 --- a/doc/sphinx-guides/source/api/search.rst +++ b/doc/sphinx-guides/source/api/search.rst @@ -35,8 +35,8 @@ show_relevance boolean Whether or not to show details of which fields were ma show_facets boolean Whether or not to show facets that can be operated on by the "fq" parameter. False by default. See :ref:`advanced search example `. fq string A filter query on the search term. Multiple "fq" parameters can be used. See :ref:`advanced search example `. show_entity_ids boolean Whether or not to show the database IDs of the search results (for developer use). -geo_point string Latitude and longitude in the form ``geo_point=42.3,-71.1``. -geo_radius string Radial distance in kilometers such as ``geo_radius=5``. +geo_point string Latitude and longitude in the form ``geo_point=42.3,-71.1``. You must supply ``geo_radius`` as well. See also :ref:`geospatial-search`. +geo_radius string Radial distance in kilometers from ``geo_point`` (which must be supplied as well) such as ``geo_radius=1.5``. metadata_fields string Includes the requested fields for each dataset in the response. Multiple "metadata_fields" parameters can be used to include several fields. The value must be in the form "{metadata_block_name}:{field_name}" to include a specific field from a metadata block (see :ref:`example `) or "{metadata_field_set_name}:\*" to include all the fields for a metadata block (see :ref:`example `). "{field_name}" cannot be a subfield of a compound field. If "{field_name}" is a compound field, all subfields are included. =============== ======= =========== @@ -732,17 +732,3 @@ Output from iteration example CORS - - -Geospatial Indexing -------------------- - -Dataverse indexes the Geospatial Bounding Box field from the Geospatial metadatablock as a solr.BBoxField enabling `Spatial Search `_. This capability is not yet exposed through the Dataverse API or UI but can be accessed by trusted applications with direct solr access. -For example, a query of the form - -.. code-block:: none - - q=*.*&fq={!bbox sfield=geolocation}=&pt=10,10&d=5 - - -would find datasets with information near the point latitude=10, longitude=10. diff --git a/doc/sphinx-guides/source/user/find-use-data.rst b/doc/sphinx-guides/source/user/find-use-data.rst index 42e1a2b23d4..2e82a1482b4 100755 --- a/doc/sphinx-guides/source/user/find-use-data.rst +++ b/doc/sphinx-guides/source/user/find-use-data.rst @@ -39,6 +39,13 @@ enter search terms for Dataverse collections, dataset metadata (citation and dom metadata. If you are searching for tabular data files you can also search at the variable level for name and label. To find out more about what each field searches, hover over the field name for a detailed description of the field. +.. _geospatial-search: + +Geospatial Search +----------------- + +Geospatial search is available from the :doc:`/api/search` (look for "geo" parameters). The metadata fields that are geospatially indexed are "West Longitude", "East Longitude", "North Latitude", and "South Latitude" from the "Geographic Bounding Box" field in the "Geospatial Metadata" block. + Browsing a Dataverse Installation --------------------------------- From 364e347e4a9342bdc02962f14e937369a67969b0 Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Mon, 21 Nov 2022 09:14:26 -0500 Subject: [PATCH 19/19] test invalid lat/long (too large) #8239 --- src/test/java/edu/harvard/iq/dataverse/api/SearchIT.java | 7 +++++++ .../edu/harvard/iq/dataverse/search/SearchUtilTest.java | 2 ++ 2 files changed, 9 insertions(+) diff --git a/src/test/java/edu/harvard/iq/dataverse/api/SearchIT.java b/src/test/java/edu/harvard/iq/dataverse/api/SearchIT.java index fc3b911c0a5..61a55a88a3b 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/SearchIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/SearchIT.java @@ -1259,6 +1259,13 @@ public void testGeospatialSearchInvalid() { .statusCode(BAD_REQUEST.getStatusCode()) .body("message", CoreMatchers.equalTo("Must contain a single comma to separate latitude and longitude.")); + Response pointLatLongTooLarge = UtilIT.search("*", null, "&geo_point=999,999&geo_radius=5"); + pointLatLongTooLarge.prettyPrint(); + pointLatLongTooLarge.then().assertThat() + // "Search Syntax Error: Error from server at http://localhost:8983/solr/collection1: + // Can't parse point '999.0,999.0' because: Bad X value 999.0 is not in boundary Rect(minX=-180.0,maxX=180.0,minY=-90.0,maxY=90.0)" + .statusCode(BAD_REQUEST.getStatusCode()); + Response junkRadius = UtilIT.search("*", null, "&geo_point=40,60&geo_radius=junk"); junkRadius.prettyPrint(); junkRadius.then().assertThat() diff --git a/src/test/java/edu/harvard/iq/dataverse/search/SearchUtilTest.java b/src/test/java/edu/harvard/iq/dataverse/search/SearchUtilTest.java index 33f50c9a4c0..6e2fb762c3b 100644 --- a/src/test/java/edu/harvard/iq/dataverse/search/SearchUtilTest.java +++ b/src/test/java/edu/harvard/iq/dataverse/search/SearchUtilTest.java @@ -111,6 +111,8 @@ public void testGetGeoPoint() { assertThrows(NumberFormatException.class, () -> { SearchUtil.getGeoRadius("somejunk,morejunk"); }, "Must be numbers."); + // invalid but let it go, it's handled by Solr, which throws an informative exception + assertEquals("999.0,-999.0", SearchUtil.getGeoPoint("999,-999")); } @Test