5

.Net Core中使用NEST简单操作Elasticsearch - 以往清泉

 2 years ago
source link: https://www.cnblogs.com/xwc1996/p/17119378.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

.Net Core中使用NEST简单操作Elasticsearch

C#中访问Elasticsearch主要通过两个包NESTElasticsearch.NetNEST用高级语法糖封装了Elasticsearch.Net可以通过类Linq的方式进行操作,而Elasticsearch.Net相比之下更为原始直接非常自由。

注意:ES8.X以上的版本有新的包Elastic.Clients.Elasticsearc支持。

此处使用NEST,我们通过Nuget安装,如下图:

988132-20230214152302201-2069002451.png

1、准备结构

 准备以下实体

public class Company
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public User User { get; set; }
    }
    public class User
    {
        public string Name { get; set; }
        public int Gender { get; set; }
    }

2、连接ES

 如果是单机连接如下代码,可以直接在Uri上指定账号密码,也可以使用ConnectionSettingsBasicAuthentication来配置账号密码:

var singleNode = new Uri("http://elastic:123456@localhost:9200");
    var connSettings = new ConnectionSettings(singleNode);
  //connSettings.BasicAuthentication("elastic", "123456"); var esClient = new ElasticClient(connSettings);

如果是多个节点集群则如下代码:

var nodes = new Uri[]
    {
        new Uri("http://esNode1:9200"),
        new Uri("http://esNode2:9200"),
        new Uri("http://esNode3:9200")
    };
    var pool = new StaticConnectionPool(nodes);
    var settings = new ConnectionSettings(pool);
    var client = new ElasticClient(settings);

3、创建索引

索引名称必须符合规则否则创建会失败,比如索引只能小写,具体代码如下:

var indexName = "my_index1";//索引名称
    var res = await esClient.Indices.CreateAsync(indexName, o => o.Map(g => g.AutoMap<Company>()));//映射结构

 也可以在向索引插入数据的时候自动判断是否存在索引,不存在会自动创建。索引结构字段映射一但创建就无法修改,可以通过新建索引然后转移数据的方式修改索引结构,但是可以往里面新增字段映射,比如修改了实体结构新的字段将会被映射。

4、插入数据

使用IndexDocumentAsync方法插入单条数据需要在ConnectionSettingsDefaultIndex方法设置默认索引。使用IndexAsync插入单条数据时需要选择指定索引,如下:

var singleNode = new Uri("http://localhost:9200");
    var connSettings = new ConnectionSettings(singleNode);
    connSettings.BasicAuthentication("elastic", "123456");
    var esClient = new ElasticClient(connSettings.DefaultIndex("my_index1"));
    var indexName = "my_index1";
    var company = new Company()
    {
        Name = "超级公司bulk",
        Description = "超级描述bulk",
    };
    var res1 = await esClient.IndexDocumentAsync(company);
    var res2 = await esClient.IndexAsync(company, g => g.Index(indexName))    

 如果需要批量插入需要用BulkDescriptor对象包裹,然后使用BulkAsync方法插入,或者不要包裹直接用IndexManyAsync方法插入,具体如下:

var company = new Company()
    {
        Name = "超级公司bulk",
        Description = "超级描述bulk"
    });
    BulkDescriptor descriptor = new BulkDescriptor();
    descriptor.Index<Company>(op => op.Document(company).Index(indexName));
    var res = await esClient.BulkAsync(descriptor);
    //var list = new List<Company>();
    //list.Add(company);
    //var res = await esClient.IndexManyAsync(list, indexName);  

 如果实体有Id则会使用Id的值做为_id的索引文档唯一值,或者可以通过手动指定如await esClient.IndexAsync(company, g => g.Index(indexName).Id(company.Id)),如果id相同执行插入操作则为更新不会重复插入。在新增后是会返回id等信息可以加以利用。

5、删除数据

删除指定单条数据需要知道数据的id,如下两种方式:

DocumentPath<Company> deletePath = new DocumentPath<Company>(Guid.Empty);
    var delRes = await esClient.DeleteAsync(deletePath, g => g.Index(indexName));
    //或者
    IDeleteRequest request = new DeleteRequest(indexName, "1231");
    var delRes = await esClient.DeleteAsync(request);

 多条删除使用DeleteByQueryAsync方法进行匹配删除,下面两种方式等价,删除Description字段模糊查询有描述的数据(最多10条):

var req = new DeleteByQueryRequest<Company>(indexName)
    {
        MaximumDocuments = 10,//一次最多删几条
        Query = new MatchQuery()
                {
                    Field = "description",
                    Query = "描述"
                }
    };
    var result = await esClient.DeleteByQueryAsync(req);
    //等价于
    var result = await esClient.DeleteByQueryAsync<Company>(dq =>
                    dq.MaximumDocuments(10).Query(
                            q => q.Match(tr => tr.Field(fd => fd.Description).Query("描述"))).Index(indexName)
                     );

 6、更新数据

 除了上述插入数据时自动根据id进行更新外还有以下的主动更新。

 根据id更新单条数据以下代码等价,可以更新部分字段值,但是_id是确定就不会更改的虽然对应的Id字段已被修改:

DocumentPath<Company> deletePath = new DocumentPath<Company>("1231");
    var res = await esClient.UpdateAsync(deletePath ,(p) => p.Doc(company).Index(indexName));
    //等价于
    IUpdateRequest<Company, Company> request = new UpdateRequest<Company, Company>(indexName, "1231")
    {
        Doc = new Company()
        {
            Id = "888",
            Description = "11111",
        }
    };
    var res = await esClient.UpdateAsync(request);

 如果有多个id更新多条数据可以用如下方法:

var res = esClient.Bulk(b => b.UpdateMany(new List<Company>() { new Company()
    {
        Id="1231",
    } }, (b, u) => b.Id(u.Id).Index(indexName).Doc(new Company { Name = "我无语了" })));

通过条件批量更新如下,

var req = new UpdateByQueryRequest<Company>(indexName)
    {
        MaximumDocuments = 10,//一次最多更新几条
        Query = new MatchQuery()
        {
            Field = "description",
            Query = "66",
        },
        Script = new ScriptDescriptor()
        .Source($"ctx._source.description = params.description;")
        .Params(new Dictionary<string, object>
        {
            { "description","小时了123123123"}
        }),
        Refresh = true
    };
    var result = await esClient.UpdateByQueryAsync(req);

 7、数据查询

上文中的更新等都用到了查询过滤,此处就用网上的这个例子吧:

var result = client.Search<VendorPriceInfo>(
                s => s
                    .Explain() //参数可以提供查询的更多详情。
                    .FielddataFields(fs => fs //对指定字段进行分析
                        .Field(p => p.vendorFullName)
                        .Field(p => p.cbName)
                    )
                    .From(0) //跳过的数据个数
                    .Size(50) //返回数据个数
                    .Query(q =>
                        q.Term(p => p.vendorID, 100) // 主要用于精确匹配哪些值,比如数字,日期,布尔值或 not_analyzed的字符串(未经分析的文本数据类型):
                        &&
                        q.Term(p => p.vendorName.Suffix("temp"), "姓名") //用于自定义属性的查询 (定义方法查看MappingDemo)
                        &&
                        q.Bool( //bool 查询
                            b => b
                                .Must(mt => mt //所有分句必须全部匹配,与 AND 相同
                                    .TermRange(p => p.Field(f => f.priceID).GreaterThan("0").LessThan("1"))) //指定范围查找
                                .Should(sd => sd //至少有一个分句匹配,与 OR 相同
                                    .Term(p => p.priceID, 32915),
                                    sd => sd.Terms(t => t.Field(fd => fd.priceID).Terms(new[] {10, 20, 30})),//多值
                                    //||
                                    //sd.Term(p => p.priceID, 1001)
                                    //||
                                    //sd.Term(p => p.priceID, 1005)
                                    sd => sd.TermRange(tr => tr.GreaterThan("10").LessThan("12").Field(f => f.vendorPrice))
                                )
                                .MustNot(mn => mn//所有分句都必须不匹配,与 NOT 相同
                                    .Term(p => p.priceID, 1001)
                                    ,
                                    mn => mn.Bool(
                                        bb=>bb.Must(mt=>mt
                                            .Match(mc=>mc.Field(fd=>fd.carName).Query("至尊"))
                                        ))
                                )
                            )
                    )//查询条件
                .Sort(st => st.Ascending(asc => asc.vendorPrice))//排序
                .Source(sc => sc.Include(ic => ic
                    .Fields(
                        fd => fd.vendorName,
                        fd => fd.vendorID,
                        fd => fd.priceID,
                        fd => fd.vendorPrice))) //返回特定的字段
               );
    

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK