Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support geopoint type for Tableau #1910

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/user/dql/metadata.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Example 1: Show All Indices Information
SQL query::

os> SHOW TABLES LIKE '%'
fetched rows / total rows = 9/9
fetched rows / total rows = 10/10
+----------------+---------------+-----------------+--------------+-----------+------------+--------------+-------------+-----------------------------+------------------+
| TABLE_CAT | TABLE_SCHEM | TABLE_NAME | TABLE_TYPE | REMARKS | TYPE_CAT | TYPE_SCHEM | TYPE_NAME | SELF_REFERENCING_COL_NAME | REF_GENERATION |
|----------------+---------------+-----------------+--------------+-----------+------------+--------------+-------------+-----------------------------+------------------|
Expand All @@ -44,6 +44,7 @@ SQL query::
| docTestCluster | null | accounts | BASE TABLE | null | null | null | null | null | null |
| docTestCluster | null | apache | BASE TABLE | null | null | null | null | null | null |
| docTestCluster | null | books | BASE TABLE | null | null | null | null | null | null |
| docTestCluster | null | geopoint | BASE TABLE | null | null | null | null | null | null |
| docTestCluster | null | nested | BASE TABLE | null | null | null | null | null | null |
| docTestCluster | null | nyc_taxi | BASE TABLE | null | null | null | null | null | null |
| docTestCluster | null | people | BASE TABLE | null | null | null | null | null | null |
Expand Down
16 changes: 16 additions & 0 deletions docs/user/general/datatypes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ The table below list the mapping between OpenSearch Data Type, OpenSearch SQL Da
+-----------------+---------------------+-----------+
| nested | array | STRUCT |
+-----------------+---------------------+-----------+
| geo_point | geo_point | STRUCT |
+-----------------+---------------------+-----------+

Notes:
* Not all the OpenSearch SQL Type has correspond OpenSearch Type. e.g. data and time. To use function which required such data type, user should explicitly convert the data type.
Expand Down Expand Up @@ -427,3 +429,17 @@ A boolean can be represented by constant value ``TRUE`` or ``FALSE``. Besides, c
|--------+---------+---------------------------+----------------------------|
| True | False | True | False |
+--------+---------+---------------------------+----------------------------+

Geopoint Data Types
==================

A geopoint has a latitude and a longitude property. Although OpenSearch `supports multiple formats <https://opensearch.org/docs/2.3/opensearch/supported-field-types/geo-point/>`_, the SQL plugin currently only supports the format :code:`{"lat": number, "lon": number}`. The geopoint object can be queried or lat and lon can be specified using dot notation. For example, ::

os> SELECT geo_point_object, geo_point_object.lat, geo_point_object.lon FROM geopoint;
fetched rows / total rows = 2/2
+----------------------------------+------------------------+------------------------+
| geo_point_object | geo_point_object.lat | geo_point_object.lon |
|----------------------------------+------------------------+------------------------|
| {'lat': 40.71, 'lon': 74.0} | 40.71 | 74.0 |
| {'lat': -33.852, 'lon': 151.216} | -33.852 | 151.216 |
+----------------------------------+------------------------+------------------------+
2 changes: 2 additions & 0 deletions doctest/test_data/geopoint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{"geo_point_object": {"lat": 40.71, "lon": 74.00}}
{"geo_point_object": {"lat": -33.852, "lon": 151.216}}
4 changes: 3 additions & 1 deletion doctest/test_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
APACHE = "apache"
WILDCARD = "wildcard"
NESTED = "nested"
GEOPOINT = "geopoint"
DATASOURCES = ".ql-datasources"


Expand Down Expand Up @@ -97,6 +98,7 @@ def set_up_test_indices(test):
load_file("apache.json", index_name=APACHE)
load_file("wildcard.json", index_name=WILDCARD)
load_file("nested_objects.json", index_name=NESTED)
load_file("geopoint.json", index_name=GEOPOINT)
load_file("datasources.json", index_name=DATASOURCES)


Expand Down Expand Up @@ -126,7 +128,7 @@ def set_up(test):

def tear_down(test):
# drop leftover tables after each test
test_data_client.indices.delete(index=[ACCOUNTS, EMPLOYEES, PEOPLE, ACCOUNT2, NYC_TAXI, BOOKS, APACHE, WILDCARD, NESTED], ignore_unavailable=True)
test_data_client.indices.delete(index=[ACCOUNTS, EMPLOYEES, PEOPLE, ACCOUNT2, NYC_TAXI, BOOKS, APACHE, WILDCARD, NESTED, GEOPOINT], ignore_unavailable=True)


docsuite = partial(doctest.DocFileSuite,
Expand Down
9 changes: 9 additions & 0 deletions doctest/test_mapping/geopoint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"mappings": {
"properties": {
"geo_point_object": {
"type": "geo_point"
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -699,7 +699,12 @@ public enum Index {
TestsConstants.TEST_INDEX_NESTED_WITH_NULLS,
"multi_nested",
getNestedTypeIndexMapping(),
"src/test/resources/nested_with_nulls.json");
"src/test/resources/nested_with_nulls.json"),
GEOPOINT(
TestsConstants.TEST_INDEX_GEOPOINT,
"geopoint",
getMappingFile("geo_point_mapping.json"),
"src/test/resources/geo_point.json");

private final String name;
private final String type;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public class TestsConstants {
public static final String TEST_INDEX_WILDCARD = TEST_INDEX + "_wildcard";
public static final String TEST_INDEX_MULTI_NESTED_TYPE = TEST_INDEX + "_multi_nested";
public static final String TEST_INDEX_NESTED_WITH_NULLS = TEST_INDEX + "_nested_with_nulls";
public static final String TEST_INDEX_GEOPOINT = TEST_INDEX + "_geopoint";
public static final String DATASOURCES = ".ql-datasources";

public static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
Expand Down
129 changes: 129 additions & 0 deletions integ-test/src/test/java/org/opensearch/sql/sql/GeoPointIT.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.sql.sql;

import static org.opensearch.sql.legacy.TestsConstants.TEST_INDEX_GEOPOINT;
import static org.opensearch.sql.util.MatcherUtils.rows;
import static org.opensearch.sql.util.MatcherUtils.verifyDataRows;

import java.util.Map;
import org.json.JSONObject;
import org.junit.Test;
import org.opensearch.sql.legacy.SQLIntegTestCase;

public class GeoPointIT extends SQLIntegTestCase {
@Override
protected void init() throws Exception {
loadIndex(Index.GEOPOINT);
}

@Test
public void test_geo_point() {
String query = "SELECT geo_point_object FROM " + TEST_INDEX_GEOPOINT;
JSONObject result = executeJdbcRequest(query);
verifyDataRows(
result,
rows(new JSONObject(Map.of("lat", 40.71, "lon", 74))),
rows(
new JSONObject(
Map.of(
"lat", -33.85253637358241,
"lon", 151.21652352950258))),
rows(JSONObject.NULL));
}

@Test
public void test_geo_point_unsupported_format() {
String exceptionMessage =
" \"error\": {\n"
+ " \"reason\": \"There was internal problem at backend\",\n"
+ " \"details\": \"geo point must be in format of {\\\"lat\\\": number, \\\"lon\\\":"
+ " number}\",\n"
+ " \"type\": \"IllegalStateException\"\n"
+ " }";

String geohashQuery = "SELECT geo_point_geohash FROM " + TEST_INDEX_GEOPOINT;
Exception exception =
assertThrows(RuntimeException.class, () -> executeJdbcRequest(geohashQuery));
assertTrue(exception.getMessage().contains(exceptionMessage));

String geopointString = "SELECT geo_point_string FROM " + TEST_INDEX_GEOPOINT;
exception = assertThrows(RuntimeException.class, () -> executeJdbcRequest(geopointString));
assertTrue(exception.getMessage().contains(exceptionMessage));

String geopointArray = "SELECT geo_point_array FROM " + TEST_INDEX_GEOPOINT;
exception = assertThrows(RuntimeException.class, () -> executeJdbcRequest(geopointArray));
assertTrue(exception.getMessage().contains(exceptionMessage));

String geopointStringPoint = "SELECT geo_point_string_point FROM " + TEST_INDEX_GEOPOINT;
exception = assertThrows(RuntimeException.class, () -> executeJdbcRequest(geopointStringPoint));
assertTrue(exception.getMessage().contains(exceptionMessage));

String geopointGeoJSON = "SELECT geo_point_geojson FROM " + TEST_INDEX_GEOPOINT;
exception = assertThrows(RuntimeException.class, () -> executeJdbcRequest(geopointGeoJSON));
assertTrue(exception.getMessage().contains(exceptionMessage));
}

@Test
public void test_geo_point_in_objects() {
String query = "SELECT object.geo_point_object FROM " + TEST_INDEX_GEOPOINT;
JSONObject result = executeJdbcRequest(query);
verifyDataRows(
result,
rows(new JSONObject(Map.of("lat", 40.71, "lon", 74))),
rows(
new JSONObject(
Map.of(
"lat", -33.85253637358241,
"lon", 151.21652352950258))),
rows(JSONObject.NULL));
}

@Test
public void test_geo_point_lat_in_objects() {
String query = "SELECT object.geo_point_object.lat FROM " + TEST_INDEX_GEOPOINT;
JSONObject result = executeJdbcRequest(query);
verifyDataRows(result, rows(40.71), rows(-33.85253637358241), rows(JSONObject.NULL));
}

@Test
public void test_geo_point_lat_and_lon() {
String query = "SELECT geo_point_object.lat, geo_point_object.lon FROM " + TEST_INDEX_GEOPOINT;
JSONObject result = executeJdbcRequest(query);
verifyDataRows(
result,
rows(40.71, 74),
rows(-33.85253637358241, 151.21652352950258),
rows(JSONObject.NULL, JSONObject.NULL));
}

@Test
public void test_geo_point_object_with_lat_and_lon() {
String query =
"SELECT geo_point_object, geo_point_object.lat,"
+ " geo_point_object.lon FROM "
+ TEST_INDEX_GEOPOINT;
JSONObject result = executeJdbcRequest(query);
verifyDataRows(
result,
rows(new JSONObject(Map.of("lat", 40.71, "lon", 74)), 40.71, 74),
rows(
new JSONObject(
Map.of(
"lat", -33.85253637358241,
"lon", 151.21652352950258)),
-33.85253637358241,
151.21652352950258),
rows(JSONObject.NULL, JSONObject.NULL, JSONObject.NULL));
}

@Test
public void test_geo_point_lat_in_functions() {
String query = "SELECT ABS(geo_point_object.lat) FROM " + TEST_INDEX_GEOPOINT;
JSONObject result = executeJdbcRequest(query);
verifyDataRows(result, rows(40.71), rows(33.85253637358241), rows(JSONObject.NULL));
}
}
6 changes: 6 additions & 0 deletions integ-test/src/test/resources/geo_point.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{"index":{"_id":"1"}}
{"geo_point_object": {"lat": 40.71, "lon": 74.00}, "object": {"geo_point_object": {"lat": 40.71, "lon": 74.00}}, "geo_point_string": "40.71, 74.00", "geo_point_geohash": "txhxegj0uyp3", "geo_point_array": [74.00, 40.71], "geo_point_string_point": "POINT (74.00 40.71)", "geo_point_geojson": {"type": "Point", "coordinates": [74.00, 40.71]}}
{"index":{"_id":"2"}}
{"geo_point_object": {"lat": -33.85253637358241, "lon": 151.21652352950258}, "object": {"geo_point_object": {"lat": -33.85253637358241, "lon": 151.21652352950258}},"geo_point_string": "-33.85356510743158, 151.22222172610114", "geo_point_geohash": "txhxegj0uyp3", "geo_point_array": [74.00, 40.71], "geo_point_string_point": "POINT (74.00 40.71)", "geo_point_geojson": {"type": "Point", "coordinates": [74.00, 40.71]}}
{"index":{"_id":"3"}}
{"geo_point_object": null, "object": {"geo_point_object": null},"geo_point_string": null, "geo_point_geohash": null, "geo_point_array": null, "geo_point_string_point": null, "geo_point_geojson": null}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"mappings": {
"properties": {
"geo_point_object": {
"type": "geo_point"
},
"object": {
"type": "object",
"properties": {
"geo_point_object": {
"type": "geo_point"
}
}
},
"geo_point_string": {
"type": "geo_point"
},
"geo_point_geohash": {
"type": "geo_point"
},
"geo_point_array": {
"type": "geo_point"
},
"geo_point_string_point": {
"type": "geo_point"
},
"geo_point_geojson": {
"type": "geo_point"
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public enum MappingType {
ScaledFloat("scaled_float", ExprCoreType.DOUBLE),
Double("double", ExprCoreType.DOUBLE),
Boolean("boolean", ExprCoreType.BOOLEAN);
// TODO: ranges, geo shape, point, shape
// TODO: ranges, geo shape, shape

private final String name;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@

import static org.opensearch.sql.data.type.ExprCoreType.UNKNOWN;

import com.google.common.collect.ImmutableMap;
import java.util.LinkedHashMap;
import lombok.EqualsAndHashCode;
import org.opensearch.sql.data.type.ExprCoreType;

/**
* The type of a geo_point value. See <a
Expand All @@ -21,6 +24,11 @@ public class OpenSearchGeoPointType extends OpenSearchDataType {
private OpenSearchGeoPointType() {
super(MappingType.GeoPoint);
exprCoreType = UNKNOWN;
this.properties =
new LinkedHashMap(
ImmutableMap.of(
"lat", new OpenSearchDataType(ExprCoreType.DOUBLE),
"lon", new OpenSearchDataType(ExprCoreType.DOUBLE)));
}

public static OpenSearchGeoPointType of() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,26 +122,34 @@ public Object objectValue() {
@Override
public Pair<Double, Double> geoValue() {
final JsonNode value = value();
if (value.has("lat") && value.has("lon")) {
Double lat = 0d;
Double lon = 0d;
Double lat = null;
Double lon = null;

if (value.has("lat")) {
try {
lat = extractDoubleValue(value.get("lat"));
} catch (Exception exception) {
throw new IllegalStateException(
"latitude must be number value, but got value: " + value.get("lat"));
}
}

if (value.has("lon")) {
try {
lon = extractDoubleValue(value.get("lon"));
} catch (Exception exception) {
throw new IllegalStateException(
"longitude must be number value, but got value: " + value.get("lon"));
}
return Pair.of(lat, lon);
} else {
}

// lat or lon can be null to support geopoint.lat query but both can't be null
if (lat == null && lon == null) {
throw new IllegalStateException(
"geo point must in format of {\"lat\": number, \"lon\": number}");
"geo point must be in format of {\"lat\": number, \"lon\": number}");
}

return Pair.of(lat, lon);
}

/** Getter for value. If value is array the whole array is returned. */
Expand Down
Loading