This is an automated email from the ASF dual-hosted git repository.
bchapuis pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-baremaps.git
The following commit(s) were added to refs/heads/main by this push:
new 6c3c50d7 Add a search service for the database (#818)
6c3c50d7 is described below
commit 6c3c50d749740df42540b49cd164b3eb435c3b1e
Author: Bertil Chapuis <[email protected]>
AuthorDate: Wed Jan 3 12:21:27 2024 +0100
Add a search service for the database (#818)
---
.../java/org/apache/baremaps/cli/map/Serve.java | 3 +
.../org/apache/baremaps/server/SearchResource.java | 97 ++++++++++++++++++++++
basemap/import.js | 6 ++
basemap/queries/{osm_ways.sql => osm_entities.sql} | 12 ++-
basemap/queries/osm_nodes.sql | 1 +
basemap/queries/osm_relations.sql | 1 +
basemap/queries/osm_ways.sql | 1 +
7 files changed, 119 insertions(+), 2 deletions(-)
diff --git a/baremaps-cli/src/main/java/org/apache/baremaps/cli/map/Serve.java
b/baremaps-cli/src/main/java/org/apache/baremaps/cli/map/Serve.java
index a1a0f1bd..d3163566 100644
--- a/baremaps-cli/src/main/java/org/apache/baremaps/cli/map/Serve.java
+++ b/baremaps-cli/src/main/java/org/apache/baremaps/cli/map/Serve.java
@@ -26,6 +26,7 @@ import
io.servicetalk.http.router.jersey.HttpJerseyRouterBuilder;
import java.nio.file.Path;
import java.util.concurrent.Callable;
import java.util.function.Supplier;
+import javax.sql.DataSource;
import org.apache.baremaps.cli.Options;
import org.apache.baremaps.config.ConfigReader;
import org.apache.baremaps.server.*;
@@ -101,6 +102,7 @@ public class Serve implements Callable<Integer> {
.register(TileResource.class)
.register(StyleResource.class)
.register(TileJSONResource.class)
+ .register(SearchResource.class)
.register(ClassPathResource.class)
.register(newContextResolver(objectMapper))
.register(new AbstractBinder() {
@@ -108,6 +110,7 @@ public class Serve implements Callable<Integer> {
protected void configure() {
bind("assets").to(String.class).named("directory");
bind("server.html").to(String.class).named("index");
+ bind(datasource).to(DataSource.class);
bind(tileStoreSupplier).to(tileStoreSupplierType);
bind(styleSupplier).to(styleSupplierType);
bind(tileJSONSupplier).to(tileJSONSupplierType);
diff --git
a/baremaps-server/src/main/java/org/apache/baremaps/server/SearchResource.java
b/baremaps-server/src/main/java/org/apache/baremaps/server/SearchResource.java
new file mode 100644
index 00000000..5b6eaaac
--- /dev/null
+++
b/baremaps-server/src/main/java/org/apache/baremaps/server/SearchResource.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.baremaps.server;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.sql.DataSource;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A resource that searches for entities in the database.
+ */
+@Singleton
[email protected]("/")
+public class SearchResource {
+
+ private static final Logger logger =
LoggerFactory.getLogger(SearchResource.class);
+
+ private final String SEARCH_QUERY =
+ """
+ SELECT id, tags, st_asewkt(st_transform(geom, 'EPSG:4326')),
ts_rank_cd(to_tsvector('english', tags), query) as rank
+ FROM osm_entities, phraseto_tsquery('english', ?) as query
+ WHERE to_tsvector('english', tags) @@ query
+ ORDER BY rank DESC
+ LIMIT ?;
+ """;
+
+ private final DataSource dataSource;
+
+ @Inject
+ public SearchResource(DataSource dataSource) {
+ this.dataSource = dataSource;
+ }
+
+ record SearchResponse(List<SearchResult> results) {
+ }
+
+ record SearchResult(long id, JsonNode tags, String wkt, double score) {
+ }
+
+ @GET
+ @javax.ws.rs.Path("/api/search")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response search(
+ @QueryParam("query") String queryText,
+ @QueryParam("limit") @DefaultValue("10") int limit) {
+ try (var connection = dataSource.getConnection();
+ var statement = connection.prepareStatement(SEARCH_QUERY)) {
+ statement.setString(1, queryText);
+ statement.setInt(2, limit);
+ var list = new ArrayList<SearchResult>();
+ try (var result = statement.executeQuery()) {
+ while (result.next()) {
+ var id = result.getLong(1);
+ var json = result.getString(2);
+ var tags = new ObjectMapper().readTree(json);
+ var wkt = result.getString(3);
+ var rank = result.getDouble(4);
+ list.add(new SearchResult(id, tags, wkt, rank));
+ }
+ }
+ var response = new SearchResponse(list);
+ return Response.status(Response.Status.OK).entity(response).build();
+ } catch (SQLException | JsonProcessingException e) {
+ logger.error("Error while searching", e);
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+ }
+ }
+}
diff --git a/basemap/import.js b/basemap/import.js
index 93054a34..f5490ae7 100644
--- a/basemap/import.js
+++ b/basemap/import.js
@@ -122,6 +122,12 @@ export default {
"databaseSrid": 3857,
"replaceExisting": true,
},
+ {
+ "type": "ExecuteSql",
+ "file": "queries/osm_entities.sql",
+ "database": config.database,
+ "parallel": false,
+ },
]
},
{
diff --git a/basemap/queries/osm_ways.sql b/basemap/queries/osm_entities.sql
similarity index 76%
copy from basemap/queries/osm_ways.sql
copy to basemap/queries/osm_entities.sql
index 54ae022c..7349c81c 100644
--- a/basemap/queries/osm_ways.sql
+++ b/basemap/queries/osm_entities.sql
@@ -13,5 +13,13 @@
-- See the License for the specific language governing permissions and
-- limitations under the License.
-CREATE INDEX IF NOT EXISTS osm_ways_geom_index ON osm_ways USING gist (geom);
-CREATE INDEX IF NOT EXISTS osm_ways_tags_index ON osm_ways USING gin (tags);
\ No newline at end of file
+CREATE VIEW osm_entities AS (
+ SELECT 'node' as type, id, tags, geom
+ FROM osm_nodes
+ UNION
+ SELECT 'way' as type, id, tags, geom
+ FROM osm_ways
+ UNION
+ SELECT 'relation' as type, id, tags, geom
+ FROM osm_relations
+);
diff --git a/basemap/queries/osm_nodes.sql b/basemap/queries/osm_nodes.sql
index 882d5e4a..290b84b2 100644
--- a/basemap/queries/osm_nodes.sql
+++ b/basemap/queries/osm_nodes.sql
@@ -14,4 +14,5 @@
-- limitations under the License.
CREATE INDEX IF NOT EXISTS osm_nodes_tags_index ON osm_nodes USING gin (tags);
+CREATE INDEX IF NOT EXISTS osm_nodes_tags_tsvector_index ON osm_nodes USING
gin (to_tsvector('english', tags));
CREATE INDEX IF NOT EXISTS osm_nodes_geom_index ON osm_nodes USING gist (geom);
diff --git a/basemap/queries/osm_relations.sql
b/basemap/queries/osm_relations.sql
index a1711bea..ceae0432 100644
--- a/basemap/queries/osm_relations.sql
+++ b/basemap/queries/osm_relations.sql
@@ -14,4 +14,5 @@
-- limitations under the License.
CREATE INDEX IF NOT EXISTS osm_relations_tags_index ON osm_relations USING gin
(tags);
+CREATE INDEX IF NOT EXISTS osm_relations_tags_tsvector_index ON osm_relations
USING gin (to_tsvector('english', tags));
CREATE INDEX IF NOT EXISTS osm_relations_geom_index ON osm_relations USING
gist (geom);
diff --git a/basemap/queries/osm_ways.sql b/basemap/queries/osm_ways.sql
index 54ae022c..6e707f39 100644
--- a/basemap/queries/osm_ways.sql
+++ b/basemap/queries/osm_ways.sql
@@ -14,4 +14,5 @@
-- limitations under the License.
CREATE INDEX IF NOT EXISTS osm_ways_geom_index ON osm_ways USING gist (geom);
+CREATE INDEX IF NOT EXISTS osm_ways_tags_tsvector_index ON osm_ways USING gin
(to_tsvector('english', tags));
CREATE INDEX IF NOT EXISTS osm_ways_tags_index ON osm_ways USING gin (tags);
\ No newline at end of file