ElasticSearch系列二:Kibana的使用和C#的Nest客户端

前言

ElasticSearch,Kibana,Logstash等安装上一章讲完了,这一章讲讲ELasticSearch怎么使用

两种方式,一种是直接写ElasticSearch查询语句,当然是在Kibana里面

还有一种是用代码写,可以用Java,C#,PHP,Python等,我这里使用C#

Kibana查询ElasticSearch

#创建
PUT /test/user/1
{
	"name":"许嵩",
	"age":34,
	"tags":["a","b","c"]
}

#查询index里面的数量
GET test/_count


#查询id为1的
GET test/user/1 


#查询name包含许嵩的
GET article/_search?q=title:许嵩


# 也可以使用这种
GET article/_search
{
  "query": {
    "match": {
      "title": "许嵩"
    }
  }
}

# 高亮查询,讲一下,所谓的高亮就是给关键字加上自己定义的一个标签,一般都是em,然后你在css里面定义em为高亮黄色或者红色即可

GET article/_search
{
  "query": {
    "match": {
      "title": "许嵩"
    }
  },
  "highlight": {
    "pre_tags": [
      "<em>"
    ],
    "post_tags": [
      "</em>"
    ],
    "encoder": "html",
    "fields": {
      "title": {
        "force_source": true,
        "fragment_size": 150,
        "fragmenter": "span",
        "number_of_fragments": 3,
        "no_match_size": 150
      }
    }
  }
}

# 更新
POST /test/user/1/_update
{
	"doc":{
		"name":"vae"
	}
}

使用C#的Nest查询ElasticSearch

在NuGet里面下Nest

连接

var settings = new ConnectionSettings(new Uri("http://192.168.3.8:9200/")).DefaultIndex("article");
var client = new ElasticClient(settings);

使用连接池

可以多搞几个ElasticSearch服务器

var uris = new[]
{
    new Uri("http://localhost:9200"),
    new Uri("http://localhost:9201"),
    new Uri("http://localhost:9202"),
};

var connectionPool = new SniffingConnectionPool(uris);
var settings = new ConnectionSettings(connectionPool)
    .DefaultIndex("people");

var client = new ElasticClient(settings);

增加文档

var person = new Person
{
    Id = 1,
    FirstName = "Martijn",
    LastName = "Laarman"
};

//同步
var indexResponse = client.IndexDocument(person); 

//异步
var asyncIndexResponse = await client.IndexDocumentAsync(person); 

批量增加文档

var bulkAllObservable = client.BulkAll(list, b => b
    .Index("article")
    .BackOffTime("30s")
    .BackOffRetries(2)
    .RefreshOnCompleted()
    .MaxDegreeOfParallelism(Environment.ProcessorCount)
    .Size(1000)
)
.Wait(TimeSpan.FromMinutes(15), next =>
{
    // do something e.g. write number of pages to console
});

搜索文档

  1. From(0) 从0开始
  2. Size(10) 每次找10个
  3. Query 查询
  4. Match 匹配,使用Match才会计算匹配度分数,不使用Match的没有匹配相似度分数
  5. Field 字段
  6. AllIndices() 所有的索引

指定索引搜指定字段

var searchResponse = client.Search<Person>(s => s
    .From(0)
    .Size(10)
    .Query(q => q
         .Match(m => m
            .Field(f => f.FirstName)
            .Query("Martijn")
         )
    )
);

var people = searchResponse.Documents;

查所有的索引: .AllIndices()

var searchResponse = client.Search<Person>(s => s
    .AllIndices()
    .From(0)
    .Size(10)
    .Query(q => q
         .Match(m => m
            .Field(f => f.FirstName)
            .Query("Martijn")
         )
    )
);

var people = searchResponse.Documents;

查询全部

var searchResponse = _client.Search<Project>(s => s
    .Query(q => q
        .MatchAll()
    )
);

//和上面一样,但是更简洁的写法
var searchResponse = _client.Search<Project>(s => s
    .MatchAll()
);

高亮查询

public ActionResult Index(string keywords="")
{
    var settings = new ConnectionSettings(new Uri("http://192.168.3.8:9200/")).DefaultIndex("article");
    var client = new ElasticClient(settings);

    var search = client.Search<Article>(s => s
        .From(0)
        .Size(10)
        .Query(q => q
            .Match(m => m
                .Field(f => f.Title)
                .Query(keywords))
            )
        .Highlight(h => h.Fields(e => e.Field("title")
                        .PreTags("<b style=‘color:red‘>")
                        .PostTags("</b>")))
        //.Sort(r => r.Descending(q => q.CreateDate))
        
????????//在工作中把<b style=‘color:red‘>这个换成em标签就可以了,然后在css里面给em加上高亮即可
    );

    foreach (var hit in search.Hits)
    {
        foreach (var highlightField in hit.Highlight)
        {
            if (highlightField.Key == "title")
            {
                foreach (var highlight in highlightField.Value)
                {
                    hit.Source.Title = highlight.ToString();
                }
            }
        }
    }

    return View(search.Documents);
}

聚合查询

var searchResponse = await client.SearchAsync<Person>(s => s
    .Size(0)
    .Query(q => q
         .Match(m => m
            .Field(f => f.FirstName)
            .Query("许嵩")
         )
    )
    .Aggregations(a => a
        .Terms("last_names", ta => ta
            .Field(f => f.LastName)
        )
    )
);

var termsAggregation = searchResponse.Aggregations.Terms("last_names");

结构化: 特点,查询结果要么是true要么是false

结构化一般查找时间,数字或者标题之类的,查询的答案始终是或否;文档是查询的匹配项,或者不是。

开始时间在2017年之间的所有数据

var searchResponse = _client.Search<Project>(s => s
    .Query(q => q
        .DateRange(r => r
            .Field(f => f.StartTime)
            .GreaterThanOrEquals(new DateTime(2017, 01, 01))
            .LessThan(new DateTime(2018, 01, 01))
        )
    )
);
{
  "query": {
    "range": {
      "startedOn": {
        "lt": "2018-01-01T00:00:00",
        "gte": "2017-01-01T00:00:00"
      }
    }
  }
}

非结构化搜索: 特点,在全文字段中搜索

我们常用的搜索就是非结构化搜索,例如我搜一个人名

var searchResponse = _client.Search<Project>(s => s
    .Query(q => q
        .Match(m => m
            .Field(f => f.Name)
            .Query("许嵩")
        )
    )
);

组合查询

查找姓是 许 名是嵩的,并且时间必须在2017年

var searchResponse = _client.Search<Project>(s => s
    .Query(q => q
        .Bool(b => b
            .Must(mu => mu
                .Match(m => m 
                    .Field(f => f.FirstName)
                    .Query("许")
                ), mu => mu
                .Match(m => m 
                    .Field(f => f.LastName)
                    .Query("嵩")
                )
            )
            .Filter(fi => fi
                 .DateRange(r => r
                    .Field(f => f.StartedOn)
                    .GreaterThanOrEquals(new DateTime(2017, 01, 01))
                    .LessThan(new DateTime(2018, 01, 01)) 
                )
            )
        )
    )
);
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "leadDeveloper.firstName": {
              "query": "Russ"
            }
          }
        },
        {
          "match": {
            "leadDeveloper.lastName": {
              "query": "Cam"
            }
          }
        }
      ],
      "filter": [
        {
          "range": {
            "startedOn": {
              "lt": "2018-01-01T00:00:00",
              "gte": "2017-01-01T00:00:00"
            }
          }
        }
      ]
    }
  }
}

还有一个更好用的写法

searchResponse = _client.Search<Project>(s => s
    .Query(q => q
        .Match(m => m
            .Field(f => f.FirstName)
            .Query("许")
        ) && q 
        .Match(m => m
            .Field(f => f.LastName)
            .Query("嵩")
        ) && +q 
        .DateRange(r => r
            .Field(f => f.StartedOn)
            .GreaterThanOrEquals(new DateTime(2017, 01, 01))
            .LessThan(new DateTime(2018, 01, 01))
        )
    )
);

组合记得使用 +

布尔查询

不好的写法

var searchResults = this.Client.Search<Project>(s => s
    .Query(q => q
        .Bool(b => b
            .Should(
                bs => bs.Term(p => p.Name, "x"),
                bs => bs.Term(p => p.Name, "y")
            )
        )
    )
);

这种写法很不好,如果很多bool嵌套查询,多个bool查询,结果是啥?结果就会像下图一样

你的代码会变成凹陷缩进的样子,很丑,而且不利于维护,看着就头大了

ElasticSearch系列二:Kibana的使用和C#的Nest客户端

推荐使用的bool查询

var firstSearchResponse = client.Search<Project>(s => s
    .Query(q => q
        .Term(p => p.Name, "x") || q
        .Term(p => p.Name, "y")
    )
);
var firstSearchResponse = client.Search<Project>(s => s
    .Query(q => q
        .Term(p => p.Name, "x") && q
        .Term(p => p.Name, "y")
    )
);
var firstSearchResponse = client.Search<Project>(s => s
    .Query(q => !q
        .Term(p => p.Name, "x")
    )
);

选择要返回的字段

我们查询不需要所有的字段,只需要几个字段就可以

查询全部的数据,但是只要Name和StartTime这两个字段

var searchResponse = _client.Search<Project>(s => s
    .StoredFields(sf => sf
        .Fields(
            f => f.Name,
            f => f.StartTime
        )
    )
    .Query(q => q
        .MatchAll()
    )
);

源筛选

var searchResponse = _client.Search<Project>(s => s
    .Source(sf => sf
        .Includes(i => i    //包括以下字段
            .Fields(
                f => f.Name,
                f => f.StartedOn
            )
        )
        .Excludes(e => e 
            .Fields("num*")   //排除其他字段
        )
    )
    .Query(q => q
        .MatchAll()
    )
);

ElasticSearch系列二:Kibana的使用和C#的Nest客户端

上一篇:C#中实现计时器功能(定时任务和计时多长时间后执行某方法)


下一篇:C#实现异步阻塞TCP