Разбор строки адреса (улица [дом]) средствами Golang и Postgis

в 11:36, , рубрики: autocomplete, gis, golang, postgis, postgresql, метки: , , , ,

Hi, читатель.
Столкнулся я на днях с интересной задачей — пользователь вводит строку, которая может быть улицей с домом, просто улицей или вообще не улицей, а нам надо узнать имел ли он ввиду улицу с домом и соответствующее ему подсказать.

— Казалось бы чего проще — разбей строку по пробелу и наслаждайся — подумал Штирлиц
— А как насчет улицы Павла Корчагина — шепнула птица Обломинго
— Эм, ну номер дома же наверняка число — сказал Штирлиц
— Ага, корп1 — хорошее число
— Мдя, придется изобретать велосипед

И расчехлил Штириц плюсомет Golang, да зарядил в него Postgis...

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

const MARK_STEP = 20
func AnalyzeString(str string) (result int, street, house string) {
	result = 100
	LastSpace := strings.LastIndex(str, " ")
	if LastSpace < 1 {
		result = 0
		street = str
		return result, street, house
	}
	if LastSpace < (len([]rune(str)) - 6) {
		result -= MARK_STEP
	} else {
		result += MARK_STEP
	}
	street = str[:LastSpace]
	house = str[LastSpace+1:]
	if models.StreetCount(street) > 0 {
		result += MARK_STEP * 2 
	} else {
		result -= MARK_STEP * 2 
	}
	if models.StreetCount(str) > 0 {
		result -= MARK_STEP
	} else {
		result += MARK_STEP
	}
	if models.HouseCount(street, house) > 0 {
		result += MARK_STEP 
	} else {
		result -= MARK_STEP * 4 
	}
	var int_count, char_count uint8
	for _, run := range []rune(house) {
		if (run > 47) && (run < 58) {
			int_count++
		} else {
			char_count++
		}
	}
	switch {
	case char_count == 0:
		{
			result += MARK_STEP * 3
		}
	case int_count == 0:
		{
			result -= MARK_STEP * 4
		}
	case int_count == char_count:
		{
			result += MARK_STEP
		}
	case int_count > char_count:
		{
			result += MARK_STEP * 2
		}
	case char_count > int_count:
		{
			result -= MARK_STEP
		}
	}
	return result, street, house
}

И так, что же это за функция и что она делает?
Функция принимает на вход строку введенную пользователем и анализирует ее возвращая вероятность того, что это улица с домом, отдельно улицу и отдельно дом.
Если вероятность больше 200 — можете не сомневаться — пользователь имел ввиду улицу с домом.

Вероятно вы заметили вызовы StreetCount(street) и HouseCount(street, house)
в принципе за ними кроется два банальный SQL запроса

rows, err := DB.Query("SELECT COUNT(*) FROM planet_osm_line WHERE highway <> '' AND name ILIKE $1 ", "%"+name+"%")

и

rows, err := DB.Query("SELECT COUNT(house.*) FROM planet_osm_polygon AS house WHERE "addr:street" ILIKE $1 AND "addr:housenumber" ILIKE $2", "%"+streetName+"%", "%"+houseNum+"%")

соответственно

И так, теперь по порядку
Стартовая вероятность 100, разбиваем строку по последнему пробелу, если не получилось (нет пробела) — то к чертям все это не улица с домом.
Если же получилось, то смотрим сколько символов осталось после пробела, если меньше 6 (это число, как и многие другие подобраны по методу профессора Тыка Неба Пальца), то стоит увеличить вероятность, а если больше или равно, то уменьшить.

Уже как-то изменив вероятность, или вообще выйдя из функции плакаться к гарбаж коллектору, продолжаем нашу эпопею (ну или не продолжаем, дядя Гарбажа не любит отпускать).
Все, что до последнего пробела считаем улицей, а то, что после — домом.
Дальше просим Postgis посмотреть, сколько улиц похоже на то, что ввел наш пользователь, если таковые вообще есть, то повышаем вероятность, если нет, то уменьшаем (нет, не выходим, ведь вполне возможно, что улицы еще нет в базе).
Теперь попробуем поискать в базе улицы по исходной строке, если есть, то стоит придержать коней и понизить вероятность, а если нет, то приударим галопом.
Повторим ту-же самую операцию с домами, спроси Postgis о том, есть ли дома с похожим номером, стоящих на похожих улицах.

Ну все, больше база нам не понадобиться.
Теперь разложим строку на символы и посчитаем сколько у нас есть цифр, а сколько букв и других символов, думаю соответствующую конструкцию switch — case объяснять не надо.

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

Автор: t0pep0

Источник

Поделиться

* - обязательные к заполнению поля