问题描述
- 使用Elasticsearch .NET client 创建ES的实例,并导入Shapefile数据建全文索引和空间索引。
- Shapefile为地址点数据,需要对指定的字段进行全文检索(分词、拼音),支持范围搜索
ES服务端配置
- ES的版本为6.3.0
- 采用ik插件进行分词
- 采用pinyin插件进行拼音模块
- 使用postman测试
#创建实例
PUT http://localhost:9200/test_index
{
"index":{
"analysis":{
"analyzer":{
"ik_pinyin_analyzer":{
"type":"custom",
"tokenizer":"ik_smart",
"filter":"pinyin_filter"
}
},
"filter":{
"pinyin_filter":{
"type":"pinyin",
"keep_first_letter": false
}
}
}
}
}
#返回结果
//////////////////返回结果////////////////////////
{
"acknowledged": true,
"shards_acknowledged": true,
"index": "test_index"
}
///////////////////返回结果//////////////////////
#配置Mapping
PUT http://localhost:9200/test_index/_mapping/test_type
{
"properties": {
"testfield":{
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart",
"fields": {
"my_pinyin":{
"type":"text",
"analyzer": "ik_pinyin_analyzer",
"search_analyzer": "ik_pinyin_analyzer"
}
}
}
}
}
#返回结果
//////////////////返回结果////////////////////////
{
"acknowledged": true
}
//////////////////返回结果////////////////////////
#索引文档
POST http://localhost:9200/test_index/test_type
{
"testfield":"今天天气不错"
}
#返回结果
//////////////////返回结果////////////////////////
{
"_index": "test_index",
"_type": "test_type",
"_id": "tjDRIWcBQbCTUJC9qxCs",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}
//////////////////返回结果////////////////////////
#搜索文档(中文)
POST http://localhost:9200/test_index/test_type/_search
{
"query":{
"match": {
"testfield": "天气"
}
}
}
#返回结果
//////////////////返回结果////////////////////////
{
"took": 2,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 0.2876821,
"hits": [
{
"_index": "test_index",
"_type": "test_type",
"_id": "tzDYIWcBQbCTUJC9HRBn",
"_score": 0.2876821,
"_source": {
"testfield": "今天天气不错"
}
}
]
}
}
//////////////////返回结果////////////////////////
#搜索文档(拼音)
POST http://localhost:9200/test_index/test_type/_search
{
"query":{
"match": {
"testfield.my_pinyin": "tianqi"
}
}
}
#返回结果
//////////////////返回结果////////////////////////
{
"took": 3,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 0.68324494,
"hits": [
{
"_index": "test_index",
"_type": "test_type",
"_id": "tzDYIWcBQbCTUJC9HRBn",
"_score": 0.68324494,
"_source": {
"testfield": "今天天气不错"
}
}
]
}
}
//////////////////返回结果////////////////////////
通过以上测试,可以确定ES的服务端功能可以实现,接下来就是使用.NET客户端进行调用
ES客户端配置
- vs2015 .net 4.6.1
- nuget 安装 Es.net client
首先建立连接,本机测试是单机部署,所以连接比较简单
var uris = new[] { new Uri("http://localhost:9200") };
var connectionPool = new SniffingConnectionPool(uris);
var settings = new ConnectionConfiguration(connectionPool);
var lowlevelClient = new ElasticLowLevelClient(settings);
ElasticLowLevelClient
这个类里面封装了各种操作,只需要构建请求体PostData
就行。官方给出的方法是利用匿名对象或者字符串,在构造复杂对象的时候单独使用可能不够方便,可以根据情况自行调整。例如,导入的shapefile有多个字段需要分词,应当如何动态生成Mappings,还有在用bulk批量导入的时候,文档应该如何转成Postdata的格式。
目标1 根据需要分词的字段和固定的空间位置字段生成请求体
分析如下:层次较多,且有动态生成的内容,使用字符串format的方法比较难控制。所以固定属性名的采用匿名对象的方式,而属性名动态生成的部分,用Dictionary
来解决。
ps C# 中 string.format()的字符串中如果本来有“{}”,需要写成“{{}}”
public static object setMapping(List splitFields)
{
#region 配置模板
var pinyin_filter_TEMPLATE = new
{
type = "pinyin",
keep_first_letter = false
};
var ik_pinyin_analyzer_TEMPLATE = new
{
type = "custom",
tokenizer = "ik_smart",
filter = "pinyin_filter"
};
var splitField_TEMPLATE = new
{
type = "text",
analyzer = "ik_max_word",
search_analyzer = "ik_max_word",
fields = new
{
my_pinyin = new
{
type = "text",
analyzer = "ik_pinyin_analyzer",
search_analyzer = "ik_pinyin_analyzer"
}
}
};
var shape_TEMPLATE = new
{
type = "geo_point"
};
var settings = new
{
index = new
{
analysis = new
{
analyzer = new
{
ik_pinyin_analyzer = ik_pinyin_analyzer_TEMPLATE
},
filter = new
{
pinyin_filter = pinyin_filter_TEMPLATE
}
}
}
};
#endregion
var propertiesBody = new Dictionary();
// 分词字段
foreach (string field in splitFields)
{
propertiesBody.Add(field, splitField_TEMPLATE);
}
// 空间位置字段
propertiesBody.Add("shape", shape_TEMPLATE);
var properties = new
{
properties = propertiesBody
};
var indexNameTEMPLATE = new Dictionary();
// type "addorpoi" ,可以用变量代替,一般type是自定义的
indexNameTEMPLATE.Add("addorpoi", properties);
var mappings = indexNameTEMPLATE;
var settinMapping = new
{
settings = settings,
mappings = mappings
};
return settinMapping;
}
目标2 根据空间位置和指定间距进行地址搜索
分析如下:搜索的请求体固定,只要换参数即可,也可以使用字符串,虽然不太简洁,但是也是一种思路。
原有的查询字符串
{
"query":{
"bool":{
"must":{
"match_all":{}
},
"filter":{
"geo_distance":{
"distance":"100m",
"shape":"30.6321727220001,120.308015149"
}
}
}
}
}
使用https://www.sojson.com/yasuoyihang.html提供的压缩转义工具,获得一行转义的json字符串,再用文本把{
换成{{
同时}
换成}}
,然后把参数扣掉换成format需要的形式。
var searchStr = string.Format(searchTemplate, "30.6321727220001,120.308015149", "100");
vat postbody = PostData.String(searchStr);