{"id":22,"date":"2015-11-12T21:07:09","date_gmt":"2015-11-12T20:07:09","guid":{"rendered":"http:\/\/jacquescortes.fr\/blog\/?p=22"},"modified":"2016-01-26T14:21:49","modified_gmt":"2016-01-26T13:21:49","slug":"elasticsearch-et-la-recherche-geospatiale","status":"publish","type":"post","link":"http:\/\/www.jacquescortes.fr\/blog\/2015\/11\/elasticsearch-et-la-recherche-geospatiale\/","title":{"rendered":"Elasticsearch et la recherche g\u00e9ospatiale"},"content":{"rendered":"<p><a href=\"http:\/\/www.jacquescortes.fr\/blog\/2015\/11\/elasticsearch-et-la-recherche-geospatiale\/logo-elastic\/\" rel=\"attachment wp-att-80\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-80\" src=\"http:\/\/www.jacquescortes.fr\/blog\/wp-content\/uploads\/2015\/11\/logo-elastic-300x92.png\" alt=\"Logo Elastic\" width=\"300\" height=\"92\" srcset=\"http:\/\/www.jacquescortes.fr\/blog\/wp-content\/uploads\/2015\/11\/logo-elastic-300x92.png 300w, http:\/\/www.jacquescortes.fr\/blog\/wp-content\/uploads\/2015\/11\/logo-elastic.png 1000w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>Mon premier article porte sur la recherche g\u00e9ospatiale et sa mise en pratique avec Elasticsearch.<\/p>\n<p>Mais d&rsquo;abord, commen\u00e7ons par voir ce qu&rsquo;est un moteur d&rsquo;indexation.<\/p>\n<h1>Qu&rsquo;est ce que Elasticsearch?<\/h1>\n<p>Elasticsearch est un moteur d&rsquo;indexation.<\/p>\n<p>Tout le monde sait ce qu&rsquo;est un\u00a0moteur de recherche, par exemple celui de Google.<br \/>\nEt bien un moteur de recherche est compos\u00e9 d&rsquo;un moteur d&rsquo;indexation o\u00f9 l&rsquo;on indexe des pointeurs (url) vers du contenu externe (pages web).<br \/>\nLe moteur d&rsquo;indexation peut aussi servir \u00e0 indexer du contenu et \u00e0 le servir directement.<br \/>\nMais en g\u00e9n\u00e9ral, il sert \u00e0 indexer ce qu&rsquo;on appelle des \u00ab\u00a0ressources\u00a0\u00bb qu&rsquo;il ne stocke ni ne g\u00e8re.<br \/>\nPar exemple pour des images ou des documents, on peut indexer les m\u00e9tadonn\u00e9es (titre, auteur, date, etc&#8230;) sous forme de json ou de xml.<br \/>\nLes avantages sont de d\u00e9charger la base de donn\u00e9es qui se concentrera sur la persistance de donn\u00e9es n\u00e9cessitant du transactionnel et\/ou du relationnel.<br \/>\nEnsuite cela permet de faire de la recherche full text, approximative ou approchante avec des scores sur la pertinence, du facetting, etc&#8230;<\/p>\n<p>Un lien avec une autre explication :<br \/>\n<a href=\"http:\/\/infrastructure.smile.eu\/Tout-savoir-sur\/Principes-d-architecture-et-outils-open-source\/La-gestion-des-donnees\/Le-moteur-d-indexation-recherche\" target=\"_blank\" class=\"broken_link\">Le moteur d&rsquo;indexation-recherche (par Smile)<\/a><\/p>\n<p>Un autre lien int\u00e9ressant qui explique la probl\u00e9matique et les solutions retenues sur des sites web grand publique \u00e0 forte charge :<br \/>\n<a href=\"http:\/\/www.oxalide.com\/2014\/01\/aperotech-6-les-moteurs-dindexation\/\" target=\"_blank\">Ap\u00e9roTech #6 : Les moteurs d\u2019indexation \u2013 Compte rendu<\/a><br \/>\nOn y retrouve entre autre Elasticsearch et on voit au passage que l&rsquo;on retrouve \u00e9galement <strong>Redis<\/strong> et <strong>RabbitMQ<\/strong> qui feront s\u00fbrement l&rsquo;objet d&rsquo;un autre article prochainement.<\/p>\n<h1>Qu&rsquo;est ce qu&rsquo;une recherche g\u00e9ospatiale?<\/h1>\n<p>Une recherche g\u00e9ospatiale cumule les m\u00eames crit\u00e8res de recherche classique avec en plus des crit\u00e8res g\u00e9ospatiales.<br \/>\nC&rsquo;est \u00e0 dire :<\/p>\n<ul>\n<li>soit une position (coordonn\u00e9es GPS, position de l&rsquo;utilisateur par exemple), un ordre de tri (du plus proche au plus \u00e9loign\u00e9 ou l&rsquo;inverse) et en option une limite (par exemple 10km autour). Le r\u00e9sultat peut \u00eatre pagin\u00e9 avec 100, 1000, 5000 ou plus de r\u00e9sultats par page.<\/li>\n<li>soit une zone d\u00e9limit\u00e9e d\u00e9finie par exemple par les coordonn\u00e9es du point en haut \u00e0 gauche et du point en bas \u00e0 droite. On appelle cela une \u00ab\u00a0bounding box\u00a0\u00bb. Ca sert par exemple \u00e0 afficher sur une carte les positions des \u00ab\u00a0documents\u00a0\u00bb index\u00e9s en fonction du niveau de zoom et de ce que regarde r\u00e9ellement l&rsquo;utilisateur sur la carte.<br \/>\nQuand l&rsquo;utilisateur d\u00e9place la carte ou change le niveau de zoom, on fait en asynchrone des requ\u00eates pagin\u00e9es vers le serveur pour afficher petit \u00e0 petit l&rsquo;ensemble des points.<br \/>\nAttention \u00e0 la limite du nombre de positions affichables sur une carte d\u00e9zoom\u00e9e compl\u00e9tement (monde entier). Ceci fera \u00e9galement l&rsquo;objet d&rsquo;un autre article, concentrons nous sur la partie backend pour le moment.<\/li>\n<\/ul>\n<h1>Mise en pratique<\/h1>\n<h2>Indexation de documents localis\u00e9s<\/h2>\n<p>Pour indexer un document dans Elasticsearch, \u00e7a se fait en un appel REST.<\/p>\n<p>Un document dans Elasticsearch doit \u00eatre au format JSON, mais \u00e7a peut aussi \u00eatre un m\u00e9ta-document ne contenant que les m\u00e9ta-donn\u00e9es ou bien le document lui m\u00eame est dans l&rsquo;un des m\u00e9ta-champs. Dans le cas de binaire, comme une image par exemple, soit on stocke un lien vers le fichier stock\u00e9 ailleurs, soit on l&rsquo;inclut mais dans ce cas il faut l&rsquo;encoder (en base 64 par exemple).<\/p>\n<p>Pour g\u00e9olocaliser un document, il faut lui rajouter un champs \u00ab\u00a0location\u00a0\u00bb de type geo_point qui contiendra la position GPS du document.<br \/>\nEn fait le champ peut s&rsquo;appeler comme on veut, l&rsquo;important est de faire un mapping explicite car le mapping dynamique ne se fait pas automatiquement sur le type geo-point.<\/p>\n<blockquote><p><code><span class=\"pln\">PUT <\/span><span class=\"pun\">\/<\/span><span class=\"pln\">attractions<br \/>\n<\/span><span class=\"pun\">{<br \/>\n<\/span><span class=\"str\">\u00a0 \"mappings\"<\/span><span class=\"pun\">:<\/span> <span class=\"pun\">{<br \/>\n<\/span><span class=\"str\">\u00a0 \u00a0 \"restaurant\"<\/span><span class=\"pun\">:<\/span> <span class=\"pun\">{<br \/>\n<\/span><span class=\"str\">\u00a0 \u00a0 \u00a0 \"properties\"<\/span><span class=\"pun\">:<\/span> <span class=\"pun\">{<br \/>\n<\/span><span class=\"str\">\u00a0 \u00a0 \u00a0 \u00a0 \"name\"<\/span><span class=\"pun\">:<\/span> <span class=\"pun\">{<br \/>\n<\/span><span class=\"str\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \"type\"<\/span><span class=\"pun\">:<\/span> <span class=\"str\">\"string\"<br \/>\n<\/span><span class=\"pun\">\u00a0 \u00a0 \u00a0 \u00a0 },<br \/>\n<\/span><span class=\"str\">\u00a0 \u00a0 \u00a0 \u00a0 \"location\"<\/span><span class=\"pun\">:<\/span> <span class=\"pun\">{<\/span> <\/code><br \/>\n<code><span class=\"str\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \"type\"<\/span><span class=\"pun\">:<\/span> <span class=\"str\">\"geo_point\"<br \/>\n<\/span><span class=\"pun\">\u00a0 \u00a0 \u00a0 \u00a0 }<br \/>\n<\/span><span class=\"pun\">\u00a0 \u00a0 \u00a0 }<br \/>\n<\/span><span class=\"pun\">\u00a0 \u00a0 }<br \/>\n<\/span><span class=\"pun\">\u00a0 }<br \/>\n<\/span><span class=\"pun\">}<\/span><\/code><\/p><\/blockquote>\n<p>Voici le mapping pour indexer un restaurant avec son nom et sa localisation.<\/p>\n<p>Maintenant, voici 3 exemples d\u2019indexation de document pour ce mapping en utilisant les diff\u00e9rents formats accept\u00e9s pour le type geo_point :<\/p>\n<blockquote><p><code><span class=\"pln\">PUT <\/span><span class=\"pun\">\/<\/span><span class=\"pln\">attractions<\/span><span class=\"pun\">\/<\/span><span class=\"pln\">restaurant<\/span><span class=\"pun\">\/<\/span><span class=\"lit\">1<br \/>\n<\/span><span class=\"pun\">{<br \/>\n<\/span><span class=\"str\">\u00a0 \"name\"<\/span><span class=\"pun\">:<\/span> <span class=\"str\">\"Chipotle Mexican Grill\"<\/span><span class=\"pun\">,<br \/>\n<\/span><span class=\"str\">\u00a0 \"location\"<\/span><span class=\"pun\">:<\/span> <span class=\"str\">\"40.715, -74.011\"<br \/>\n<\/span><span class=\"pun\">}<br \/>\n<\/span><span class=\"pln\"><br \/>\nPUT <\/span><span class=\"pun\">\/<\/span><span class=\"pln\">attractions<\/span><span class=\"pun\">\/<\/span><span class=\"pln\">restaurant<\/span><span class=\"pun\">\/<\/span><span class=\"lit\">2<br \/>\n<\/span><span class=\"pun\">{<br \/>\n<\/span><span class=\"str\">\u00a0 \"name\"<\/span><span class=\"pun\">:<\/span> <span class=\"str\">\"Pala Pizza\"<\/span><span class=\"pun\">,<br \/>\n<\/span><span class=\"str\">\u00a0 \"location\"<\/span><span class=\"pun\">:<\/span> <span class=\"pun\">{<\/span> <span class=\"str\">\"lat\"<\/span><span class=\"pun\">:<\/span> <span class=\"lit\">40.722<\/span><span class=\"pun\">,<\/span> <span class=\"str\">\"lon\"<\/span><span class=\"pun\">:<\/span> <span class=\"pun\">-<\/span><span class=\"lit\">73.989\u00a0<\/span><span class=\"pun\">}<br \/>\n<\/span><span class=\"pun\">}<\/span><\/code><\/p>\n<p><code><span class=\"pln\">PUT <\/span><span class=\"pun\">\/<\/span><span class=\"pln\">attractions<\/span><span class=\"pun\">\/<\/span><span class=\"pln\">restaurant<\/span><span class=\"pun\">\/<\/span><span class=\"lit\">3<br \/>\n<\/span><span class=\"pun\">{<br \/>\n<\/span><span class=\"str\">\u00a0 \"name\"<\/span><span class=\"pun\">:<\/span> <span class=\"str\">\"Mini Munchies Pizza\"<\/span><span class=\"pun\">,<br \/>\n<\/span><span class=\"str\">\u00a0 \"location\"<\/span><span class=\"pun\">:<\/span> <span class=\"pun\">[<\/span> <span class=\"pun\">-<\/span><span class=\"lit\">73.983<\/span><span class=\"pun\">,<\/span> <span class=\"lit\">40.719<\/span> <span class=\"pun\">]<br \/>\n<\/span><span class=\"pun\">}<\/span><\/code><\/p><\/blockquote>\n<p>Attention, dans le cas du format tableau (le 3\u00e8me exemple) c&rsquo;est longitude puis latitude, donc l&rsquo;ordre inverse des deux autres formats.<br \/>\nC&rsquo;est ainsi pour respecter le standard geojson.<\/p>\n<p>A partir de l\u00e0, on va pouvoir faire des recherches sur nos documents, des restaurants dans notre exemple.<br \/>\nOn va donc pouvoir faire une recherche full text, par exemple les restaurants dont le nom contient le mot \u00ab\u00a0pizza\u00a0\u00bb.<br \/>\nOn va aussi pouvoir chercher les restaurants selon une recherche g\u00e9ospatiale.<br \/>\nOn va aussi pouvoir combiner les deux et en plus demander un r\u00e9sultat facett\u00e9 \/ agr\u00e9g\u00e9, tout en ne demandant que\u00a0les 10 premiers afin de faire de la pagination.<\/p>\n<h2>Recherche g\u00e9ospatiale<\/h2>\n<p>Une recherche dans Elasticsearch est compos\u00e9e de filtres.<br \/>\nNous avons plusieurs types de filtres g\u00e9ospatiales \u00e0 notre dispositions :<\/p>\n<ul>\n<li>geo_bounding_box : recherche les positions \u00e0 l&rsquo;int\u00e9rieur d&rsquo;un rectangle dont on passe les coordonn\u00e9es en haut \u00e0 gauche et en bas \u00e0 droite<br \/>\nVoir la doc pour l&rsquo;optimisation de l&rsquo;index pour ce cas de recherche :<br \/>\n<a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/guide\/current\/geo-bounding-box.html\" target=\"_blank\">https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/guide\/current\/geo-bounding-box.html<\/a><\/li>\n<li>geo_distance : recherche les positions qui sont \u00e0 moins d&rsquo;une certaine distance d&rsquo;une coordonn\u00e9e GPS centrale pass\u00e9e dans la requ\u00eate<br \/>\nCa revient \u00e0 chercher les positions comprises dans un cercle.<br \/>\nLa doc nous conseille de privil\u00e9gier si possible le rectangle plut\u00f4t que le cercle car c&rsquo;est beaucoup plus efficace.<br \/>\nN\u00e9anmoins, en passant par ce filtre, Elasticsearch r\u00e9alise tout seul un pr\u00e9filtre en calculant le rectangle dans lequel s&rsquo;inscrit le cercle pour \u00e9liminer un maximum de document et limiter le calcul de distance sur un minimum de document car c&rsquo;est ce qui est co\u00fbteux.<br \/>\nL&rsquo;autre optimisation possible est sur le choix de l&rsquo;algo pour calculer la distance. Si on n&rsquo;a pas besoin d&rsquo;\u00eatre hyper pr\u00e9cis, d&rsquo;autant si la position elle m\u00eame n&rsquo;est pas tr\u00e8s pr\u00e9cise, on peut prendre l&rsquo;algo le plus rapide qui consid\u00e8re que la terre est plate.<br \/>\nLe plus pr\u00e9cis consid\u00e8re la terre sph\u00e9rique et un autre algo est interm\u00e9diaire et pr\u00e9cis dans 99.9% des cas. C&rsquo;est ce dernier algo qui est s\u00e9lectionn\u00e9 par d\u00e9faut.<br \/>\n<a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/guide\/current\/geo-distance.html\" target=\"_blank\">https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/guide\/current\/geo-distance.html<\/a><\/li>\n<li>geo_distance_range : c&rsquo;est la m\u00eame chose que le filtre geo_distance mais avec une distance minimum et une distance maximum.<\/li>\n<li>geo_polygon : le plus co\u00fbteux des filtres, recherche les positions incluses dans un polygon. La doc conseille de passer par les geo_shapes qui eux utilisent l&rsquo;ensemble des geohash inclus dans le polygon.<br \/>\n<a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/guide\/current\/geo-shapes.html\" target=\"_blank\">https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/guide\/current\/geo-shapes.html<\/a><\/li>\n<\/ul>\n<p>Les filtres g\u00e9ospatiales \u00e9tant co\u00fbteux en terme de performance, la doc conseille de les mettre en dernier crit\u00e8re et de faire passer devant les autres crit\u00e8res comme les filtres \u00ab\u00a0term\u00a0\u00bb ou \u00ab\u00a0range\u00a0\u00bb.<\/p>\n<p>Encore une optimisation est possible en pr\u00e9parant un cache sur une r\u00e9gion d\u00e9sign\u00e9e par un geo_bouding_box.<br \/>\n<a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/guide\/current\/geo-caching.html\" target=\"_blank\" class=\"broken_link\">https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/guide\/current\/geo-caching.html<\/a><br \/>\nSi par exemple dans notre Elasticsearch nous avons index\u00e9 des positions sur la plan\u00e8te enti\u00e8re et que pour certains utilisateurs nous savons qu&rsquo;ils sont en France voir m\u00eame dans la r\u00e9gion Ile de France, nous pouvons appliquer un pr\u00e9filtre de mise en cache sur un bounding box couvrant cette r\u00e9gion.<br \/>\nDu coup, toutes les recherches \u00ab\u00a0\u00e0 moins d&rsquo;1 km de la position de l&rsquo;utilisateur\u00a0\u00bb se feront sur le r\u00e9sultat du pr\u00e9filtre mis en cache.<\/p>\n<p>Et une derni\u00e8re optimisation est possible en r\u00e9duisant la place m\u00e9moire que prend une position au d\u00e9triment de sa pr\u00e9cision.<br \/>\nChaque position prend 16 octets en m\u00e9moire pour la latitude et la longitude.<br \/>\nOn peut r\u00e9duire cette taille de 62% en prenant une pr\u00e9cision de 3m et de 75% en prenant une pr\u00e9cision de 1km.<br \/>\n<a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/guide\/current\/geo-memory.html\" target=\"_blank\" class=\"broken_link\">https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/guide\/current\/geo-memory.html<\/a><\/p>\n<h2>Tri du r\u00e9sultat de la recherche<\/h2>\n<p>Elasticsearch permet de trier le r\u00e9sultat par distance du plus proche au plus \u00e9loign\u00e9 et le contraire.<br \/>\nDans ce cas, la distance calcul\u00e9e est donn\u00e9e avec l&rsquo;unit\u00e9 de mesure choisie. Par exemple le m\u00e8tre ou le km.<br \/>\n<a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/guide\/current\/sorting-by-distance.html\" target=\"_blank\">https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/guide\/current\/sorting-by-distance.html<\/a><\/p>\n<p>L&rsquo;autre fa\u00e7on de trier est par le scoring (note \u00e9valuant la pertinence du r\u00e9sultat). Et la distance peut \u00eatre l&rsquo;un des crit\u00e8res pour calculer le score.<br \/>\n<a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/guide\/current\/function-score-query.html\" target=\"_blank\">https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/guide\/current\/function-score-query.html<\/a><\/p>\n<h2>Exemple : recherche des positions pr\u00e9sentes dans un rectangle (bounding box) par ordre d&rsquo;\u00e9loignement d&rsquo;une position donn\u00e9e dans la requ\u00eate<\/h2>\n<blockquote><p><code><span class=\"pln\">GET <\/span><span class=\"pun\">\/<\/span><span class=\"pln\">attractions<\/span><span class=\"pun\">\/<\/span><span class=\"pln\">restaurant<\/span><span class=\"pun\">\/<\/span><span class=\"pln\">_search<br \/>\n<\/span><span class=\"pun\">{<br \/>\n<\/span><span class=\"str\">\u00a0 \"query\"<\/span><span class=\"pun\">:<br \/>\n<\/span><span class=\"pun\">\u00a0 {<br \/>\n<\/span><span class=\"str\">\u00a0 \u00a0 \"filtered\"<\/span><span class=\"pun\">:<br \/>\n<\/span><span class=\"pun\">\u00a0 \u00a0 {<br \/>\n<\/span><span class=\"str\">\u00a0 \u00a0 \u00a0 \"filter\"<\/span><span class=\"pun\">:<br \/>\n<\/span><span class=\"pun\">\u00a0 \u00a0 \u00a0 {<br \/>\n<\/span><span class=\"str\">\u00a0 \u00a0 \u00a0 \u00a0 \"geo_bounding_box\"<\/span><span class=\"pun\">:<br \/>\n<\/span><span class=\"pun\">\u00a0 \u00a0 \u00a0 \u00a0 {<br \/>\n<\/span><span class=\"str\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \"type\"<\/span><span class=\"pun\">:<\/span> <span class=\"str\">\"indexed\"<\/span><span class=\"pun\">,<br \/>\n<\/span><span class=\"str\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \"location\"<\/span><span class=\"pun\">:<br \/>\n<\/span><span class=\"pun\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 {<br \/>\n<\/span><span class=\"str\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \"top_left\"<\/span><span class=\"pun\">:\u00a0<\/span><span class=\"pun\">{\u00a0<\/span><span class=\"str\">\"lat\"<\/span><span class=\"pun\">:<\/span> <span class=\"lit\">40<\/span><span class=\"pun\">.<\/span><span class=\"lit\">8<\/span><span class=\"pun\">,<\/span> <span class=\"str\">\"lon\"<\/span><span class=\"pun\">:<\/span> <span class=\"pun\">-<\/span><span class=\"lit\">74.0<\/span> <span class=\"pun\">},<br \/>\n<\/span><span class=\"str\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \"bottom_right\"<\/span><span class=\"pun\">:<\/span> <span class=\"pun\">{<\/span> <span class=\"str\">\"lat\"<\/span><span class=\"pun\">:<\/span> <span class=\"lit\">40.4<\/span><span class=\"pun\">,<\/span> <span class=\"str\">\"lon\"<\/span><span class=\"pun\">:<\/span> <span class=\"pun\">-<\/span><span class=\"lit\">73.0<\/span> <span class=\"pun\">}<br \/>\n<\/span><span class=\"pun\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 }<br \/>\n<\/span><span class=\"pun\">\u00a0 \u00a0 \u00a0 \u00a0 }<br \/>\n<\/span><span class=\"pun\">\u00a0 \u00a0 \u00a0 }<br \/>\n<\/span><span class=\"pun\">\u00a0 \u00a0 }<br \/>\n<\/span><span class=\"pun\">\u00a0 },<br \/>\n<\/span><span class=\"str\">\u00a0 \"sort\"<\/span><span class=\"pun\">:<br \/>\n<\/span><\/code><code><span class=\"pun\">\u00a0 [<br \/>\n<\/span><span class=\"pun\">\u00a0 \u00a0 {<br \/>\n<\/span><span class=\"str\">\u00a0 \u00a0 \u00a0 \"geo_distance\"<\/span><span class=\"pun\">:<br \/>\n<\/span><span class=\"pun\">\u00a0 \u00a0 \u00a0 {<br \/>\n<\/span><span class=\"str\">\u00a0 \u00a0 \u00a0 \u00a0 \"location\"<\/span><span class=\"pun\">:\u00a0<\/span><span class=\"pun\">{<\/span> <span class=\"str\">\"lat\"<\/span><span class=\"pun\">:<\/span> <span class=\"lit\">40.715<\/span><span class=\"pun\">,<\/span> <span class=\"str\">\"lon\"<\/span><span class=\"pun\">:<\/span> <span class=\"pun\">-<\/span><span class=\"lit\">73.998<\/span> <span class=\"pun\">},<br \/>\n<\/span><span class=\"str\">\u00a0 \u00a0 \u00a0 \u00a0 \"order\"<\/span><span class=\"pun\">:<\/span> <span class=\"str\">\"asc\"<\/span><span class=\"pun\">,<br \/>\n<\/span><span class=\"str\">\u00a0 \u00a0 \u00a0 \u00a0 \"unit\"<\/span><span class=\"pun\">:<\/span> <span class=\"str\">\"km\"<\/span><span class=\"pun\">,<br \/>\n<\/span><span class=\"str\">\u00a0 \u00a0 \u00a0 \u00a0 \"distance_type\"<\/span><span class=\"pun\">:<\/span> <span class=\"str\">\"plane\"<br \/>\n<\/span><span class=\"pun\">\u00a0 \u00a0 \u00a0 }<br \/>\n<\/span><span class=\"pun\">\u00a0 \u00a0 }<br \/>\n<\/span><span class=\"pun\">\u00a0 ]<br \/>\n<\/span><span class=\"pun\">}<\/span><\/code><\/p><\/blockquote>\n<p>distance_type \u00e0 la valeur \u00ab\u00a0plane\u00a0\u00bb indique d&rsquo;utiliser l&rsquo;algo le moins pr\u00e9cis mais le plus performant. La distance calcul\u00e9e sera donc approximative.<\/p>\n<h2>G\u00e9o-aggr\u00e9gations<\/h2>\n<p>Elasticsearch permet d&rsquo;agr\u00e9ger les r\u00e9sultats. Sur une requ\u00eate g\u00e9ospatiale, il y a trois types d\u2019agr\u00e9gation :<\/p>\n<ul>\n<li>geo_distance : groupe les r\u00e9sultats dans des cercles concentriques autour d&rsquo;un point central<br \/>\n<a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/guide\/current\/geo-distance-agg.html\">https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/guide\/current\/geo-distance-agg.html<\/a><\/li>\n<li>geohash_grid : groupe les r\u00e9sultats dans des cellules g\u00e9ohash afin d&rsquo;\u00eatre affich\u00e9s sur une carte<br \/>\n<a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/guide\/current\/geohash-grid-agg.html\">https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/guide\/current\/geohash-grid-agg.html<\/a><\/li>\n<li>geo_bounds : retourne les coordonn\u00e9es du plus petit rectangle (bound) qui permet d&rsquo;afficher tous les r\u00e9sultats. Cela permet de choisir le bon niveau de zoom sur la carte en \u00e9vitant d&rsquo;afficher des zones vides<br \/>\n<a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/guide\/current\/geo-bounds-agg.html\">https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/guide\/current\/geo-bounds-agg.html<\/a><\/li>\n<\/ul>\n<h1>Conclusion<\/h1>\n<p>Voil\u00e0, nous avons fait le tour des principales fonctionnalit\u00e9s g\u00e9ospatiales d&rsquo;Elasticsearch.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Mon premier article porte sur la recherche g\u00e9ospatiale et sa mise en pratique avec Elasticsearch. Mais d&rsquo;abord, commen\u00e7ons par voir ce qu&rsquo;est un moteur d&rsquo;indexation. Qu&rsquo;est ce que Elasticsearch? Elasticsearch est un moteur d&rsquo;indexation. Tout le monde sait ce qu&rsquo;est un\u00a0moteur de recherche, par exemple celui de Google. Et bien un moteur de recherche est [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":80,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7,6,5],"tags":[],"series":[],"class_list":{"0":"post-22","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"has-post-thumbnail","7":"category-elasticsearch","8":"category-geospatiale","9":"category-indexation","10":"czr-hentry"},"_links":{"self":[{"href":"http:\/\/www.jacquescortes.fr\/blog\/wp-json\/wp\/v2\/posts\/22","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.jacquescortes.fr\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.jacquescortes.fr\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.jacquescortes.fr\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.jacquescortes.fr\/blog\/wp-json\/wp\/v2\/comments?post=22"}],"version-history":[{"count":13,"href":"http:\/\/www.jacquescortes.fr\/blog\/wp-json\/wp\/v2\/posts\/22\/revisions"}],"predecessor-version":[{"id":220,"href":"http:\/\/www.jacquescortes.fr\/blog\/wp-json\/wp\/v2\/posts\/22\/revisions\/220"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/www.jacquescortes.fr\/blog\/wp-json\/wp\/v2\/media\/80"}],"wp:attachment":[{"href":"http:\/\/www.jacquescortes.fr\/blog\/wp-json\/wp\/v2\/media?parent=22"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.jacquescortes.fr\/blog\/wp-json\/wp\/v2\/categories?post=22"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.jacquescortes.fr\/blog\/wp-json\/wp\/v2\/tags?post=22"},{"taxonomy":"series","embeddable":true,"href":"http:\/\/www.jacquescortes.fr\/blog\/wp-json\/wp\/v2\/series?post=22"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}