LINUX.ORG.RU

Организовать БД для быстрого поиска географических адресов

 , ,


1

1

Имеется база данных с географическими объектами, взятая из ФИАС. Там у каждого объекта есть ссылка на родителя, и вложенность этого дерева в разных местах разная (бывает, например, «деревня» внутри «города», а внутри неё улицы и дома).

Нужно научиться искать по этой базе (может быть, после конвертации в другую структуру) по простым человеческим запросам типа «нижний новгород гагарина 1» или «кстово, зелёная 13». Плюс хотелось бы иметь возможность добавлять произвольное количество альтернативных сокращений для каждого объекта (ну там, «60-летия» – «60 лет», «улица такого-то» – «улица имени такого-то»). И желательно чтобы поиск получался стабильно быстрый, в идеале чтобы даже можно было сделать автокомплит с тем же алгоритмом, и он бы не тупил.

Как это всё лучше сделать? Что-то уже несколько недель бьюсь, и все мои попытки тормозят. Как бы вы организовали хранение и поиск по такой базе?

БД – PostgreSQL 9.5.



Последнее исправление: greatperson (всего исправлений: 1)

Ответ на: комментарий от trupanka

Я взял архив с XML внутри, и просто читал нужные мне файлы (SOCRBASE, ADDROBJ и HOUSE) в потоке, передавая питоновскому XmlPullParser и преобразовывая каждый распарсенный элемент в строку разделённую табами, которая в свою очередь шла потоком на вход постгресовскому COPY table FROM stdin. Чтобы было чуть быстрее, я не загружал ненужные мне столбцы, а оставил буквально штук пять-семь столбцов из каждой таблицы.

greatperson
() автор топика

А ты рассматривал вариант использования Elasticsearch, если, конечно, у тебя нет аллергии к Java? Там можно организовать и текстовый поиск, и автокомплит, но последний сложнее.

dave ★★★★★
()
Ответ на: комментарий от trupanka

Как-то так: https://gist.github.com/maaaks/25ae101019d736e5a66332ec2e72e48c

В проекте используется ORM Peewee, поэтому модели написаны для него. Но основная суть там просто в методе load_xml(), который можно легко задействовать с любым фреймворком, только тебе нужно будет из какого-то своего места получить список имён нужных полей для таблиц. И обёртку над COPY тоже какую-то другую использовать, вероятно.

greatperson
() автор топика
Последнее исправление: greatperson (всего исправлений: 1)
Ответ на: комментарий от dave

Нет, не рассматривал, потому что с миром Java знаком очень плохо. А как именно в Elasticsearch будет такое сделано? Он сам умеет в поиск по произвольной иерархии объектов или как? Ткни в нужный кусок какого-нибудь мануала, если не сложно.

greatperson
() автор топика
Ответ на: комментарий от greatperson

Спасибо, как-нибудь попробую поковыряться.
По теме не помогу, т.к. нуб. Ты бы еще запрос оставил, который медленный.
Если у тебя «ссылка на родителя» то это либо вложенные множества, либо рекурсивный запрос. А еще можно заиспользовать ltree или массивы. В ltree поиск сделать типа «Новгород.*Гагарина 1*»

trupanka
()
Ответ на: комментарий от greatperson

Elasticsearch умеет хранить документы (объекты json) как база данных, но с возможностью текстового поиска. Просто в одних полях задаешь название города, улицу и т.д., а в других - географические координаты.

Потом, когда пользователь заходит, то делаешь поиск по фразе. Соответственно, Elasticsearch тебе выдаст выборку с некоторой релевантностью.

Важным моментом является то, что Elasticsearch умеет делать stemming - это как раз привидение слов к некоторому каноническому виду, причем, можно настраивать, но я так понимаю, заниматься такой настройкой - дело непростое.

Для автокомплита там есть, как минимум, одно решение, но оно может не всем подойти.

В дополнение, Elasticsearch масштабируется горизонтально через добавление новых узлов, т.е. работает в кластере.

Я видел доку только на английском. Могу рекомендовать google://«elasticsearch definitive guide»

dave ★★★★★
()

В PostgtreSQL есть полнотекстовый поиск в котором поддерживается морфология. Видимо нужно разобраться как задаются морфологические правила там.

Evgueni ★★★★★
()
Ответ на: комментарий от trupanka

Мой запрос приводить не вижу смысла, так как он большой и всё время разный, я экспериментирую. :-) Последнее, что я пробовал, было большой SQL-простынёй со вложенными запросами и временными таблицами, где я сначала преобразовывал запрос в массив больших массивов айдишников, которые бы удовлетворили хотя бы одному слову. Грубо говоря, «Гагарина, 1» превращался в [[«улица Гагарина», «улица Гагарина»], [«1», «1», «1», «1»...]], где в начале — все улицы и площади Гагарина по всей стране, а затем — все дома 1 по всей стране. А затем я искал те записи, у которых в полной адресной цепочке (а я её хранил заранее, как Materialized Path) обязательно присутстствовало по одному элементу из каждого массива. В примере выше это и нашло бы нам только те объекты, которые действительно являются домами 1 и при этом действительно расположены на улице или площади или чём-то ещё с именем «Гагарина». Плюс оставалась ещё возможность сортировать результат, ставя выше те результаты, в которых элементы стоят в том же порядке, что в запросе.

И это всё вроде как даже работает, но медленно, очень медленно. Там слишком большие временные таблицы, с которыми идут джойны, и это заметно даже на примерах без «домов 1», то есть даже при поиске улиц. К счастью, для уже сделанных ранее запросов PostgreSQL что-то кэширует, что даёт надежду, что второй и последующий запросы к автокомплиту были бы более/менее приемлемыми по времени. К несчастью, первый работает долго, хоть в плане фич меня полностью устроил бы (первая стадия с выбором айдишников по именам происходит быстро, и там можно использовать любое количество синонимов для разных географических объектов).

greatperson
() автор топика
Ответ на: комментарий от Evgueni

В PostgtreSQL есть полнотекстовый поиск

Такой вариант тоже рассматривал, сейчас его вижу как основной, наверное, потому что всё остальное не получилось. То есть для каждого объекта я буду хранить его полный адрес со всеми возможными синонимами на каждом уровне, что-то в духе:

Россия Российская Федерация РФ, область Нижегородская, город Нижний Новгород НН, проспект Гагарина имени Гагарина, дом 1к1 1 корпус 1 д1к1

Тогда да, можно искать просто по строке, и это будет быстро. Только вот один момент с сортировкой результатов: в Нижнем Новгороде есть улица Нижегородская. И запрос «НН, Нижегородская, 1» должен показать мне именно дом на этой улице, но при таком подходе в него попадут вообще все дома 1 внутри Нижегородской области. Вот я совсем не понимаю, как с этим быть. Есть идеи?

greatperson
() автор топика
Последнее исправление: greatperson (всего исправлений: 3)
Ответ на: комментарий от greatperson

Иерархии чего? Это уже твоя задача отвечать на такие вопросы) Я тебе просто дал подсказку посмотреть еще на Elasticsearch, который умеет полнотекстовый поиск.

dave ★★★★★
()

Если запросы сильно тормозят, то есть Materialized View. Так себе вариант, но задачу решить должен.

spoilt ★★★
()

Немного не в тему но

посмотри в сторону google places api.

ggrn ★★★★★
()

По поводу произвольных структур - есть еще jsonb в постгресе. При правильно настроенной индексации - очень быстр. На одном проекте мы делали аттрибуты к продуктам и сначала хотели классический EAV, но требования менялись и сложность выросла колоссально. Сели на jsonb. По скорости все устраивает.

spoilt ★★★
()

Плюсую elasticsearch, хоть сам ее серьезно не использовал. Но по твоему описанию, это именно то, что нужно.

anonymous_sama ★★★★★
()

Столько комментариев, ни одного по существу. Тебе нужно или все влить в одну плоскую таблицу, поставив индексы на основные поля, по которым ищешь, или разбить на таблицы со связями, но последняя таблица с номерами домов все равно будет если не с данными, то со ссылками на данные. Далее разбиваешь поисковый запрос на части, и делаешь общий поиск по слитым вместе полям like, и and по каждой части поискового запроса. С телефона, пример не покажу.

Shadow ★★★★★
()
Ответ на: комментарий от Shadow

Второй вариант, если честно, не понял.

Первый, с плоской таблицей — как я понимаю, означает ограничения на то, сколько полей смогут иметь синонимы, так ведь?

greatperson
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.