ElasticsearchCRUD使用(十三)【Elasticsearch谷歌地图搜索的MVC应用】

来源:互联网 发布:阴司守门人网络剧资源 编辑:程序博客网 时间:2024/06/08 03:05

本文介绍如何创建一个使用谷歌地图和Elasticsearch的MVC应用程序进行geo_distance搜索,并找到最近的点(文档)到您的位置。
Elasticsearch索引使用geo_point来定义每个文档的位置。Elasticsearch支持GeoJson格式。

Elasticsearch索引和类型使用以下模型创建:

public class MapDetail{    public long Id { get; set; }    public string Name { get; set; }    public string Details { get; set; }    public string Information { get; set; }    public string DetailsType { get; set; }    [ElasticsearchGeoPoint]    public GeoPoint DetailsCoordinates { get; set; }}

DetailsCoordinates属性使用GeoPoint类,用于geo_distance搜索。 Elasticsearch中的映射使用ElasticsearchCRUD的IndexCreate方法创建。 Elasticsearch中的Geo类型如果是geo_point,则需要一个ElasticsearchGeoPoint属性,如果它是一个shape 类型,则需要一个ElasticsearchGeoShape属性。 必须映射Geo属性,并在索引新文档时不能自动创建。

public void InitMapDetailMapping(){    using (var context = new ElasticsearchContext(        ConnectionString,         new ElasticsearchSerializerConfiguration(_elasticsearchMappingResolver)))    {        context.TraceProvider = new ConsoleTraceProvider();        context.IndexCreate<MapDetail>();    }}

可以使用以下方式查看映射:

http://localhost:9200/_mapping

这里写图片描述

一旦创建了索引和类型,就会使用_bulk API添加一些数据。 这些文件都使用SaveChanges()方法发送。

public void AddMapDetailData(){    var dotNetGroup = new MapDetail { DetailsCoordinates = new GeoPoint(7.47348, 46.95404), Id = 1, Name = ".NET User Group Bern", Details = "http://www.dnug-bern.ch/", DetailsType = "Work" };    var dieci = new MapDetail { DetailsCoordinates = new GeoPoint(7.41148, 46.94450), Id = 2, Name = "Dieci Pizzakurier Bern", Details = "http://www.dieci.ch", DetailsType = "Pizza" };    var babylonKoeniz = new MapDetail { DetailsCoordinates = new GeoPoint(7.41635, 46.92737), Id = 3, Name = "PIZZERIA BABYLON Köniz", Details = "http://www.pizza-babylon.ch/home-k.html", DetailsType = "Pizza" };    var babylonOstermundigen = new MapDetail { DetailsCoordinates = new GeoPoint(7.48256, 46.95578), Id = 4, Name = "PIZZERIA BABYLON Ostermundigen", Details = "http://www.pizza-babylon.ch/home-o.html", DetailsType = "Pizza" };    using (var context = new ElasticsearchContext(ConnectionString, new ElasticsearchSerializerConfiguration(_elasticsearchMappingResolver)))    {        context.TraceProvider = new ConsoleTraceProvider();        context.AddUpdateDocument(dotNetGroup, dotNetGroup.Id);        context.AddUpdateDocument(dieci, dieci.Id);        context.AddUpdateDocument(babylonKoeniz, babylonKoeniz.Id);        context.AddUpdateDocument(babylonOstermundigen, babylonOstermundigen.Id);        context.SaveChanges();    }}

Elasticsearch中的索引和类型在global.asax Application_Start方法中初始化。 这将检查索引是否存在,并创建一个新的索引(如果没有)。

private void InitSearchEngine(){    var searchProvider = new SearchProvider();    if (!searchProvider.MapDetailsIndexExists())    {        searchProvider.InitMapDetailMapping();        searchProvider.AddMapDetailData();    }}

使用geo_distance filter 和 query查询索引。 这将搜索最大距离内的所有文档,并从最接近您的搜索位置的升序排序命中结果。

{  "query" :  {    "filtered" : {        "query" : {            "match_all" : {}        },        "filter" : {            "geo_distance" : {                "distance" : "300m",                 "detailscoordinates" : [7.41148,46.9445]            }        }    }  }, "sort" : [        {            "_geo_distance" : {                "detailscoordinates" : [7.41148,46.9445],                "order" : "asc",                "unit" : "m"            }        }    ]    }}

上面的Elasticsearch 查询看起来像这样在C#

var search = new Search{    Query = new Query(        new Filtered(             new Filter(                new GeoDistanceFilter(                     "detailscoordinates",                     new GeoPoint(centerLongitude, centerLatitude),                     new DistanceUnitMeter(maxDistanceInMeter)                )            )        )        {            Query = new Query(new MatchAllQuery())        }    ),    Sort = new SortHolder(        new List<ISort>        {            new SortGeoDistance("detailscoordinates", DistanceUnitEnum.m)            {                Order = OrderEnum.asc            }        }    )};

然后在HomeController中使用它,如下所示:

public ActionResult Search(int maxDistanceInMeter, double centerLongitude, double centerLatitude){    var searchResult = _searchProvider.SearchForClosest(maxDistanceInMeter, centerLongitude, centerLatitude);    var mapModel = new MapModel    {        MapData = new JavaScriptSerializer().Serialize(searchResult),        CenterLongitude = centerLongitude,        CenterLatitude = centerLatitude,        MaxDistanceInMeter = maxDistanceInMeter    };    return View("Index", mapModel);}   

razor 索引视图使用此数据在地图显示中。 使用绿色图像显示与您的搜索位置最接近的文档。 最大搜索距离内的所有命中也显示在地图中。 您可以移动您的中心位置,增加或减少最大允许距离,结果将被正确显示。

@*Bern  Lat 46.94792, Long 7.44461 *@@model WebAppGeoElasticsearch.Models.MapModel<input type="hidden" value="@Model.MapData" id="mapdata" name="mapdata" />@using (Html.BeginForm("Search", "Home")){    <fieldset class="form">        <legend>SEARCH for closest document in the search engine using geo distance</legend>        <table width="800">            <tr>                <th></th>            </tr>            <tr>            </tr>            <tr>                <td>                    <input type="submit" value="Search fo closest: " style="width: 300px">                </td>                <td>                    <input type="hidden" value="@Model.CenterLongitude" id="centerLongitude" name="centerLongitude" />                    <input type="hidden" value="@Model.CenterLatitude" id="centerLatitude" name="centerLatitude" />                </td>                <td>                    <p style="width: 300px">Max distance in meter:</p>                    <input id="maxDistanceInMeter" name="maxDistanceInMeter" type="text" title="" value="@Model.MaxDistanceInMeter" style="width: 200px" />                </td>            </tr>        </table>    </fieldset>}<div class="row">    @*Bern  Lat 46.94792, Long 7.44461 *@    <div id="googleMap" style="width: 1000px; height: 800px;">    </div></div>@section scripts{    <script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script>    <script type="text/javascript" src="http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markermanager/src/markermanager.js"></script>    <script language="javascript" type="text/javascript">        var map;        var mgr;        function initialize() {            var myOptions = {                zoom: 13,                center: new google.maps.LatLng(46.94792, 7.44461),                mapTypeId: google.maps.MapTypeId.ROADMAP            };            map = new google.maps.Map(document.getElementById("googleMap"), myOptions);            mgr = new MarkerManager(map);            var infoWindow = new google.maps.InfoWindow({ content: "contents" });            google.maps.event.addListener(mgr, 'loaded', function() {                var modelData = $.parseJSON($("#mapdata").val());                var first = true;                $.each(modelData, function(entryIndex, entry) {                    //alert("Data" + entry.DetailsCoordinates + ", " + entry.Details);                    var htmlString = "<a href=\"" + entry.Details + "\">" + entry.Name + "</a>";                    var coor = entry.DetailsCoordinates.toString();                    var array = coor.split(',');                   // alert("Lat" + array[1] + "Long" + array[0]);                    if (first) {                        var marker = new google.maps.Marker({                            position: new google.maps.LatLng(array[1], array[0]),                            html: htmlString,                            icon: "http://localhost:2765/Content/yourposition.png"                        });                        first = false;                    } else {                        var marker = new google.maps.Marker({                            position: new google.maps.LatLng(array[1], array[0]),                            html: htmlString                        });                    }                    google.maps.event.addListener(marker, "click", function() {                        infoWindow.setContent(this.html);                        infoWindow.open(map, this);                    });                    mgr.addMarker(marker, 0);                });               // alert('homemarker: ' + $("#centerLatitude").val() + ' Current Lng: ' + $("#centerLongitude").val());                var homemarker = new google.maps.Marker({                    position: new google.maps.LatLng($("#centerLatitude").val(), $("#centerLongitude").val()),                    html: "YOU",                    draggable: true,                    icon: "http://localhost:2765/Content/ort.png"                });                google.maps.event.addListener(homemarker, 'dragend', function(evt) {                   // alert('Marker dropped: Current Lat: ' + evt.latLng.lat().toFixed(3) + ' Current Lng: ' + evt.latLng.lng().toFixed(3));                    $("#centerLongitude").val(evt.latLng.lng().toFixed(3));                    $("#centerLatitude").val(evt.latLng.lat().toFixed(3));                });                mgr.addMarker(homemarker, 0);                mgr.refresh();            });        }        google.maps.event.addDomListener(window, 'load', initialize);    </script>}

搜索后的应用程序视图:
这里写图片描述

结论

您可以看到,使用Elasticsearch进行Geo搜索很容易。 支持一系列Geo搜索过滤器,地理边界框过滤器,地理距离过滤器,地理距离范围过滤器,地理多边形过滤器,GeoShape过滤器,Geohash信元过滤器(Geo Bounding Box Filter, Geo Distance Filter, Geo Distance Range Filter, Geo Polygon Filter, GeoShape Filter, Geohash Cell Filter)以及大多数geoJSON形状和GeoShape查询。 可以创建最优搜索以匹配大多数要求。

0 0