mirror of
https://github.com/osm-search/Nominatim.git
synced 2026-02-14 10:27:57 +00:00
Compare commits
14 Commits
docs-4.0.x
...
v4.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e56add9888 | ||
|
|
9628df3031 | ||
|
|
423f338d04 | ||
|
|
3a2597e5c4 | ||
|
|
641f261495 | ||
|
|
5884a6e7a6 | ||
|
|
10e979e841 | ||
|
|
8dc1441635 | ||
|
|
c79dcfad9a | ||
|
|
1886952666 | ||
|
|
7326b246b7 | ||
|
|
345c812e43 | ||
|
|
fd4ba3989e | ||
|
|
e2d2571ad0 |
@@ -20,7 +20,7 @@ project(nominatim)
|
||||
|
||||
set(NOMINATIM_VERSION_MAJOR 4)
|
||||
set(NOMINATIM_VERSION_MINOR 0)
|
||||
set(NOMINATIM_VERSION_PATCH 0)
|
||||
set(NOMINATIM_VERSION_PATCH 1)
|
||||
|
||||
set(NOMINATIM_VERSION "${NOMINATIM_VERSION_MAJOR}.${NOMINATIM_VERSION_MINOR}.${NOMINATIM_VERSION_PATCH}")
|
||||
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
4.0.1
|
||||
|
||||
* fix initialisation error in replication script
|
||||
* ICU tokenizer: avoid any special characters in word tokens
|
||||
* better error message when API php script does not exist
|
||||
* fix quoting of house numbers in SQL queries
|
||||
* small fixes and improvements in search query parsing
|
||||
* add documentation for moving the database to a different machine
|
||||
|
||||
4.0.0
|
||||
|
||||
* refactor name token computation and introduce ICU tokenizer
|
||||
|
||||
@@ -101,7 +101,7 @@ This will get diffs from the replication server, import diffs and index
|
||||
the database. The default replication server in the
|
||||
script([Geofabrik](https://download.geofabrik.de)) provides daily updates.
|
||||
|
||||
## Importing Nominatim to an external PostgreSQL database
|
||||
## Using an external PostgreSQL database
|
||||
|
||||
You can install Nominatim using a database that runs on a different server when
|
||||
you have physical access to the file system on the other server. Nominatim
|
||||
@@ -109,6 +109,11 @@ uses a custom normalization library that needs to be made accessible to the
|
||||
PostgreSQL server. This section explains how to set up the normalization
|
||||
library.
|
||||
|
||||
!!! note
|
||||
The external module is only needed when using the legacy tokenizer.
|
||||
If you have choosen the ICU tokenizer, then you can ignore this section
|
||||
and follow the standard import documentation.
|
||||
|
||||
### Option 1: Compiling the library on the database server
|
||||
|
||||
The most sure way to get a working library is to compile it on the database
|
||||
@@ -167,3 +172,44 @@ NOMINATIM_DATABASE_MODULE_PATH="<directory on the database server where nominati
|
||||
|
||||
Now change the `NOMINATIM_DATABASE_DSN` to point to your remote server and continue
|
||||
to follow the [standard instructions for importing](Import.md).
|
||||
|
||||
|
||||
## Moving the database to another machine
|
||||
|
||||
For some configurations it may be useful to run the import on one machine, then
|
||||
move the database to another machine and run the Nominatim service from there.
|
||||
For example, you might want to use a large machine to be able to run the import
|
||||
quickly but only want a smaller machine for production because there is not so
|
||||
much load. Or you might want to do the import once and then replicate the
|
||||
database to many machines.
|
||||
|
||||
The important thing to keep in mind when transferring the Nominatim installation
|
||||
is that you need to transfer the database _and the project directory_. Both
|
||||
parts are essential for your installation.
|
||||
|
||||
The Nominatim database can be transferred using the `pg_dump`/`pg_restore` tool.
|
||||
Make sure to use the same version of PostgreSQL and PostGIS on source and
|
||||
target machine.
|
||||
|
||||
!!! note
|
||||
Before creating a dump of your Nominatim database, consider running
|
||||
`nominatim freeze` first. Your database looses the ability to receive further
|
||||
data updates but the resulting database is only about a third of the size
|
||||
of a full database.
|
||||
|
||||
Next install Nominatim on the target machine by following the standard installation
|
||||
instructions. Again make sure to use the same version as the source machine.
|
||||
|
||||
You can now copy the project directory from the source machine to the new machine.
|
||||
If necessary, edit the `.env` file to point it to the restored database.
|
||||
Finally run
|
||||
|
||||
nominatim refresh --website
|
||||
|
||||
to make sure that the local installation of Nominatim will be used.
|
||||
|
||||
If you are using the legacy tokenizer you might also have to switch to the
|
||||
PostgreSQL module that was compiled on your target machine. If you get errors
|
||||
that PostgreSQL cannot find or access `nominatim.so` then copy the installed
|
||||
version into the `module` directory of your project directory. The installed
|
||||
copy can usually be found under `/usr/local/lib/nominatim/module/nominatim.so`.
|
||||
|
||||
@@ -257,7 +257,7 @@ class SearchDescription
|
||||
if (empty($this->aName)) {
|
||||
$this->bNameNeedsAddress = $bNeedsAddress;
|
||||
} else {
|
||||
$this->bNameNeedsAddress |= $bNeedsAddress;
|
||||
$this->bNameNeedsAddress &= $bNeedsAddress;
|
||||
}
|
||||
if ($bSearchable) {
|
||||
$this->aName[$iId] = $iId;
|
||||
@@ -584,11 +584,11 @@ class SearchDescription
|
||||
// will be narrowed down by an address. Remember that with ordering
|
||||
// every single result has to be checked.
|
||||
if ($this->sHouseNumber && ($this->bRareName || !empty($this->aAddress) || $this->sPostcode)) {
|
||||
$sHouseNumberRegex = '\\\\m'.$this->sHouseNumber.'\\\\M';
|
||||
$sHouseNumberRegex = $oDB->getDBQuoted('\\\\m'.$this->sHouseNumber.'\\\\M');
|
||||
|
||||
// Housenumbers on streets and places.
|
||||
$sChildHnr = 'SELECT * FROM placex WHERE parent_place_id = search_name.place_id';
|
||||
$sChildHnr .= " AND housenumber ~* E'".$sHouseNumberRegex."'";
|
||||
$sChildHnr .= ' AND housenumber ~* E'.$sHouseNumberRegex;
|
||||
// Interpolations on streets and places.
|
||||
if (preg_match('/^[0-9]+$/', $this->sHouseNumber)) {
|
||||
$sIpolHnr = 'SELECT * FROM location_property_osmline ';
|
||||
@@ -601,7 +601,7 @@ class SearchDescription
|
||||
}
|
||||
// Housenumbers on the object iteself for unlisted places.
|
||||
$sSelfHnr = 'SELECT * FROM placex WHERE place_id = search_name.place_id';
|
||||
$sSelfHnr .= " AND housenumber ~* E'".$sHouseNumberRegex."'";
|
||||
$sSelfHnr .= ' AND housenumber ~* E'.$sHouseNumberRegex;
|
||||
|
||||
$sSql = '(CASE WHEN address_rank = 30 THEN EXISTS('.$sSelfHnr.') ';
|
||||
$sSql .= ' ELSE EXISTS('.$sChildHnr.') ';
|
||||
@@ -739,9 +739,9 @@ class SearchDescription
|
||||
return $aResults;
|
||||
}
|
||||
|
||||
$sHouseNumberRegex = '\\\\m'.$this->sHouseNumber.'\\\\M';
|
||||
$sHouseNumberRegex = $oDB->getDBQuoted('\\\\m'.$this->sHouseNumber.'\\\\M');
|
||||
$sSQL = 'SELECT place_id FROM placex WHERE';
|
||||
$sSQL .= " housenumber ~* E'".$sHouseNumberRegex."'";
|
||||
$sSQL .= ' housenumber ~* E'.$sHouseNumberRegex;
|
||||
$sSQL .= ' AND ('.join(' OR ', $aIDCondition).')';
|
||||
$sSQL .= $this->oContext->excludeSQL(' AND place_id');
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ class HouseNumber
|
||||
// up of numbers, add a penalty
|
||||
$iSearchCost = 1;
|
||||
if (preg_match('/\\d/', $this->sToken) === 0
|
||||
|| preg_match_all('/[^0-9]/', $this->sToken, $aMatches) > 2) {
|
||||
|| preg_match_all('/[^0-9 ]/', $this->sToken, $aMatches) > 3) {
|
||||
$iSearchCost += strlen($this->sToken) - 1;
|
||||
}
|
||||
if (!$oSearch->hasOperator(\Nominatim\Operator::NONE)) {
|
||||
|
||||
@@ -4,6 +4,7 @@ Subcommand definitions for API calls from the command line.
|
||||
import logging
|
||||
|
||||
from nominatim.tools.exec_utils import run_api_script
|
||||
from nominatim.errors import UsageError
|
||||
|
||||
# Do not repeat documentation of subcommand classes.
|
||||
# pylint: disable=C0111
|
||||
@@ -53,6 +54,18 @@ def _add_api_output_arguments(parser):
|
||||
"Parameter is difference tolerance in degrees."))
|
||||
|
||||
|
||||
def _run_api(endpoint, args, params):
|
||||
script_file = args.project_dir / 'website' / (endpoint + '.php')
|
||||
|
||||
if not script_file.exists():
|
||||
LOG.error("Cannot find API script file.\n\n"
|
||||
"Make sure to run 'nominatim' from the project directory \n"
|
||||
"or use the option --project-dir.")
|
||||
raise UsageError("API script not found.")
|
||||
|
||||
return run_api_script(endpoint, args.project_dir,
|
||||
phpcgi_bin=args.phpcgi_path, params=params)
|
||||
|
||||
class APISearch:
|
||||
"""\
|
||||
Execute a search query.
|
||||
@@ -114,8 +127,7 @@ class APISearch:
|
||||
if not args.dedupe:
|
||||
params['dedupe'] = '0'
|
||||
|
||||
return run_api_script('search', args.project_dir,
|
||||
phpcgi_bin=args.phpcgi_path, params=params)
|
||||
return _run_api('search', args, params)
|
||||
|
||||
class APIReverse:
|
||||
"""\
|
||||
@@ -158,8 +170,7 @@ class APIReverse:
|
||||
if args.polygon_threshold:
|
||||
params['polygon_threshold'] = args.polygon_threshold
|
||||
|
||||
return run_api_script('reverse', args.project_dir,
|
||||
phpcgi_bin=args.phpcgi_path, params=params)
|
||||
return _run_api('reverse', args, params)
|
||||
|
||||
|
||||
class APILookup:
|
||||
@@ -198,8 +209,7 @@ class APILookup:
|
||||
if args.polygon_threshold:
|
||||
params['polygon_threshold'] = args.polygon_threshold
|
||||
|
||||
return run_api_script('lookup', args.project_dir,
|
||||
phpcgi_bin=args.phpcgi_path, params=params)
|
||||
return _run_api('lookup', args, params)
|
||||
|
||||
|
||||
class APIDetails:
|
||||
@@ -249,8 +259,7 @@ class APIDetails:
|
||||
for name, _ in DETAILS_SWITCHES:
|
||||
params[name] = '1' if getattr(args, name) else '0'
|
||||
|
||||
return run_api_script('details', args.project_dir,
|
||||
phpcgi_bin=args.phpcgi_path, params=params)
|
||||
return _run_api('details', args, params)
|
||||
|
||||
|
||||
class APIStatus:
|
||||
@@ -271,6 +280,4 @@ class APIStatus:
|
||||
|
||||
@staticmethod
|
||||
def run(args):
|
||||
return run_api_script('status', args.project_dir,
|
||||
phpcgi_bin=args.phpcgi_path,
|
||||
params=dict(format=args.format))
|
||||
return _run_api('status', args, dict(format=args.format))
|
||||
|
||||
@@ -136,6 +136,7 @@ class UpdateReplication:
|
||||
recheck_interval = args.config.get_int('REPLICATION_RECHECK_INTERVAL')
|
||||
|
||||
tokenizer = tokenizer_factory.get_tokenizer_for_db(args.config)
|
||||
indexer = Indexer(args.config.get_libpq_dsn(), tokenizer, args.threads or 1)
|
||||
|
||||
while True:
|
||||
with connect(args.config.get_libpq_dsn()) as conn:
|
||||
@@ -148,8 +149,6 @@ class UpdateReplication:
|
||||
|
||||
if state is not replication.UpdateState.NO_CHANGES and args.do_index:
|
||||
index_start = dt.datetime.now(dt.timezone.utc)
|
||||
indexer = Indexer(args.config.get_libpq_dsn(), tokenizer,
|
||||
args.threads or 1)
|
||||
indexer.index_full(analyse=False)
|
||||
|
||||
with connect(args.config.get_libpq_dsn()) as conn:
|
||||
|
||||
@@ -10,7 +10,7 @@ Version information for Nominatim.
|
||||
# and must always be increased when there is a change to the database or code
|
||||
# that requires a migration.
|
||||
# Released versions always have a database patch level of 0.
|
||||
NOMINATIM_VERSION = (4, 0, 0, 0)
|
||||
NOMINATIM_VERSION = (4, 0, 1, 0)
|
||||
|
||||
POSTGRESQL_REQUIRED_VERSION = (9, 5)
|
||||
POSTGIS_REQUIRED_VERSION = (2, 2)
|
||||
|
||||
@@ -21,8 +21,8 @@ transliteration:
|
||||
- !include icu-rules/extended-unicode-to-asccii.yaml
|
||||
- ":: Ascii ()"
|
||||
- ":: NFD ()"
|
||||
- "[^[:Ascii:]] >"
|
||||
- ":: lower ()"
|
||||
- "[^a-z0-9[:Space:]] >"
|
||||
- ":: NFC ()"
|
||||
sanitizers:
|
||||
- step: split-name-list
|
||||
|
||||
@@ -113,23 +113,36 @@ class TestCli:
|
||||
assert func.called == 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize("params", [('search', '--query', 'new'),
|
||||
('reverse', '--lat', '0', '--lon', '0'),
|
||||
('lookup', '--id', 'N1'),
|
||||
('details', '--node', '1'),
|
||||
('details', '--way', '1'),
|
||||
('details', '--relation', '1'),
|
||||
('details', '--place_id', '10001'),
|
||||
('status',)])
|
||||
def test_api_commands_simple(self, mock_func_factory, params):
|
||||
@pytest.mark.parametrize("params", [('search', '--query', 'new'),
|
||||
('reverse', '--lat', '0', '--lon', '0'),
|
||||
('lookup', '--id', 'N1'),
|
||||
('details', '--node', '1'),
|
||||
('details', '--way', '1'),
|
||||
('details', '--relation', '1'),
|
||||
('details', '--place_id', '10001'),
|
||||
('status',)])
|
||||
class TestCliApiCall:
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_cli_call(self, cli_call):
|
||||
self.call_nominatim = cli_call
|
||||
|
||||
def test_api_commands_simple(self, mock_func_factory, params, tmp_path):
|
||||
(tmp_path / 'website').mkdir()
|
||||
(tmp_path / 'website' / (params[0] + '.php')).write_text('')
|
||||
mock_run_api = mock_func_factory(nominatim.clicmd.api, 'run_api_script')
|
||||
|
||||
assert self.call_nominatim(*params) == 0
|
||||
assert self.call_nominatim(*params, '--project-dir', str(tmp_path)) == 0
|
||||
|
||||
assert mock_run_api.called == 1
|
||||
assert mock_run_api.last_args[0] == params[0]
|
||||
|
||||
|
||||
def test_bad_project_idr(self, mock_func_factory, params):
|
||||
mock_run_api = mock_func_factory(nominatim.clicmd.api, 'run_api_script')
|
||||
|
||||
assert self.call_nominatim(*params) == 1
|
||||
|
||||
|
||||
class TestCliWithDb:
|
||||
|
||||
|
||||
@@ -106,6 +106,16 @@ class TestCliReplication:
|
||||
assert str(update_mock.last_args[1]['osm2pgsql']) == '/secret/osm2pgsql'
|
||||
|
||||
|
||||
@pytest.mark.parametrize("update_interval", [60, 3600])
|
||||
def test_replication_catchup(self, monkeypatch, index_mock, update_interval, placex_table):
|
||||
monkeypatch.setenv('NOMINATIM_REPLICATION_UPDATE_INTERVAL', str(update_interval))
|
||||
states = [nominatim.tools.replication.UpdateState.NO_CHANGES]
|
||||
monkeypatch.setattr(nominatim.tools.replication, 'update',
|
||||
lambda *args, **kwargs: states.pop())
|
||||
|
||||
assert self.call_nominatim('--catch-up') == 0
|
||||
|
||||
|
||||
def test_replication_update_custom_threads(self, update_mock):
|
||||
assert self.call_nominatim('--once', '--no-index', '--threads', '4') == 0
|
||||
|
||||
|
||||
Reference in New Issue
Block a user