ElasticsearchCRUD使用(五)【Elasticsearch中的子文档,父文档】

设置文档搜索引擎

AdventureWorks2012用于填充搜索引擎的数据。 可以在这里下载

以下是使用的传输方法(Entity子对象作为子文档保存在stateprovinces索引中):

public void SaveToElasticsearchStateProvince()
{
    IElasticsearchMappingResolver elasticsearchMappingResolver = new ElasticsearchMappingResolver();
    using ( var elasticsearchContext = new ElasticsearchContext("http://localhost:9200/", new ElasticsearchSerializerConfiguration(elasticsearchMappingResolver,true,true)))
    {
        elasticsearchContext.TraceProvider = new ConsoleTraceProvider();
        using (var databaseEfModel = new SQLDataModel())
        {
            int pointer = 0;
            const int interval = 20;
            bool firstRun = true;
            int length = databaseEfModel.StateProvince.Count();

            while (pointer < length)
            {
                var collection = databaseEfModel.StateProvince.OrderBy(t => t.StateProvinceID).Skip(pointer).Take(interval).ToList<StateProvince>();

                foreach (var item in collection)
                {
                    var ee = item.CountryRegion.Name;
                    elasticsearchContext.AddUpdateDocument(item, item.StateProvinceID);
                }

                if (firstRun)
                {
                    elasticsearchContext.SaveChangesAndInitMappingsForChildDocuments();
                    firstRun = false;
                }
                else
                {
                    elasticsearchContext.SaveChanges();
                }

                pointer = pointer + interval;
            }
        }
    }
}

注意:当您使用代码首先从Entity Framework中的现有数据库导入数据时,ElasticsearchCRUD要求将[key]属性添加到子对象主键。 这些用于为每个文档定义文档_id。 一个文件只能有一个_id!

ElasticSearchProvider for 子文档,父文档

ElasticSearchProvider类实现了Elasticsearch的访问层。 存储层使用ElasticsearchCRUD访问Elasticsearch。 需要为子/父文档配置ElasticsearchSerializerConfiguration类。 为嵌套文档设置默认配置,因此需要更改。 我在默认构造函数中已经构造了所有内容,这也可以与使用构建注入的IoC一起使用。 _context与该类具有相同的生命周期,需要处理。

private const string ConnectionString = "http://localhost:9200/";
private readonly IElasticsearchMappingResolver _elasticsearchMappingResolver;
private readonly ElasticsearchContext _context;

public ElasticSearchProvider()
{
    _elasticsearchMappingResolver = new ElasticsearchMappingResolver();
    _elasticsearchMappingResolver.AddElasticsearchMappingForEntityType(
        typeof(Address), 
        new ElasticsearchMappingAddress()
    );
    _context = new ElasticsearchContext(
        ConnectionString, 
        new ElasticsearchSerializerConfiguration(
            _elasticsearchMappingResolver,
            true,
            true
        )
    );
}

定义Address 类型的映射是因为这是父索引'stateprovinces'中的子文档。 由于这个原因,默认映射需要被覆盖。 ElasticSearchMapping类可以被继承,只需要实现一个方法:GetIndexForType。 现在Address 类型将被正确映射。

using System;
using ElasticsearchCRUD;

namespace WebSearchWithElasticsearchChildDocuments.Search
{
    public class ElasticsearchMappingAddress : ElasticsearchMapping
    {
        // This address type is a child type form stateprovince in the stateprovinces index
        public override string GetIndexForType(Type type)
        {
            return "stateprovinces";
        }
    }
}

这个类然后在构造函数中添加到上面的Resolver中。

_elasticsearchMappingResolver.AddElasticsearchMappingForEntityType(
       typeof(Address), 
       new ElasticsearchMappingAddress()
   );

现在,搜索提供可以实现CRUD功能

public IEnumerable<T> QueryString<T>(string term) 
{ 
        return _context.Search<T>(BuildQueryStringSearch(term)).PayloadResult.Hits.HitsResult.Select(t =>t.Source).ToList();
}

private Search BuildQueryStringSearch(string term)
{
    var names = "";
    if (term != null)
    {
        names = term.Replace("+", " OR *");
    }
    var search = new Search
    {
        Query = new Query(new QueryStringQuery(names + "*"))
    };
    return search;
}

public void AddUpdateDocument(Address address)
{
        // if the parent has changed, the child needs to be deleted and created again. This in not required in this example
    _context.AddUpdateDocument(address, address.AddressID, address.StateProvinceID);
    _context.SaveChanges();
}

public void UpdateAddresses(long stateProvinceId, List<Address> addresses)
{
    foreach (var item in addresses)
    {
        _context.AddUpdateDocument(item, item.AddressID, item.StateProvinceID);
    }

    _context.SaveChanges();
}

public void DeleteAddress(long addressId)
{
    _context.DeleteDocument<Address>(addressId);
    _context.SaveChanges();
}

public List<SelectListItem> GetAllStateProvinces()
{
    var result = from element in _context.Search<StateProvince>("").PayloadResult.Hits.HitsResult.Select(t => t.Source)
    select new SelectListItem
    {
       Text = string.Format("StateProvince: {0}, CountryRegionCode {1}",
                    element.StateProvinceCode, element.CountryRegionCode),
       Value = element.StateProvinceID.ToString()
    };

    return result.ToList();
}

public PagingTableResult<Address> GetAllAddressesForStateProvince(string stateprovinceid, int jtStartIndex, int jtPageSize, string jtSorting)
{
    var result = new PagingTableResult<Address>();
    var data = _context.Search<Address>(
                    BuildSearchForChildDocumentsWithIdAndParentType(
                        stateprovinceid, 
                        "stateprovince",
                        jtStartIndex, 
                        jtPageSize, 
                        jtSorting)
                );

    result.Items = data.PayloadResult.ToList();
    result.TotalCount = data.TotalHits;
    return result;
}

// {
//  "from": 0, "size": 10,
//  "query": {
//  "term": { "_parent": "parentdocument#7" }
//  },
//  "sort": { "city" : { "order": "desc" } }"
// }
private Search BuildSearchForChildDocumentsWithIdAndParentType(object parentId, string parentType, int jtStartIndex, int jtPageSize, string jtSorting)
{
    var search = new Search
    {
        From = jtStartIndex,
        Size = jtPageSize,
        Query = new Query(new TermQuery("_parent", parentType + "#" + parentId))
    };

    var sorts = jtSorting.Split(' ');
    if (sorts.Length == 2)
    {
        var order = OrderEnum.asc;
        if (sorts[1].ToLower() == "desc")
        {
            order = OrderEnum.desc;
        }

        search.Sort = CreateSortQuery(sorts[0].ToLower(), order);
    }
    return search;
}

public SortHolder CreateSortQuery(string sort, OrderEnum order)
{
    return new SortHolder(
        new List<ISort>
        {
            new SortStandard(sort)
            {
                Order = order
            }
        }
    );
}

_context.Search方法接受在Elasticsearch Search API中直接使用的任何Json字符串。 Elasticsearch提供了如何将这个Json查询放在一起的良好文档。 因为这个例子中的查询非常简单,所以我用StringBuilder添加了它们。 如果需要更复杂的查询,也许您应该使用NEST进行搜索功能。 ElasticsearchCRUD的重点是进行CRUD操作,轻松实现简单,嵌套或父/子文档的数据传输。

SearchController

现在实现后端,需要为视图实现搜索控制器。 jTable表格使用ajax请求直接访问控制器。 jTable要求数据是所需的格式。 该表格执行分页并发送stateprovince文档的parentId(用于Elasticsearch中的地址子文档路由)

using System;
using System.Collections.Generic;
using System.Web.Mvc;
using WebSearchWithElasticsearchChildDocuments.Search;

namespace WebSearchWithElasticsearchChildDocuments.Controllers
{
    [RoutePrefix("Search")]
    public class SearchController : Controller
    {
        readonly ISearchProvider _searchProvider = new ElasticSearchProvider();

        [HttpGet]
        public ActionResult Index()
        {
            return View();
        }

        [Route("Search")]
        public JsonResult Search(string term)
        {
            return Json(_searchProvider.QueryString<StateProvince>(term), "AddressListForStateProvince", JsonRequestBehavior.AllowGet);
        }

        [Route("GetAddressForStateProvince")]
        public JsonResult GetAddressForStateProvince(string stateprovinceid, int jtStartIndex = 0, int jtPageSize = 0, string jtSorting = null)
        {
            try
            {
                var data = _searchProvider.GetAllAddressesForStateProvince(stateprovinceid, jtStartIndex, jtPageSize, jtSorting);
                return Json(new { Result = "OK", Records = data.Items, TotalRecordCount = data.TotalCount });
            }
            catch (Exception ex)
            {
                return Json(new { Result = "ERROR", Message = ex.Message });
            }
        }

        [Route("CreateAddressForStateProvince")]
        public JsonResult CreateAddressForStateProvince(Address address)
        {
            try
            {
                _searchProvider.AddUpdateDocument(address);
                return Json(new { Result = "OK", Records = address });
            }
            catch (Exception ex)
            {
                return Json(new { Result = "ERROR", Message = ex.Message });
            }
        }

        [HttpPost]
        [Route("DeleteAddress")]
        public ActionResult DeleteAddress(long addressId, long selectedstateprovinceid)
        {
            _searchProvider.DeleteAddress(addressId, selectedstateprovinceid);
            return Json(new { Result = "OK"});
        }
    }
}

Razor视图如下:

@model WebSearchWithElasticsearchChildDocuments.Models.SearchModel

<br/>

<fieldset class="form">
    <legend></legend>
    <table width="500">
        <tr>
            <th></th>
        </tr>
        <tr>
            <td>
                <label for="autocomplete">Search: </label>
            </td>
        </tr>
        <tr>
            <td>
                <input id="autocomplete" type="text" style="width:500px" />
            </td>
        </tr>


    </table>
</fieldset>

<div id="addressResultsForStateProvince" />
<input name="selectedstateprovinceid" id="selectedstateprovinceid" type="hidden" value="" />

@section scripts
{
    <link href="http://localhost:49907/Content/themes/flat/jquery-ui-1.10.3.min.css" rel="stylesheet" />
    <link href="~/Scripts/jtable/themes/jqueryui/jtable_jqueryui.min.css" rel="stylesheet" />
    <script type="text/javascript">

        $(document).ready(function() {

            var updateResults = [];
            $("input#autocomplete").autocomplete({
                source: function(request, response) {
                    $.ajax({
                        url: "http://localhost:49907/Search/search",
                        dataType: "json",
                        data: {
                            term: request.term,
                        },
                        success: function(data) {
                            var itemArray = new Array();
                            for (i = 0; i < data.length; i++) {
                                var labelData = data[i].Name + ", " + data[i].StateProvinceCode + ", " + data[i].CountryRegionCode;
                                itemArray[i] = { label: labelData, value: labelData, data: data[i] }
                            }

                            console.log(itemArray);
                            response(itemArray);
                        },
                        error: function(data, type) {
                            console.log(type);
                        }
                    });
                },
                select: function(event, ui) {
                    $("#selectedstateprovinceid").val(ui.item.data.StateProvinceID);
                    $('#addressResultsForStateProvince').jtable('load', {selectedstateprovinceid : ui.item.data.StateProvinceID});
                    console.log(ui.item);
                }
            });

                $('#addressResultsForStateProvince').jtable({
                    title: 'Address list of selected StateProvince',
                    paging: true,
                    pageSize: 10,
                    sorting: true,
                    multiSorting: true,
                    defaultSorting: 'City asc',
                    actions: {
                        deleteAction: function (postData, jtParams) {
                            return $.Deferred(function ($dfd) {
                                $.ajax({
                                    url: 'http://localhost:49907/Search/DeleteAddress?addressId=' + postData.AddressID + "&selectedstateprovinceid=" + $('#selectedstateprovinceid').val(),
                                    type: 'POST',
                                    dataType: 'json',
                                    data: postData,
                                    success: function (data) {
                                        $dfd.resolve(data);
                                    },
                                    error: function () {
                                        $dfd.reject();
                                    }
                                });
                            });
                        },
                        listAction: function (postData, jtParams) {
                            return $.Deferred(function ($dfd) {

                                console.log(jtParams);
                                $.ajax({
                                    url: 'http://localhost:49907/Search/GetAddressForStateProvince?stateprovinceid=' + $('#selectedstateprovinceid').val(),
                                    type: 'POST',
                                    dataType: 'json',
                                    data: jtParams,
                                    success: function (data) {
                                        $dfd.resolve(data);
                                    },
                                    error: function () {
                                        $dfd.reject();
                                    }
                                });
                            });
                        },
                        createAction: function (postData) {
                            return $.Deferred(function ($dfd) {
                                $.ajax({
                                    url: 'http://localhost:49907/Search/CreateAddressForStateProvince?stateprovinceid=' + $('#selectedstateprovinceid').val(),
                                    type: 'POST',
                                    dataType: 'json',
                                    data: postData,
                                    success: function (data) {
                                        $dfd.resolve(data);
                                    },
                                    error: function () {
                                        $dfd.reject();
                                    }
                                });
                            });
                        },
                    updateAction: function(postData) {
                        return $.Deferred(function ($dfd) {
                            $.ajax({
                                url: 'http://localhost:49907/Search/CreateAddressForStateProvince?stateprovinceid=' + $('#selectedstateprovinceid').val(),
                                type: 'POST',
                                dataType: 'json',
                                data: postData,
                                success: function (data) {
                                    $dfd.resolve(data);
                                },
                                error: function () {
                                    $dfd.reject();
                                }
                            });
                        });
                    }
                },
                fields: {
                    AddressID: {
                        key: true,
                        create: true,
                        edit: true,
                        list: true
                    },
                    AddressLine1: {
                        title: 'AddressLine1',
                        width: '20%'
                    },
                    AddressLine2: {
                        title: 'AddressLine2',
                        create: true,
                        edit: true,
                        width: '20%'
                    },
                    City: {
                        title: 'City',
                        create: true,
                        edit: true,
                        width: '15%'
                    },
                    StateProvinceID: {
                        title: 'StateProvinceID',
                        create: false,
                        edit: false,
                        width: '10%'
                    },
                    PostalCode: {
                        title: 'PostalCode',
                        create: true,
                        edit: true,
                        width: '10%'
                    },
                    ModifiedDate: {
                        title: 'ModifiedDate',
                        edit: false,
                        create: false,
                        width: '15%',
                        display: function (data) { return moment(data.record.ModifiedDate).format('DD/MM/YYYY HH:mm:ss'); }
                    }
                }
            });
        });
    </script>
}

当autocomplete 选择一个StateProvince项时,它将加载具有子address 项的jTable。 这些项目可以被编辑,删除或新的添加到父StateProvince。 所需的javascript库和css文件都包含在MVC包中。 (jQuery-UI,moment.js和jTable)
这是它的外观:
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值