找到最近的城市,有房源(商家)

时间:2009-10-13 13:09:46

标签: php mysql sphinx

我已经在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英里范围内搜索列表而没有任何问题,但现在我需要点“加入”它们...我想喜欢能够:

a)按名称搜索城市时,只返回列表距离他们25英里的城市 b)当我查看一个城市的结果(纬度/长度已知)时,拉出距其最远25英里的3个最近的城市

有没有办法构建单个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    |         |
+-------------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+

但是当我尝试更新数据以创建lat / long数据中的点时,我收到错误:

> update cities_copy set point = Point(latitude, longitude);
Cannot get geometry object from data you send to the GEOMETRY field

我的语法是在这里还是我遇到了其他一些问题?

1 个答案:

答案 0 :(得分:2)

您需要执行以下操作:

  • 创建一个额外的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
但是请注意,如果你有很多城市,它将不会非常有效。

为了提高效率,您需要创建一个tesselation表(将在您的位置附近平铺地球表面),计算切片的接近顺序并与它们连接。

这是一个简单的脚本来演示:

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