GeoJsonデータの検索
Oracle NoSQL Databaseには、検索ジオメトリと特定の関係を持つGeoJsonデータを検索する4つの関数が用意されています。
boolean geo_intersect(any*, any*)
- いずれかのオペランドが0個または複数の項目を返した場合はfalseを返します。
- いずれかのオペランドがNULLを返した場合はNULLを返します。
- いずれかのオペランドが有効なジオメトリ・オブジェクトではない項目を返した場合は、falseを返します。
boolean geo_inside(any*, any*)
- いずれかのオペランドが0個または複数の項目を返した場合はfalseを返します。
- いずれかのオペランドがNULLを返した場合はNULLを返します。
- いずれかのオペランドが有効なジオメトリ・オブジェクトではない項目を返した場合は、falseを返します(ただし、オペランドが有効なジオメトリを返さないことがコンパイル時に検出された場合は、エラーが発生します)。
- 2番目のオペランドがポリゴンではないジオメトリ・オブジェクトを返した場合は、falseを返します。
boolean geo_within_distance(any*, any*, double)
- 最初の2つのオペランドのいずれかが0個または複数の項目を返した場合はfalseを返します。
- 最初の2つのオペランドのいずれかがNULLを返した場合はNULLを返します。
- 最初の2つのオペランドのいずれかが有効なジオメトリ・オブジェクトではない項目を返した場合は、falseを返します。
boolean geo_near(any*, any*, double)
geo_nearは、内部でgeo_within_distanceに変換され、2つのジオメトリ間の距離に(暗黙的な) ORDER BYが指定されます。ただし、問合せに(明示的な) ORDER BYがすでに存在する場合、距離による順序付けは実行されません。geo_near関数はWHERE句にのみ使用できます。この場合、最上位レベルの述語(ORまたはNOT演算子の下にネストされていない)にする必要があります。
前述の検索関数以外に、次の2つの関数が用意されています。
double geo_distance(any*, any*)
- いずれかのオペランドが0個または複数の項目を返した場合は-1を返します。
- いずれかのオペランドがNULLを返した場合はNULLを返します。
- オペランドのいずれかがジオメトリでない場合は-1を返します。
boolean geo_is_geometry(any*)
- オペランドが0個または複数の項目を返した場合はfalseを返します。
- オペランドがNULLを返した場合はNULLを返します。
- 入力が単一の有効なジオメトリ・オブジェクトである場合はtrueを返します。それ以外の場合はfalseを返します。
前述の地理関数は、ジオメトリ・オブジェクトに対しては機能しますが、FeatureやFeatureCollectionには機能しません。ただし、含まれているジオメトリ・オブジェクトを地理関数に渡すことによって、FeatureやFeatureCollectionに対する問合せを効果的に行うことはできます。この例を次の項に示します。
例10-1 GeoJsonデータの検索
行に該当するものが格納されている表を考えてみます。この表には、主キーとしてのid列とjsonタイプのpoi列があります。
CREATE TABLE PointsOfInterest (
id INTEGER, poi JSON,
PRIMARY KEY(id));
INSERT INTO PointsOfInterest VALUES (
1,
{
"kind" : "city hall",
"address" : {
"state" : "CA",
"city" : "Campbell",
"street" : "70 North 1st street"
},
"location" : {
"type" : "point",
"coordinates" : [121.94,37.29]
}
}
);
INSERT INTO PointsOfInterest VALUES (
2,
{
"kind" : "nature park",
"name" : "castle rock state park",
"address" : {
"state" : "CA",
"city" : "Los Gatos",
"street" : "15000 Skyline Blvd"
},
"location" : {
"type" : "polygon",
"coordinates" : [
[
[122.1301, 37.2330],
[122.1136, 37.2256],
[122.0920, 37.2291],
[122.1020, 37.2347],
[122.1217, 37.2380],
[122.1301, 37.2330]
]
]
}
}
);
次の問合せでは、北カリフォルニアの自然公園を検索します。この問合せでは、geo_insideではなくgeo_intersectを使用して、隣接する州との境界線をまたぐ公園を含めます。
SELECT t.poi AS park
FROM PointsOfInterest t
WHERE t.poi.kind = "nature park"
AND
geo_intersect(
t.poi.location,
{
"type" : "polygon",
"coordinates" : [
[
[121.94, 36.28],
[117.52, 37.38],
[119.99, 39.00],
[120.00, 41.97],
[124.21, 41.97],
[124.39, 40.42],
[121.94, 36.28]
]
]
}
);
次の問合せでは、指定されたルートの1マイル以内にあるガソリン・スタンドを検索します。返されるガソリン・スタンドは、ルートからの距離ごとに昇順で並べられます。
SELECT
t.poi AS gas_station,
geo_distance(
t.poi.location,
{
"type" : "LineString",
"coordinates" : [
[121.9447, 37.2975],
[121.9500, 37.3171],
[121.9892, 37.3182],
[122.1554, 37.3882],
[122.2899, 37.4589],
[122.4273, 37.6032],
[122.4304, 37.6267],
[122.3975, 37.6144]
]
}
) AS distance
FROM PointsOfInterest t
WHERE t.poi.kind = "gas station"
AND
geo_near(
t.poi.location,
{
"type" : "LineString",
"coordinates" : [
[121.9447, 37.2975],
[121.9500, 37.3171],
[121.9892, 37.3182],
[122.1554, 37.3882],
[122.2899, 37.4589],
[122.4273, 37.6032],
[122.4304, 37.6267],
[122.3975, 37.6144]
]
},
1609
);
例10-2 GeoJsonデータの検索
この例では、Oracle NoSQL DatabaseでFeatureCollectionを問い合せる方法を示します。会社に関する情報を格納するcompanies表を考えます。この情報には、各会社のオフィスの所在地、各オフィス住所の不動産などが含まれます。
CREATE TABLE companies (
id INTEGER, info JSON, PRIMARY KEY(id));
INSERT INTO companies VALUES (
1,
{
"id" : 1,
"info" : {
"name" : "acme",
"CEO" : "some random person",
"locations" : {
"type" : "FeatureCollection",
"features" : [
{
"type" : "Feature",
"geometry" : {
"type" : "point",
"coordinates" : [ 23.549, 35.2908 ]
},
"properties" : {
"kind" : "development",
"city" : "palo alto"
}
},
{
"type" : "Feature",
"geometry" : {
"type" : "point",
"coordinates" : [ 23.9, 35.17 ]
},
"properties" : {
"kind" : "sales",
"city" : "san jose"
}
}
]
}
}
}
);
次の問合せでは、検索地域内に販売代理店がある会社を検索して、検索された会社ごとに、同じ検索地域内の販売代理店の地理ロケーションを含む配列を返します。
SELECT id,
c.info.locations.features [
geo_intersect(
$element.geometry,
{
"type" : "polygon",
"coordinates" : [
[
[23.48, 35.16],
[24.30, 35.16],
[24.30, 35.70],
[23.48, 35.70],
[23.48, 35.16]
]
]
}
)
AND
$element.properties.kind = "sales"
].geometry AS loc
FROM companies c
WHERE EXISTS c.info.locations.features [
geo_intersect(
$element.geometry,
{
"type" : "polygon",
"coordinates" : [
[
[23.48, 35.16],
[24.30, 35.16],
[24.30, 35.70],
[23.48, 35.70],
[23.48, 35.16]
]
]
}
)
AND
$element.properties.kind = "sales"
] ;
この問合せを効率的に実行するために、次の索引を作成できます。
CREATE INDEX idx_kind_loc ON companies (
info.locations.features[].properties.kind AS STRING,
info.locations.features[].geometry AS POINT);