Recherche Géospatiale avec Sphinx

Aujourd'hui, je vais vous parler d'un de mes nouveaux "jouets" : Sphinx Search

Sphinx, pour celles et ceux d'entre vous qui ne connaissent pas encore (bouuuu :p) est un moteur de recherche open source qui est utilisé sur des sites à forte fréquentation comme Craiglist, Joomla, Mysql.com, Scribd, et j'en passe.

Je ne vais pas vous expliquer comment installer et créer un index sous Sphinx, cet article ayant plutôt pour but de vous montrer comment exploiter les fonctionnalités de recherches géographiques du moteur.

En effet, en 2 temps, 3 mouvements, vous pourrez définir un index géographique, et l'utiliser pour récupérer les éléments de votre base de données situés à moins de X mètres d'un item donné.

Imaginez donc que vous possédez une table, listant tout une liste de points d'intérêts, ou même de villes, et que vous avez, pour chacun de ces items leurs coordonnées latitude / longitude (en degrés)

Votre but est donc d'obtenir, pour une ville donnée, les villes situées dans un rayon de 5 km.

Commençons tout d'abord par définir une source cities dans Sphinx, comme suit :

source cities
{
type = mysql
sql_host = localhost
sql_user = root
sql_pass = XXX
sql_db = ma_db

sql_query = SELECT id country, zip, name, \
RADIANS(lat) AS latitude, RADIANS(lon) AS longitude \
FROM cities;
sql_attr_float = latitude
sql_attr_float = longitude

sql_query_info = SELECT name FROM cities WHERE id=$id
}

Vous définissez tout d'abord les informations de connexion à la base. Ensuite viens la requête qui va vous permettre d'indexer les données :

sql_query = SELECT id country, zip, name, \
RADIANS(lat) AS latitude, RADIANS(lon) AS longitude \
FROM cities;

Pour que Sphinx puisse travailler efficacement avec vos coordonnées, il faut les convertir via RADIANS(). C'est là la seule difficulté de ce tutorial :)

Viens à présent le moment de définir un index utilisant cette source :

index cities
{
source = cities
path = /var/lib/sphinxsearch/data/cities
docinfo = extern
mlock = 0
morphology = none
min_word_len = 1
charset_type = utf-8
charset_table = 0..9, A..Z->a..z, _, a..z, U+410..U+42F->U+430..U+44F, U+430..U+44F
ignore_chars = U+00AD
html_strip = 0
enable_star = 0
}

Si vous avez déjà créé un index sous Sphinx, rien de ce qui est ci-dessus ne doit vous sembler étranger :)

Il ne vous reste plus qu'à lancer l'indexation de votre nouvelle config :

indexer --rotate cities

A présent, le clou du spectacle : l'interrogation de la base :)

$search = new SphinxClient();
$search->SetServer('localhost', 9312);
$search->SetMatchMode(SPH_MATCH_ALL);
$search->SetArrayResult(true);
$search->SetGeoAnchor('latitude', 'longitude', (float) deg2rad($latitude), (float) deg2rad($longitude));
$search->SetSortMode ( SPH_SORT_EXTENDED, "@geodist asc");
$circle = (float) $radius;
$search->SetFilterFloatRange('@geodist', 0.0, $circle);

$result = $search->Query('', 'cities');

Les variables $latitude et $longitude étant les coordonnées de votre item de référence, et $radius le rayon en mètres dont lequel doivent se situer les résultats.

Vous remarquerez l'attribut @geodist dans le filtre de recherche et dans le critère de tri. Cet attribut fourni par Sphinx représente la distance en mètres de vos résultats de recherche.

Maintenant, vous avez toutes les clés en main pour monter un super système de POI autour de votre position ;)

Temps de lecture : 3 minutes