2009-10-13 6 views
2

J'ai deux sources de données déjà en Sphinx:trouver plus proche des villes qui ont Listings (entreprises)

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 
    ... 
} 

Utilisation de l'API Sphinx PHP J'ai fait des recherches pour les villes correspondant par leur nom et les recherches pour les listes à 25 miles de un lat/long sans aucun problème, mais maintenant je dois en quelque sorte «joindre» ... Je voudrais être en mesure de:

a) lorsque vous recherchez des villes par nom, renvoyez uniquement les villes ayant des listes dans 25 miles d'entre eux et b) quand je regarde les résultats pour une ville (lat/long est connu), tirer les 3 villes les plus proches qui ont des listes dans les 25 miles de leur

Existe-t-il un moyen de construire une recherche sphinx unique pour accomplir ces deux recherches?

Modifier basée sur la chaîne de commentaire ci-dessous:

J'ai mis à jour mes villes tableau d'inclure un point de champ de type Point et créé un index spatial sur elle:

 
> 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 |   | 
+-------------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 

Mais quand je essayez de mettre à jour les données pour créer des points de données à long lat/je reçois une erreur:

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

Est-ce ma syntaxe off ici ou suis-je en cours d'exécution dans un autre problème?

+0

Si vos objets sont assez proches les uns des autres pour vous d'être en mesure d'utiliser le modèle de la Terre plate, vous devrez convertir votre latitude et la longitude à un cartésienne système, disons, «UTM» ou «Pulkovo» ou quelle que soit la projection la mieux adaptée à votre pays. En ce qui concerne 'UPDATE', cela doit être un problème avec la version' MySQL'. Essayez ceci: 'SET coord = GeomFromText (CONCAT ('Point (', latitude, '', longitude, ')'))', avec 'latitude' et' longitude' convertis en un système cartésien. – Quassnoi

+0

Je n'ai pas converti lat/long en cartesian, mais c'est exactement la requête que j'ai utilisée pour générer les points la dernière fois, avec le résultat final que j'ai dû utiliser ce truc LineStringFromWKB/AsBinary. –

+0

et les villes (points) sont contenues dans les États-Unis (au moins pour l'instant). –

Répondre

2

Vous devez effectuer les opérations suivantes:

  • Créer un champ GEOMETRY supplémentaire qui détiendrait Point(Latitude, Longitude), en remplaçant la latitude et la longitude de coordonnées métriques pour la terre plate.

  • Créer un index SPATIAL sur ce champ

  • fixer la première requête:

    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 
         ) 
    

Pour connaître les trois villes les plus proches, exécutez cette requête:

SELECT cp.* 
FROM cities cc 
CROSS JOIN 
     cities cp 
WHERE cc.id = @id 
ORDER BY 
     GLength(LinePoint(cc.Coords, cp.Coords)) 
LIMIT 3 

, mais notez qu'il ne sera pas très efficace si vous avez beaucoup de cit sies Pour le rendre efficace, vous devrez créer une table de tesselation (qui va paver la surface de la Terre près de vos emplacements), calculer l'ordre de proximité des tuiles et vous joindre à elles.

Voici un script simple à démontrer:

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 
+0

c'est juste en utilisant mysql alors, correct? J'espérais continuer à chercher dans le sphinx parce que c'est beaucoup plus rapide (et nous avons 25000 villes). –

+0

Vous pouvez construire votre index dans 'Sphinx', en utilisant MySQL comme source de données. Ainsi, vous pouvez continuer à chercher dans Sphinx et utiliser les capacités spatiales de MySQL. – Quassnoi

+0

'BTW', quel est le' type' de vos sources? – Quassnoi

Questions connexes