ElasticsearchCRUD使用(五)【Elasticsearch中的子文档,父文档】
来源:互联网 发布:阿里云降价 编辑:程序博客网 时间:2024/06/05 13:41
设置文档搜索引擎
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
。 一个文件只能有一个_i
d!
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)
这是它的外观:
- ElasticsearchCRUD使用(五)【Elasticsearch中的子文档,父文档】
- ElasticsearchCRUD使用(三)【嵌套文档的MVC】
- ElasticsearchCRUD使用(七)【Elasticsearch中的实时重建索引】
- ElasticsearchCRUD使用(十六)【Elasticsearch聚合】
- ElasticsearchCRUD使用(十)【Elasticsearch类型与ElasticsearchCRUD的映射】
- ElasticsearchCRUD使用(十一)【Elasticsearch同义词分析器】
- ElasticsearchCRUD使用(十二)【Elasticsearch的German分析器】
- ElasticsearchCRUD使用(二)【简单的文档进行搜索的MVC应用程序】
- elasticsearch 文档
- ElasticsearchCRUD使用(八)【使用Elasticsearch和WebAPI导出CSV】
- ElasticsearchCRUD使用(六)【EF和Elasticsearch的MVC应用程序】
- ElasticsearchCRUD使用(九)【Elasticsearch父子,孙子节点文件和路由】
- ElasticsearchCRUD使用(十三)【Elasticsearch谷歌地图搜索的MVC应用】
- ElasticsearchCRUD使用(十七)【Elasticsearch搜索多个指标和类型】
- ElasticsearchCRUD使用(十八)【进行MVC搜索Elasticsearch高亮】
- 使用elasticsearch-head创建新文档
- DevExpress ASPxGridView 使用文档五
- Elasticsearch Java API(八)--搜索有相同父id的子文档
- HashMap练习统计字符串中每个字符出现的次数和HashMap嵌套HashMap
- easyui from表单清空内容
- 输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。(笔试题) 句子中单词以空格符隔开。为简单起见,没有标点符号。 例如输入“I am a student”,则输出“student a
- Linux下的查找指令
- QT QWidget设置窗体透明度方法汇总
- ElasticsearchCRUD使用(五)【Elasticsearch中的子文档,父文档】
- python 3种 多线程方法
- POJ 2100 · Graveyard Design【尺取法】
- xgboost使用小结
- Android 状态保存与恢复流程 完全解析
- BZOJ 4010 [HNOI2015]菜肴制作 拓扑排序
- 逻辑推理
- bzoj刷题记录5.11-5.15
- 面试常见的二叉树问题