【Elasticsearch】查与部分增

前言:
最近感觉有好多事情要做,时间真是越来越珍贵啦

正文:
    首先Elasticsearch(以下简称es)是一个基于Lucene的搜索服务器,顾名思义是查询方面的一个能手,但是他也是有一套自己的增删改的、相对而言用处较少,也比较简单所以简单略过啦:

    增:指定的 /Index/Type/id 发送 PUT 请求
        可不指定id,POST

    删:curl -X DELETE 'localhost:9200/accounts/person/1'

    更新:PUT 请求,重新发送一次数据

    查:/Index/Type/Id发出 GET 
    搜索/Index/Type/_search 返回值:
        total:返回记录数,本例是2条。
        max_score:最高的匹配程度,本例是1.0。
        hits:返回的记录组成的数组。

用于操作的curl格式如下:

curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d '<BODY>'

 1、VERB:http方法,get、post、put、head或delete(我是大写的)
 2、PROTOCOL:http或https
 3、HOST:es集群中任意节点的主机名,可以用localhost代本机节点
 4、PATH:API的终端路径
 5、QUERY_STRING:任意可选的查询字符串参数 (如 ?pretty 将输出 JSON 返回值)
 6、BODY:一个 JSON 格式的请求体 (不是必须)

curl -XGET 'http://localhost:9200/_count?pretty' -d '
{
    "query": {
        "match_all": {}
    }
}
'

为了撑得起搜索服务器的名号,es为查询提供了丰富的支持:

常用的term查询:

      不分词地查询某字段里有(一个或多个)关键词的文档

#查询preview字段有elasticsearch或book关键词的文档(从1开始返回2个结果)

#minimum_match:最小匹配集,1最少有一个匹配,只要有一个出现在preview中文档将被查询出来,如为2 则priview中两个词都要有才能被选中

GET /Index/type/_search
{
    "from":1,
    "size":2,
    "query":{
        "term":{
            "preview":["elasticsearch","book"],
            "minimum_match":1
        }
    }
}
简体:
GET /index/type/_search?q=preview:elasticsearch

match查询:

      会根据自指定的字段提供合适的分词器(安装分词器或用自带的)

GET /Index/type/_search{
    "query":{
        "match":{
            "preview":"elasticsearch"
        }
    }
}

还有match_phrase,其slop指定关键词之间要相隔多少个词,这个就不写了,占地;

常用的MySQL中有group by、order by、and 、or、not等,es中有没有相似的语法呐?既然我问了那应该是有的

{
    "bool":{
        "must":[],//and
        "should":[],//or
        "must_not:[]//not
    }
}
#官网上贴过来个看起来高大上的例子
POST _search
{
  "query": {
    "bool" : {
      "must" : {
        "term" : { "user" : "kimchy" }
      },
      "filter": {//filter通:过滤,符合规则 则添加到桶中,只影响聚合
        "term" : { "tag" : "tech" }
      },
      "must_not" : {//这个在bool里面,就是嵌套查了
        "range" : {//范围过滤,gte是大于等于、lte小于等于、gt 大于 lt:小于
          "age" : { "gte" : 10, "lte" : 20 }
        }
      },//bool结束
      "should" : [
        { "term" : { "tag" : "wow" } },
        { "term" : { "tag" : "elasticsearch" } }
      ],
      "minimum_should_match" : 1,//最少应该匹配1个
      "boost" : 1.0//类似权重,给符合添加的结果设定权重
    }
  }
}

另一种使用and、or、not查询:

{
    "filtered" : {
        "query" :   { ... },//默认match_all
        "filter" :  { ... },
        "strategy": "leap_frog"
    }
}
#实例如下
#The filtered query is passed as the value of the query parameter in the search request.
#在搜索请求中过滤后的查询作为查询参数
GET /index/type/_search
{
    "query":{
        "filtered":{
            "filter":{
                "bool":{##or
                    "should":[
                        {"term":{"productID":"124566"}},
                        {
                            "bool":{
                                "must":[##and
                                    {"term":{"productID":"24345"}},
                                    {"term":{"price":20}}
                                ]
                            }
                        }
                    ]
                }
            }
        }
    }
}
#翻译成SQL就是:
select document from products where productID = "124566" or (productID="24345" and price=20) 

post_filter:后置过滤器,查询完毕过滤搜索结果;只和聚合一起使用且使用了不同的过滤条件时使用

分组聚合aggs:

      对一份数据执行分组聚合,这个aggs里面可以进一步操作:count、avg、max、min等;

GET /tvs/sales/_search
{
        "size" : 0,##只获取聚合结果,而不要执行聚合的原始数据
        "aggs" : { 
        "popular_colors" : { ##给每个aggs起的名字,随便取什么都ok
             "histogram": {#按照给定值进行分组,price以2000为间隔分组
                "field": "price",
                "interval": 2000   #2000一个区域,数据对应放入
              },
             "terms" : { 
                 "field" : "color"##根据指定的字段color的值进行分组
              },
              "aggs": {//嵌套、专业点叫做下钻分析,多个维度、多次分组
                   "avg_price": {
                     "avg": {//取price的平均数avg,可以min、max、sum或count
                       "field": "price"
                     },
                      "cardinality": {#去重,具体:对brand去重
                        "field": "brand" 
                      }
                  }
               }
          }
     }
}
#相当于:Select avg(price) from  tvs.sales group by color
结果有一个默认的排序规则:
    按照doc_count降序排序,doc_count是es中bucket操作默认执行的一个内置metric(聚合统计操作:maxmin、count、avgsum)

此外还支持地理位置的查询,这个挺简单的,上网查吧;

增的话也挺简单的,用PUT和POST,可以插入一条也可以多条

PUT /website/blogs/1
{
  "title":    "我的第一篇博客",
  "content":     "这是我的第一篇博客,开通啦!!!",
  "userId":     1 
}
#多条
POST /company/rd_center/_bulk
{ "index": { "_id": "1" }}
{ "name": "北京研发总部", "city": "北京", "country": "中国" }
{ "index": { "_id": "2" }}
{ "name": "上海研发中心", "city": "上海", "country": "中国" }
{ "index": { "_id": "3" }}
{ "name": "硅谷人工智能实验室", "city": "硅谷", "country": "美国" }

JAVA API

     首先连上es:这个连接方式开始的代码一直报红线,弄了好长时间,刚开始接触es、又有任务在身,我真是要崩溃了,后来在社区里面shiyuan给了我下面的连接方式,当然es不只这一种连接方式,当时给我的代码同一个类import了两个包,所以悲剧了很长时间;在es中文社区得到了很多帮助,很谢谢大佬们的帮助,特别是laoyang360,现在看看自己问了很多不该问的问题,只怪那是太年轻

 public TransportClient Client() throws UnknownHostException {
        Settings settings = Settings.builder()
                .put("cluster.name", "elasticsearch")
                .put("client.transport.sniff", true)
                .build();
        TransportClient client = new PreBuiltTransportClient(settings)
                .addTransportAddress(new TransportAddress(InetAddress.getByName("192.168.22.95"), 9300));
        return  client;
    }

上面获得了集群或者es服务器的连接,接下来该index:

//如果index存在则插入 不存在则创建,这只是一种形式,兼顾建与增所以写了出来
IndexRequest indexRequest = new IndexRequest("car_shop", "cars", "1")
                .source(XContentFactory.jsonBuilder()
                        .startObject()
                        .field("brand", "宝马")
                        .field("name", "宝马320")
                        .field("price", 310000)
                        .field("produce_date", "2018-04-01")
                        .endObject()
                );

 UpdateRequest updateRequest = new UpdateRequest("car_shop", "cars", "1")
                .doc(XContentFactory.jsonBuilder()
                        .startObject()
                        .field("price", 310000)
                        .endObject())
                .upsert(indexRequest);

UpdateResponse updateResponse = client.update(updateRequest).get();
System.out.println(updateResponse.getVersion());//锁机制也用到了这个版本控制
client.close();

Elasticsearch的jar包为我们提供了很丰富的增删改特别是查的方法,如图
调用方法
可以点出来很多方法,根据需要使用
目前使用的是:boolQuery

QueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.matchQuery("brand","宝马"))//must
                .mustNot(QueryBuilders.termQuery("name.raw","宝马313"))//must_not
                .should(QueryBuilders.rangeQuery("produce_date").gte("2017-02-04").lte("2017-09-09"))//should
                .should(QueryBuilders.rangeQuery("price").gte(28000).lte(35000));//should

 SearchResponse searchResponse = client.prepareSearch("car_shop")//index
                .setTypes("cars")//type
                .setQuery(queryBuilder)//类似参数
                .get();//执行
  //response,这个应该能猜出来是什么吧,hits击中
for (SearchHit searchHit : searchResponse.getHits().getHits()) {
            System.out.println(searchHit.getSourceAsString());
}

还有disMaxQuery和curl格式、DSL查询差不多、灵活运用吧,学习中……:

 QueryBuilder disMaxQueryBuilder = QueryBuilders.disMaxQuery()
              .add(QueryBuilders.matchQuery("title","java solution"))
              .add(QueryBuilders.matchQuery("content","java solution"));

SearchResponse searchResponse = client.prepareSearch("index")
            .setTypes("type")
            .setQuery(disMaxQueryBuilder)
            .get();
 client.close();

多索引查询:

MultiGetResponse multiGetesponses = client.prepareMultiGet()
                    .add("car_shop", "cats", "1")
                    .add("car_shop", "cars", "2")
                    .get();
for (MultiGetItemResponse multiGetItemResponses : multiGetesponses) {
       GetResponse getResponse = multiGetItemResponses.getResponse();
             if (getResponse.isExists()) {
                System.out.println(getResponse.getSourceAsString());
             }
         }
client.close();

client这个也是可以查的:

SearchResponse searchResponse = client.prepareSearch("car_shop")
                .setTypes("cars")
                .setQuery(QueryBuilders.matchQuery("brand","宝马"))
                .setSize(1)//每个primary分片返回的文档数 
                .get();
searchResponse = client.prepareSearch("car_shop")
                .setTypes("cars")
                .setQuery(QueryBuilders.multiMatchQuery("宝马","brand","name"))//brand和name中有 宝马 这个值的
                .get();
searchResponse = client.prepareSearch("car_shop")
                .setTypes("cars")
                .setQuery(QueryBuilders.termQuery("name.raw", "宝马318"))
                .setScroll(new TimeValue(6000))//滚动的有效时长:保持搜索的上下文环境多长时间
                .get();
searchResponse = client.prepareSearch("car_shop")
                .setTypes("cars")
                .setQuery(QueryBuilders.prefixQuery("name", "宝"))//name以宝字开头的
                .get();

搜索请求一次请求最大量为[10000],但是为了请求大量数据应该怎么办呐?在Scroll帮助下我们可以做一个初始阶段搜索并且持续批量地从es中获取结果直到没有结果剩下,这有点像传统数据库里的cursors(游标)。
——————————————————————————————————————————
我们可以看出Scroll API的创建并不是为了实时的用户响应,而是为了处理大量的数据。从 scroll 请求返回的结果只是反映了 search 发生那一时刻的索引状态,就像一个快照。

我们通过SearchResponse对象的getScrollId()方法获取滚动ID;滚动ID将在下一次请求中使用,这个 ID 可以传递给 scroll API 来检索 下一个批次的结果,每次返回下一个批次结果 直到没有结果返回时停止

  do {
            for (SearchHit searchHit : searchResponse.getHits().getHits()) {
                System.out.println("batch" + ++batchCount);
                System.out.println(searchHit.getSourceAsString());
            }
            searchResponse = client.prepareSearchScroll(searchResponse.getScrollId())
                    .setScroll(new TimeValue(6000))
                    .execute()
                    .actionGet();
        } while (searchResponse.getHits().getHits().length != 0);

        client.close();

es命名很不错,看到名字大概就知道了他的作用,这样挺好,也就不做过多的解释了;
参考 参考 参考
滚动搜索(Scroll API)

如果获取位置,这个要怎么办呐?

//搜索两个坐标点组成的一个区域
SearchResponse searchResponse = client.prepareSearch("car_shop")
              .setTypes("shops")
              .setQuery(QueryBuilders.geoBoundingBoxQuery("pin.location")
                     .setCorners(40.74, -72.1, 30.43, -72.44))
              .get();

//指定一个区域,由三个坐标点,组成,比如上海大厦,东方明珠塔,上海火车站
 List<GeoPoint> points = new ArrayList<GeoPoint>();
 points.add(new GeoPoint(40.73, -74.1));
 points.add(new GeoPoint(40.01, -71.12));
 points.add(new GeoPoint(50.56, -90.58));
 searchResponse = client.prepareSearch("car_shop")
       .setTypes("shops")
       .setQuery(QueryBuilders.geoPolygonQuery("pin.location", points))
       .get();

//搜索距离当前位置在200公里内的4s店
searchResponse = client.prepareSearch("car_shop")
           .setTypes("shops")
           .setQuery(QueryBuilders.geoDistanceQuery("pin.location")
                   .point(40, -70)
                   .distance(200, DistanceUnit.KILOMETERS))
           .get();
 client.close();

利用模板查询也是es的一大特色(新建文件,保存到config/script目录下的后缀名是.mustache文件),有一个复用的思想:

 Map<String,Object> scriptParam = new HashMap<String,Object>();
 scriptParam.put("from",0);//模板所需参数
 scriptParam.put("size",1);
 scriptParam.put("brand","宝马");

//读取 模板文件 上面是参数  第一个是名字、类型、参数、索引……
SearchResponse searchResponse = new SearchTemplateRequestBuilder(client)
                .setScript("page_query_by_brand")//模板名称
//              .setScriptType(ScriptType.FILE)
//              .setScriptParams(scriptParams)
                .setRequest(new SearchRequest("car_shop").types("sales"))
                .get()
                .getResponse();

模板大概长这个样子:

{
  "query": {
    "bool": {
      "must": {
        "match": {
          "line": "{{text}}" 
        }
      },
      "filter": {
        {{#line_no}} 
          "range": {
            "line_no": {
              {{#start}} 
                "gte": "{{start}}" 
                {{#end}},{{/end}} 
              {{/start}} 
              {{#end}} 
                "lte": "{{end}}" 
              {{/end}} 
            }
          }
        {{/line_no}} 
      }
    }
  }
}

小结:
    es虽然有增删改查,不过单一职责嘛建议大家还是用他来查询,其他的事情可以让数据库或缓存来做,要学习的地方还要很多,感觉自己还没有入门,这些知识虽然写了博客,但是很尴尬。

[死磕Elasitcsearch]知识星球地址:http://t.cn/RmwM3N9;微信公众号:铭毅天下; 博客:https://blog.csdn.net/laoyang360

【中华石杉个人首页】

版权声明:本文为ma15732625261原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/ma15732625261/article/details/79950808

智能推荐

CentOS 7配置南大docker镜像

文章目录 CentOS 7配置南大docker镜像 0.帮助页面 1.系统要求 2.卸载旧版本(没有旧版本可跳过) 3.安装方式 4.准备工作 5.可选操作 Stable Test Nightly 6.安装docker引擎 7. (可选)修改配置文件防止与xshell连接冲突 8.启动docker CentOS 7配置南大docker镜像 0.帮助页面 南大docker源:https://mirr...

Qcon演讲纪实:详解如何在实时视频通话中实现AR功能

2018年4月20日-22日,由 infoQ 主办的 Qcon 2018全球软件开发大会在北京如期举行。声网首席 iOS 研发工程师,iOS 端移动应用产品设计和技术架构负责人龚宇华,受邀分享了《基于 ARkit 和 ARcore,在实时视频通话中实现 AR 功能》,在演讲中剖析了 AR 与 VR 差异,ARKit 的工作原理,以及逐步讲解如何基于 ARKit 与声网Agora SDK 创建 AR...

POJ2348 UVa10368 HDU1525 Euclid's Game【博弈】

Euclid's GameTime Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 4106    Accepted Submission(s): 1947 Probl...

使用Breeze.js编写更好的查询

这篇文章是由同行评审Agbonghama柯林斯 。 感谢所有SitePoint的审稿作出SitePoint内容也可以是最好的! 数据量正在迅速发展,他们正在变得越来越复杂,维护。 许多开发人员希望避免由数据问题他们的工作过程中造成的问题和头痛。 一个使我们的工作更轻松的图书馆是Breeze.js 。 在这篇文章中,我们将讨论我们如何能够写出更好的查询与Breeze.js。 但是首先,我们应该知道什...

Netty框架构建Nio编程

~~~ 随手点赞,养成习惯 ~~~ 为什么选择Netty框架 Netty是业界最流行的NIO框架之一,它的健壮性、功能、性能、可定制性和可扩展性在同类框架中都是首屈一指的。 优点: ① API使用简单,开发门槛低 ②功能强大,预置了多种编解码功能,支持多种主流协议 ③ 定制能力强,可以通过ChannelHandler对通信框架进行灵活地扩展; ④性能高,通过与其他业界主流的NIO框架对比,Nett...

猜你喜欢

【JZOJ5262】【GDOI2018模拟8.12】树(DP,性质题)

Description Solution 首先我们可以知道两个性质:1、路径u-v和路径v-w可以合并为路径u-w;2、路径u1-v1加路径u2-v2和路径u1-v2加路径u2-v1是等价的(就是起始点和终点可以互换) 那么知道这些性质之后就很好做了。我们只用知道每个点多少次做起点和多少次做终点。 我们设f[i]表示满足i子树的需求i上的值要是多少。 那么枚举i的所有儿子,判断a[i]-f[i],...

【String-easy】541. Reverse String II 反转的元素,有反转个数和间隔

1. 题目原址 https://leetcode.com/problems/reverse-string-ii/ 2. 题目描述 3. 题目大意 给定一个字符串,和字符串的间隔k, 这个k表示每k个数反转一次,然后再间隔k个元素再反转k个元素。 4. 解题思路 只要按照间隔去反转就可以了。然后间隔k个元素不反转是通过让i每次递增 2*k完成的。 5. AC代码 6. 相似题型 【1】344. Re...

【C语言笔记结构体】

我们都知道C语言中变量的类型决定了变量存储占用的空间。当我们要使用一个变量保存年龄时可以将其声明为int类型,当我们要使用一个变量保存某一科目的考试成绩时可以将其声明为float。 那么,当我们要做一个学生信息管理系统时,需要保存学生的姓名、学号、年龄等信息,该怎么做呢? 如当要保存三个学生的信息时, 方法一是: 方法二是: 显然,方法二跟更清晰,因为它把name、num、age都集成在一个模板,...

39. Combination Sum 回溯算法简析

LeetCode传送门     这道题要求给你一组正数 C,然后给你一个目标数 T,让你从那组C中找到加在一起等于 T 的那些组合。     例如:给你 [2,3,6,7] 和 7,则返回 [[2,2,3],[7] ] 。      想解决这个问题前,我们首先引入一个新问题,图(树)的遍历问题。  ...

git安装|Linux系统安装 git|Linux如何安装git?Linux通过远程安装git|

Git是一个开源的分布式版本控制系统,可以有效、高速地处理项目版本管理。Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。 开发者需要一个GIT账号,通过这个查看项目的提交记录,可以更加清楚项目的开发情况,便于版本控制。 以下介绍在CentOS8操作系统搭建GIT服务器。   一、安装GIT服务器流程   安装GIT...