найти ближайшие города, в которых есть объявления (предприятия)
Вопрос
У меня уже есть два источника данных в Sphinx:
source cities {
...
sql_query = SELECT id, city_name, state_name, state_abbr, latitude,
longitude, population FROM cities;
sql_attr_uint = population
sql_attr_float = latitude
sql_attr_float = longitude
...
}
source listings {
...
sql_query = SELECT entry_id, title, url_title, category_names,
address1, address2, city, state, zip, latitude, longitude,
listing_summary, listing_url, extended_info FROM listings;
sql_attr_float = latitude
sql_attr_float = longitude
...
}
Используя PHP Sphinx API, я без проблем выполнил поиск совпадающих городов по названию и списки в пределах 25 миль по широте/долготе, но теперь мне нужно как бы «присоединиться» к ним...Я хотел бы иметь возможность:
а) При поиске городов по имени возвращайте только города, имеющие списки в пределах 25 миль от них и б), когда я просматриваю результаты для одного города (LAT/LON из них
Есть ли способ создать единый поиск Sphinx для выполнения этих двух поисков?
Редактировать на основе цепочки комментариев ниже:
Я обновил таблицу городов, включив в нее точку поля типа Point, и создал для нее пространственный индекс:
> describe cities_copy; +-------------+-----------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+-----------------------+------+-----+---------+----------------+ | id | mediumint(7) unsigned | NO | PRI | NULL | auto_increment | | city_name | varchar(64) | NO | MUL | NULL | | | state_name | varchar(64) | NO | | NULL | | | state_abbr | varchar(8) | NO | | NULL | | | county_name | varchar(64) | NO | | NULL | | | county_id | smallint(3) unsigned | NO | | NULL | | | latitude | float(13,10) | NO | MUL | NULL | | | longitude | float(13,10) | NO | | NULL | | | population | int(8) unsigned | NO | MUL | NULL | | | point | point | NO | MUL | NULL | | +-------------+-----------------------+------+-----+---------+----------------+ > show indexes from cities_copy; +-------------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | +-------------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ | cities_copy | 0 | PRIMARY | 1 | id | A | 23990 | NULL | NULL | | BTREE | | | cities_copy | 0 | city/state | 1 | city_name | A | NULL | NULL | NULL | | BTREE | | | cities_copy | 0 | city/state | 2 | state_abbr | A | 23990 | NULL | NULL | | BTREE | | | cities_copy | 1 | lat/long | 1 | latitude | A | NULL | NULL | NULL | | BTREE | | | cities_copy | 1 | lat/long | 2 | longitude | A | NULL | NULL | NULL | | BTREE | | | cities_copy | 1 | population | 1 | population | A | NULL | NULL | NULL | | BTREE | | | cities_copy | 1 | point | 1 | point | A | NULL | 32 | NULL | | SPATIAL | | +-------------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
Но когда я пытаюсь обновить данные, чтобы создать точки из данных широты и долготы, я получаю ошибку:
> update cities_copy set point = Point(latitude, longitude); Cannot get geometry object from data you send to the GEOMETRY field
У меня здесь неправильный синтаксис или я столкнулся с какой-то другой проблемой?
Решение
Вам необходимо сделать следующее:
Создайте дополнительный
GEOMETRY
поле, которое будет содержатьPoint(Latitude, Longitude)
, заменяя широту и долготу метрическими координатами плоской Земли.Создать
SPATIAL
индекс в этом полеИсправьте первый запрос:
SELECT * FROM cities cc WHERE EXISTS ( SELECT NULL FROM listings cp WHERE MBRContains(LineString(Point(cc.latitude - 25, cc.longitude - 25), Point(cc.latitude + 25, cc.longitude + 25)), cp.Coords) AND GLength(LineString(cc.Coords, cp.Coords)) <= 25 )
Чтобы узнать три ближайших города, введите этот запрос:
SELECT cp.*
FROM cities cc
CROSS JOIN
cities cp
WHERE cc.id = @id
ORDER BY
GLength(LinePoint(cc.Coords, cp.Coords))
LIMIT 3
, однако учтите, что это будет не очень эффективно, если у вас много городов.
Чтобы сделать его эффективным, вам нужно создать таблицу тесселяции (которая будет мозаикой поверхности Земли рядом с вашими местоположениями), вычислить порядок близости плиток и соединить их.
Вот простой скрипт для демонстрации:
CREATE TABLE t_spatial (id INT NOT NULL PRIMARY KEY, coords Point) ENGINE=MyISAM;
INSERT
INTO t_spatial
VALUES
(1, Point(0, 0)),
(2, Point(0, 1)),
(3, Point(1, 0)),
(4, Point(1, 1));
SELECT s1.id, s2.id, GLength(LineString(s1.coords, s2.coords))
FROM t_spatial s1
CROSS JOIN
t_spatial s2