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

本文详细介绍如何利用Elasticsearch实现地理位置搜索功能。通过构建MVC应用并集成谷歌地图,演示了如何设置索引、添加数据及执行geo_distance查询。此外,还提供了完整的代码示例。

本文介绍如何创建一个使用谷歌地图和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查询。 可以创建最优搜索以匹配大多数要求。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值