найти ближайшие города, в которых есть объявления (предприятия)

StackOverflow https://stackoverflow.com/questions/1560152

  •  21-09-2019
  •  | 
  •  

Вопрос

У меня уже есть два источника данных в 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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top