Elasticsearch入门教程

1.  什么是 Elasticsearch

Elastic是一个实时的分布式搜索分析引擎, 它能让你以一个之前从未有过的速度和规模,去探索你的数据。 它被用作全文检索、结构化搜索、分析以及这三个功能的组合。

2.  安装

  • Elasticsearch 需要 Java 8 环境

2.1. Windows上运行ElasticSearch

下载安装包 elasticsearch-6.2.4.zip https://www.elastic.co/downloads/elasticsearch

解压压缩包,其目录结构如下:

从命令窗口运行位于bin文件夹中的elasticsearch.bat,可以使用CTRL + C停止或关闭它


见到xxxx started,那么就是启动完成了,打开浏览器输入http:\\localhost:9200http:\\127.0.0.1:9200,如果出现以下文本证明启动成功了。

{
  "name" : "ubH8NDf",  
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "sJfrIwlVRBmAArbWRLWyEA",  
  "version" : {
    "number" : "6.2.4",
    "build_hash" : "ccec39f",
    "build_date" : "2018-04-12T20:37:28.497551Z",
    "build_snapshot" : false,
    "lucene_version" : "7.2.1",
    "minimum_wire_compatibility_version" : "5.6.0",
    "minimum_index_compatibility_version" : "5.0.0"
  },
  "tagline" : "You Know, for Search"
}

 

默认情况下,Elastic 只允许本机访问,如果需要远程访问,可以修改 Elastic 安装目录的config/elasticsearch.yml文件,去掉network.host的注释,将它的值改成0.0.0.0,然后重新启动 Elastic。

network.host: 0.0.0.0

上面代码中,设成0.0.0.0让任何人都可以访问。线上服务不要这样设置,要设成具体的 IP。

2.2.  集群搭建

  1. 准备好三个文件夹
  2. 修改配置文件:进入到其中某个节点文件中config文件夹中,打开elasticsearch.yml进行配置
  3. 具体的配置信息参考如下:
    节点1的配置信息:  
    http.cors.enabled: true #是否允许跨域
    http.cors.allow-origin: "*"
    cluster.name: my-esLearn   #集群名称,保证唯一  
    node.name: node-1   #节点名称,必须不一样  
    network.host: 10.118.16.83   #必须为本机的ip地址  
    http.port: 9200   #服务端口号,在同一机器下必须不一样  
    transport.tcpport: 9300   #集群间通信端口号,在同一机器下必须不一样  
    #设置集群自动发现机器ip集合  
    discovery.zen.ping.unicast.hosts: ["10.118.16.83:9300", "10.118.16.83:9301", "10.118.16.83:9302"]  
    
    节点2的配置信息:  
    http.cors.enabled: true  #是否允许跨域
    http.cors.allow-origin: "*"
    cluster.name: my-esLearn   #集群名称,保证唯一  
    node.name: node-2   #节点名称,必须不一样  
    network.host: 10.118.16.83   #必须为本机的ip地址  
    http.port: 9201   #服务端口号,在同一机器下必须不一样  
    transport.tcpport: 9301   #集群间通信端口号,在同一机器下必须不一样  
    #设置集群自动发现机器ip集合  
    discovery.zen.ping.unicast.hosts: ["10.118.16.83:9300", "10.118.16.83:9301", "10.118.16.83:9302"]  
    
    节点3的配置信息:  
    http.cors.enabled: true #是否允许跨域
    http.cors.allow-origin: "*"
    cluster.name: my-esLearn   #集群名称,保证唯一  
    node.name: node-3   #节点名称,必须不一样  
    network.host: 10.118.16.83   #必须为本机的ip地址  
    http.port: 9202   #服务端口号,在同一机器下必须不一样  
    transport.tcpport: 9302   #集群间通信端口号,在同一机器下必须不一样  
    #设置集群自动发现机器ip集合  
    discovery.zen.ping.unicast.hosts: ["10.118.16.83:9300", "10.118.16.83:9301", "10.118.16.83:9302"] `
    View Code

2.3.  elasticsearch-head的搭建

解压已经下载 elasticsearch-head-master.zip,同时确认本机已经安装好nodejs,cmd->node -v确认nodejs是否安全成功。

解压压缩包,切换到elasticsearch-head-master已解压好的文件夹下。

c:\elasticsearch-head-master>npm install
c:\elasticsearch-head-master>npm start

用浏览器打开,http://10.118.16.83:9100/, 只要出现下图界面就证明成功了。

3.  基本概念

  • 集群(Cluster)

ES集群是一个或多个节点的集合,它们共同存储了整个数据集,并提供了联合索引以及可跨所有节点的搜索能力。多节点组成的集群拥有冗余能力,它可以在一个或几个节点出现故障时保证服务的整体可用性。集群靠其独有的名称进行标识,默认名称为“elasticsearch”。节点靠其集群名称来决定加入哪个ES集群,一个节点只能属一个集群。

  • 节点(node)

一个节点是一个逻辑上独立的服务,可以存储数据,并参与集群的索引和搜索功能, 一个节点也有唯一的名字,群集通过节点名称进行管理和通信.

  •  主节点

主节点的主要职责是和集群操作相关的内容,如创建或删除索引,跟踪哪些节点是群集的一部分,并决定哪些分片分配给相关的节点。稳定的主节点对集群的健康是非常重要的。虽然主节点也可以协调节点,路由搜索和从客户端新增数据到数据节点,但最好不要使用这些专用的主节点。一个重要的原则是,尽可能做尽量少的工作。

对于大型的生产集群来说,推荐使用一个专门的主节点来控制集群,该节点将不处理任何用户请求。

  • 数据节点

持有数据和倒排索引。

  •  客户端节点

它既不能保持数据也不能成为主节点,该节点可以响应用户的情况,把相关操作发送到其他节点;客户端节点会将客户端请求路由到集群中合适的分片上。对于读请求来说,协调节点每次会选择不同的分片处理请求,以实现负载均衡。

  • 索引Index

ES将数据存储于一个或多个索引中,索引是具有类似特性的文档的集合。类比传统的关系型数据库领域来说,索引相当于SQL中的一个数据库,或者一个数据存储方案(schema)。索引由其名称(必须为全小写字符)进行标识,并通过引用此名称完成文档的创建、搜索、更新及删除操作。一个ES集群中可以按需创建任意数目的索引。每个 Index (即数据库)的名字必须是小写。
下面的命令可以查看当前节点的所有 Index

$ curl -X GET 'http://10.118.16.83:9200/_cat/indices?v'
  • 文档类型(Type)

索引内部的逻辑分区(category/partition),然而其意义完全取决于用户需求。因此,一个索引内部可定义一个或多个类型(type)。一般来说,类型就是为那些拥有相同的域的文档做的预定义。例如,在索引中,可以定义一个用于存储用户数据的类型,一个存储日志数据的类型,以及一个存储评论数据的类型。类比传统的关系型数据库领域来说,类型相当于“表”

  •  文档Document

Lucene索引和搜索的原子单位,它是包含了一个或多个域的容器,基于JSON格式进行表示。文档由一个或多个域组成,每个域拥有一个名字及一个或多个值,有多个值的域通常称为“多值域”。每个文档可以存储不同的域集,但同一类型下的文档至应该有某种程度上的相似之处。相当于数据库的“记录”,例如:

{
  "user": "张三",
  "title": "工程师",
  "desc": "数据库管理"
}
View Code

同一个 Index 里面的 Document,不要求有相同的结构(scheme),但是最好保持相同,这样有利于提高搜索效率。

  •  Mapping

相当于数据库中的schema,用来约束字段的类型,不过 Elastic的 mapping 可以自动根据数据创建

ES中,所有的文档在存储之前都要首先进行分析。用户可根据需要定义如何将文本分割成token、哪些token应该被过滤掉,以及哪些文本需要进行额外处理等等。

分片(shard) :ES的“分片(shard)”机制可将一个索引内部的数据分布地存储于多个节点,它通过将一个索引切分为多个底层物理的Lucene索引完成索引数据的分割存储功能,这每一个物理的Lucene索引称为一个分片(shard)。

每个分片其内部都是一个全功能且独立的索引,因此可由集群中的任何主机存储。创建索引时,用户可指定其分片的数量,默认数量为5个

4.  数据操作

4.1.  文档更新

  4.1.1. 新增

向指定的 /Index/Type 发送 PUT 请求,就可以在 Index 里面新增一条记录。

$ curl -H "Content-Type:application/json" -X PUT '10.118.16.83:9200/megacorp/employee/1' -d '
{
    "first_name" : "John",
    "last_name" :  "Smith",
    "age" :        25,
    "about" :      "I love to go rock climbing",
    "interests": [ "sports", "music" ]
}'
View Code

返回:

{
    "_index": "megacorp",
    "_type": "employee",
    "_id": "1",
    "_version": 1,
    "result": "created",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 0,
    "_primary_term": 1
}
View Code

若不指定id, 则改为Post请求 。id字段就是一个随机字符串。

再新增两条:

$ curl -H "Content-Type:application/json" -X PUT '10.118.16.83:9200/megacorp/employee/2' -d '
{
    "first_name" :  "Jane",
    "last_name" :   "Smith",
    "age" :         32,
    "about" :       "I like to collect rock albums",
    "interests":  [ "music" ]
}'
 
$ curl -H "Content-Type:application/json" -X PUT '10.118.16.83:9200/megacorp/employee/3' -d '
{
    "first_name" :  "Douglas",
    "last_name" :   "Fir",
    "age" :         35,
    "about":        "I like to build cabinets",
    "interests":  [ "forestry" ]
}'
View Code

 4.1.2. 更新

更新记录就是使用 PUT 请求,重新发送一次数据。

$ curl -H "Content-Type:application/json" -X PUT '10.118.16.83:9200/megacorp/employee/1' -d '
{
    "first_name" : "John",
    "last_name" :  "Smith",
    "age" :        26,
    "about" :      "I love to go rock climbing",
    "interests": [ "sports", "music" ]
}' 
View Code
返回:
{
    "_index": "megacorp",
    "_type": "employee",
    "_id": "3",
    "_version": 2,
    "result": "updated",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 1,
    "_primary_term": 1
}
View Code

记录的 Id 没变,但是版本(version)从1变成2,操作类型(result)从created变成updated,created字段变成false,因为这次不是新建记录

4.1.3. 检索文档

$ curl '10.118.16.83:9200/megacorp/employee/1'

 删除

删除记录就是发出 DELETE 请求

$ curl -X DELETE '10.118.16.83:9200/megacorp/employee/1'

4.2.    返回所有记录

使用 GET 方法,直接请求/Index/Type/_search,就会返回所有记录。

$ curl '10.118.16.83:9200/accounts/person/_search'
 
{
    "took": 4,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 3,
        "max_score": 1,
        "hits": [
            {
                "_index": "megacorp",
                "_type": "employee",
                "_id": "2",
                "_score": 1,
                "_source": {
                    "first_name": "John",
                    "last_name": "Smith",
                    "age": 25,
                    "about": "I love to go rock climbing",
                    "interests": [
                        "sports",
                        "music"
                    ]
                }
            },
            …//此处省略
        ]
    }
}
View Code

上面代码中,返回结果的 took字段表示该操作的耗时(单位为毫秒),timed_out字段表示是否超时,hits字段表示命中的记录,里面子字段的含义如下。
total:返回记录数,本例是3条。
max_score:最高的匹配程度,本例是1.0。
hits:返回的记录组成的数组。
返回的记录中,每条记录都有一个_score字段,表示匹配的程序,默认是按照这个字段降序排列。

4.3.    匹配查询

Elastic提供两种方式:

l  Query-string 搜索通过命令非常方便地进行临时性的即席搜索 ,但它有自身的局限性。

$ curl '10.118.16.83:9200/megacorp/employee/_search?q=last_name:Smith'

l  提供一个丰富灵活的查询语言叫做 查询表达式 , 它支持构建更加复杂和健壮的查询。领域特定语言 (DSL), 指定了使用一个 JSON 请求

$ curl -H "Content-Type:application/json" '10.118.16.83:9200/megacorp/employee/_search'  -d '
{
  "query" : { "match" : { "last_name" : " Smith" }}
}'

4.4.    过滤查询

同样搜索姓氏为 Smith 的雇员,但这次我们只需要年龄大于 30 的。查询需要稍作调整,使用过滤器 filter [range ],它支持高效地执行一个结构化查询。

$ curl -H "Content-Type:application/json" '10.118.16.83:9200/megacorp/employee/_search'  -d '
{
  "query" : {
        "bool": {
            "must": {
                "match" : {
                    "last_name" : "smith" 
                }
            },
            "filter": {
                "range" : {
                    "age" : { "gt" : 30 } 
                }
            }
        }
    }
}' 
View Code

4.5.    全文搜索

截止目前的搜索相对都很简单:单个姓名,通过年龄过滤。现在尝试下稍微高级点儿的全文搜索——一项 传统数据库确实很难搞定的任务。搜索下所有喜欢攀岩(rock climbing)的雇员。

$ curl -H "Content-Type:application/json" '10.118.16.83:9200/megacorp/employee/_search'  -d '
{
    "query" : {
        "match" : {
            "about" : "rock climbing"
        }
    }
}'
View Code

得到两个匹配的文档

这是一个很好的案例,阐明了 Elasticsearch 如何 在 全文属性上搜索并返回相关性最强的结果。Elasticsearch中的 相关性 概念非常重要,也是完全区别于传统关系型数据库的一个概念,数据库中的一条记录要么匹配要么不匹配。

4.6.    短语搜索

找出一个属性中的独立单词是没有问题的,但有时候想要精确匹配一系列单词或者短语 。 比如, 我们想执行这样一个查询,仅匹配同时包含 “rock” 和 “climbing” ,并且 二者以短语 “rock climbing” 的形式紧挨着的雇员记录。

为此对 match 查询稍作调整,使用一个叫做 match_phrase 的查询:

$ curl -H "Content-Type:application/json" '10.118.16.83:9200/megacorp/employee/_search'  -d '
{
  "query" : {
        "match_phrase" : {
            "about" : "rock climbing"
        }
    }
}'
View Code

4.7.    高亮搜索

许多应用都倾向于在每个搜索结果中 高亮 部分文本片段,以便让用户知道为何该文档符合查询条件。在 Elasticsearch 中检索出高亮片段也很容易。

再次执行前面的查询,并增加一个新的 highlight 参数:

$ curl -H "Content-Type:application/json" '10.118.16.83:9200/megacorp/employee/_search'  -d '
{
  "query" : {
        "match_phrase" : {
            "about" : "rock climbing"
        }
    },
    "highlight": {
        "fields" : {
            "about" : {}
        }
    }
}'
View Code

查询结果:

4.8.    聚合

Elasticsearch 有一个功能叫聚合(aggregations),允许我们基于数据生成一些精细的分析结果。聚合与 SQL 中的 GROUP BY 类似但更强大。

举个例子,挖掘出雇员中最受欢迎的兴趣爱好:

$ curl -H "Content-Type:application/json" '10.118.16.83:9200/megacorp/employee/_search'  -d '
{
  "aggs": {
    "all_interests": {
      "terms": { "field": "interests" },
          "aggs": { //为指标新增aggs层

              "avg_age": { //指定指标的名字,在返回的结果中也是用这个变量名来储存数值的

                 "avg": {//指标参数:平均值

                       "field": "age" //明确求平均值的字段为'age'

                  }

              }

          }
}
  }
}'
View Code

执行失败:

{
  "error": {
    "root_cause": [
      {
        "type": "illegal_argument_exception",
        "reason": "Fielddata is disabled on text fields by default. Set fielddata=true on [interests] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory."
      }
    ],
    "type": "search_phase_execution_exception",
    "reason": "all shards failed",
    "phase": "query",
    "grouped": true,
    "failed_shards": [
      {
        "shard": 0,
        "index": "megacorp",
        "node": "-Md3f007Q3G6HtdnkXoRiA",
        "reason": {
          "type": "illegal_argument_exception",
          "reason": "Fielddata is disabled on text fields by default. Set fielddata=true on [interests] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory."
        }
      }
    ],
    "caused_by": {
      "type": "illegal_argument_exception",
      "reason": "Fielddata is disabled on text fields by default. Set fielddata=true on [interests] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory."
    }
  },
  "status": 400
}
View Code

排序,聚合这些操作用单独的数据结构(fielddata)缓存到内存里了,需要单独开启,官方解释在此fielddata

$ curl -H "Content-Type:application/json" -X POST '10.118.16.83:9200/megacorp/_mapping/employee' -d '

{

    "properties":{

        "interests":{

            "type":"text",

            "fielddata":true

        }

    }

}'
View Code

 

切记,这里一旦类型错误,以后可就麻烦咯

查询结果:

{"took":40,"timed_out":false,"_shards":{"total":5,"successful":5,"skipped":0,"failed":0},"hits":{"total":4,"max_score":1.0,"hits":[{"_index":"megacorp","_type":"employee","_id":"_cat","_score":1.0,"_source":{

    "properties":{

        "age":{

            "type":"integer",

            "fielddata":true

        }

    }

}

},{"_index":"megacorp","_type":"employee","_id":"2","_score":1.0,"_source":{

    "first_name" :  "Jane",

    "last_name" :   "Smith",

    "age" :         32,

    "about" :       "I like to collect rock albums",

    "interests":  [ "music" ]

 

}

},{"_index":"megacorp","_type":"employee","_id":"1","_score":1.0,"_source":{

    "first_name" : "John",

    "last_name" :  "Smith",

    "age" :        25,

    "about" :      "I love to go rock climbing",

    "interests": [ "sports", "music" ]

}

},{"_index":"megacorp","_type":"employee","_id":"3","_score":1.0,"_source":{

 "first_name" :  "Douglas",

    "last_name" :   "Fir",

    "age" :         35,

    "about":        "I like to build cabinets",

    "interests":  [ "forestry" ]
}

}]},"aggregations":{"all_interests":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"music","doc_count":2,"avg_age":{"value":28.5}},{"key":"forestry","doc_count":1,"avg_age":{"value":35.0}},{"key":"sports","doc_count":1,"avg_age":{"value":25.0}}]}}}
View Code

Java实现

@Test

    public void aggsTermsQuery() {

        SearchResponse response = client.prepareSearch("cars")

                .setTypes("transactions")

                .addAggregation(AggregationBuilders.terms("all_interests").field("interests")

                        .subAggregation(AggregationBuilders.avg("avg_price")

                                .field("price")))

                .setSize(0)

                .get();
        Map<String, Aggregation> aggMap = response.getAggregations().asMap();

        StringTerms teamAgg= (StringTerms) aggMap.get("popular_colors"); 

        Iterator<Bucket> teamBucketIt = teamAgg.getBuckets().iterator(); 

        while (teamBucketIt .hasNext()) { 

            Bucket buck = teamBucketIt .next(); 

            //max/min以此类推 

            logger.info(buck.getKeyAsString() + ", " + buck.getDocCount());

            Map<String, Aggregation> subAggMap = buck.getAggregations().asMap();

            long avgAgg= (long) ((InternalAvg)subAggMap.get("avg_price")).getValue();

            logger.info("avg:{}", avgAgg);
        } 

    }
View Code

4.9.  排序与分页

Elastic使用sort进行排序,默认一次返回10条结果,可以通过size字段改变这个设置。还可以通过from字段,指定位移。

GET /_search
{
    "query" : {
        "bool" : {
            "filter" : { "term" : { "user_id" : 1 }}
        }
    },
    "sort": { "date": { "order": "desc" }},
"from": 1,
    "size": 1
 
}
View Code

5.  中文分词设置

5.1. 安装分词插件

注意:安装对应版本的插件。
下载插件https://github.com/medcl/elasticsearch-analysis-ik/releases

5.2. 使用 IK Analysis

要使用 IK Analysis,需要在文档类里面,指定相应的分词器。

ik_max_word 和 ik_smart 区别
ik_max_word: 会将文本做最细粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,中华人民,中华,华人,人民共和国,人民,人,民,共和国,共和,和,国国,国歌”,会穷尽各种可能的组合;

ik_smart: 会做最粗粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,国歌”。

具体使用可参考https://github.com/medcl/elasticsearch-analysis-ik

public void highlighter() throws UnknownHostException {
    String preTags = "<strong>";  
    String postTags = "</strong>"; 
    HighlightBuilder highlightBuilder = new HighlightBuilder(); 
    highlightBuilder.preTags(preTags);//设置前缀  
    highlightBuilder.postTags(postTags);//设置后缀  
    highlightBuilder.field("content");

    SearchResponse response = client.prepareSearch("msg")
            .setTypes("tweet")
            .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
            .setQuery(QueryBuilders.matchQuery("content", "中国"))
            .highlighter(highlightBuilder)
            .setFrom(0).setSize(60).setExplain(true)
            .get();

    logger.info("search response is:total=[{}]",response.getHits().getTotalHits());
    SearchHits hits = response.getHits();
    for (SearchHit hit : hits) {
        logger.info("{} -- {} -- {}", hit.getId(), hit.getSourceAsString(), hit.getHighlightFields());
    }

    SearchRequestBuilder builder = client.prepareSearch("msg")
            .setTypes("tweet")
            .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
            .setQuery(QueryBuilders.matchQuery("content", "中国"));

} 
View Code

6.  通过Java程序连接Elasticsearch

 需要注意的是,我们通过浏览器 http://10.118.16.83:9200 访问可以正常访问,这里需要知晓,9200端口是用于Http协议访问的,如果通过客户端访问需要通过9300端口才可以访问

pom.xml添加依赖

<!-- Elasticsearch核心依赖包 -->
    <dependency>
        <groupId>org.elasticsearch.client</groupId>
        <artifactId>transport</artifactId>
        <version>6.2.4</version>
    </dependency>
    <!-- 日志依赖 -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.21</version>
        <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.8.2</version>
    </dependency>
View Code

6.1. 单点

import java.net.InetAddress;
import java.net.UnknownHostException;

import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.junit.Test;

public class ElasticsearchTest1 {
    public static final String HOST = "127.0.0.1";

    public static final int PORT = 9200; //http请求的端口是9200,客户端是9300

    @SuppressWarnings("resource")
    @Test
    public void test1() throws UnknownHostException {
        TransportClient client = new PreBuiltTransportClient(Settings.EMPTY).addTransportAddress(
                new TransportAddress(InetAddress.getByName(HOST), PORT));
        System.out.println("Elasticssearch connect info:" + client.toString());
        client.close();
    }
} 
View Code

6.2. 集群

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;

import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ElasticsearchTest2 {
    public static final String HOST = "10.118.16.83";

    public static final int PORT = 9300; //http请求的端口是9200,客户端是9300

    private TransportClient client = null;

    private Logger logger = LoggerFactory.getLogger(ElasticsearchTest2.class);

    public static final String CLUSTER_NAME = "my-esLearn"; //实例名称

    //1.设置集群名称:默认是elasticsearch,并设置client.transport.sniff为true,使客户端嗅探整个集群状态,把集群中的其他机器IP加入到客户端中  
    private static Settings settings = Settings.builder()
            .put("cluster.name",CLUSTER_NAME)  
            .put("client.transport.sniff", true)  
            .build();  

    /**
     * 获取客户端连接信息
     * @Title: getConnect
     * @author ld
     * @date 2018-05-03
     * @return void
     * @throws UnknownHostException
     */
    @SuppressWarnings("resource")
    @Before
    public void getConnect() throws UnknownHostException {
        client = new PreBuiltTransportClient(settings).addTransportAddress(
                new TransportAddress(InetAddress.getByName(HOST), PORT));
        logger.info("连接信息:" + client.toString());
    }

    @After
    public void closeConnect() {
        if (client != null) {
            client.close();
        }
    }

    @Test
    public void test1() throws UnknownHostException {
        logger.info("Elasticssearch connect info:" + client.toString());
    }

    @Test
    public void addIndex() throws IOException {
        IndexResponse response = client.prepareIndex("msg", "tweet", "2").setSource(
                XContentFactory.jsonBuilder()
                .startObject()
                .field("userName", "es")
                .field("msg", "Hello,Elasticsearch")
                .field("age", 14)
                .endObject()).get();
    }

    @Test
    public void search() {
        SearchResponse response = client.prepareSearch("msg")
                .setTypes("tweet")
                .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
                .setQuery(QueryBuilders.matchPhraseQuery("user_name", "es"))
                .setFrom(0).setSize(60).setExplain(true)
                .get();

        logger.info("search response is:total=[{}]",response.getHits().getTotalHits());
        SearchHits hits = response.getHits();
        for (SearchHit hit : hits) {
            logger.info(hit.getId());
            logger.info(hit.getSourceAsString());
        }
    }
}
View Code

7.  mysql与elasticsearch同步

可参考elasticsearch与数据库同步工具Logstash-input-jdbc

8.  与solr的比较

 

solr

Elastic

功能

官方提供的功能更多

官方功能少,但是第三方插件很丰富,扩展能力更强

建立索引和查询效率

建立索引的速度和Es差不多,索引建立完成后的检索速度也很快,但是一边建立索引一边搜索会很慢(建立索引时会造成IO阻塞)

 建立索引速度和solr差不多,第一次检索会比solr慢,之后就快了。一边建立索引一边搜索的速度不影响(

索引会先存在内存中,内存不足再写入磁盘,还有队列空闲时把索引写入硬盘)

支持的数据格式

Xml等多种格式

json

分布式管理

zookeeper

自己维护

Sharding

没有自动shard rebalancing的功能

Shard必须一次设置好,之后不能修改,如果要修改则需要重新建立索引

高级查询

没有Query DSL

有Query DSL,能够支持更加高级和复杂的查询语法,而且还可以以此扩展实现类sql语法的查询

搜索

传统搜索应用

实时搜索应用(1s的延迟)

插件

不支持插件式开发

支持插件式开发,丰富的插件库

 

9.  在线资源

Elasticsearch: 权威指南 

Elasticsearch 参考手册

https://www.elastic.co/guide/

中文社区

Java Api

 

posted @ 2018-05-14 10:45  羊皮纸月亮  阅读(4394)  评论(0编辑  收藏  举报