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

本文介绍如何创建一个使用谷歌地图和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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值