ElasticSearch:基于lucene开发的全文检索工具,底层即lucene

核心概念

  • 索引 index 文档集合
  • 类型 type 所属类型
  • 字段 field 不同属性的分类标识
  • 映射 mapping 数据使用规则设置
  • 文档 document 一个文档 索引的基础信息单元
  • 接近实时 nrt 一个接近实时搜索平台
  • 集群 cluster 一个或多个节点组织在一起,一个集群有一个唯一名字标识
  • 节点 node 集群中的一个服务器
  • 分片和复制 shards&replicas 由于单个节点硬件限制而产生。如:数据太大,单台服务器无法解决。分片:将索引划分多份(可指定片数)并每片多份存储不同节点上。 复制:拉取分片拼合完整。

  • 索引index(拥有什么样的mapping,mapping内可创建多种类型)~数据库
  • 类型type(拥有什么属性即域field,类型内包含多个文档)~一张表
  • 文档document(根据type所拥有属性可添加属性和对应的值 即添加域field)~表中一条数据
  • 域field(拥有什么关键字和值)~表字段

操作

安装启动:elasticsearch
可用界面操作工具:elasticsearch-head-master 安装=npm install 、 启动=grunt server
发送请求的工具(可用可不用):postman 只是一个请求发送的工具

请求方式:

  • get:查询
  • post:更新
  • put:添加
  • delete:删除

索引

创建索引

url:地址:端口/索引名称
请求方式:put
请求体:json数据,即索引库的某些属性
{
    "mappings" :{
          "类型名称":{
                "properties": {
                     "id": {
                            "type": "long",
                            "store":true,
                            "index":"not_analyzed"
                     },
                     "title":{
                            "type": "text",
                            "store" : true,
                            "index" :"analyzed",
                            "analyzer":"standard"
                    },
                    "content": {
                            "type": "text",
                            "store" :true,
                            "index" :"analyzed",
                            "analyzer":"standard"
                    }
                }
          }
    }
}

设置mapping

url:地址:端口/索引名称/类型名称/_mappings
请求方式:post
请求体:json数据
{
      "类型名称":{
            "properties": {
                  属性...
            }
      }
}

Elasticsearch支持如下类型:

  • 字符串: text, keyword(注:5之前的版本里有string类型,5之后不再支持此类型)
  • 数字: byte, short, integer, long, float, double
  • 布尔型:boolean
  • 日期: date
  • 复杂类型:如object, nested等

删除索引

url:地址:端口/索引名称
请求方式:delete
请求体:可无

文档

要区分文档id和名称为id的属性两者是不同的

索引库添加文档

url:地址:端口/索引名称/类型名称[/自定义文档id(不指定id会默认生成)]
请求方式:put或post
请求体:json数据
{
      "已有某一属性名称":"值"
      ......
}

删除文档

url:地址:端口/索引名称/类型名称/文档id
请求方式:delete
请求体:可无

修改文档 先删除,后修改 通过文档id修改,保证修改文档id已存在即可修改某一文档

url:地址:端口/索引名称/类型名称/文档id
请求方式:post
请求体:json数据
{
    按照要求修改,例如:
    "id":123,
    "title":"修改后的title"
    ......
}

查询文档
1–根据id查询

url:地址:端口/索引名称/类型名称/文档id
请求方式:get
请求体:无

返回:结果

查询es数据_查询es数据

2–根据关键词查询

url:地址:端口/索引名称/类型名称/_search
请求方式:post
请求体:json数据
{
      "query":{
            "term":{
                  "属性名称":"值" 如:"title":"新"
            }
      }
}

返回:结果

查询es数据_big data_02

2–queryString查询 先分词,后查询

查询es数据_big data_03

查询es数据_大数据_04

分词器

标准分词器: 英文按照单词分词,中文只能按一个一个字分词 分词效果测试:

url:地址:端口/_analyze?analyzer=standard&pretty-true&text=xxx
请求方式:get
请求体:

查询es数据_elasticsearch_05

es集成ik中文分析器 下载ik分析器压缩包解压缩,放到es安装目录的plugins目录下 es启动会自动扫描该目录然后扫描ik配置文档

  • ik_smart 最少切分
  • ik_max_word 最细粒度划分

查询es数据_大数据_06

创建中文分词索引库

查询es数据_json数据_07

elasticSearch集群

  • 单服务器:复制多个elasticSearch,修改配置文件
#集群名称,保证唯一 
cluster.name: my-elasticsearch
#节点名称,必须不一样 
node.name: node-1
#必须为本机ip地址 
network.host: 127.0.0.1
#服务端口号,同一机器下必须不一样 
http.port: 9201
#集群间通信端口号,同一机器下必须不一样 
transport.tcp.port: 9301
#设置集群自动发现机器ip集合 
discovery.zen.ping.unicast.hosts: ["127.0.0.1:9301","127.0.0.1:9302","127.0.0.1:9303"]
  • 多服务器:配置配置文件,搭建集群

elasticSearch编程

java管理es相关操作

  • 创建java工程
  • 添加jar
<dependencies>
     <dependency>
        <groupId>org.elasticsearch</groupId>
        <artifactId>elasticsearch</artifactId>
        <version>5.6.8</version>
    </dependency>
    <dependency>
        <groupId>org.elasticsearch.client</groupId>
        <artifactId>transport</artifactId>
        <version>5.6.8</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-to-slf4j</artifactId>
        <version>2.9.1</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.24</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>1.7.21</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.12</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>
  • 编写方法

索引库、文档

package com.xiaoai.es;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.junit.Before;
import org.junit.Test;

import java.net.InetAddress;
import java.security.PublicKey;

public class ElasticSearchClientTest {

    //----创建索引库
    @Test
    public void createIndex() throws Exception{
//        1- 创建string对象配置集群名称
        Settings settings = Settings.builder()
                .put("cluster.name","my-elasticsearch") //设置es集群的名称
                .build();
//        2- 创建客户端client对象
        TransportClient client = new PreBuiltTransportClient(settings);
        //设置连接节点的ip、端口  多设置防止节点报废
        client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9301));
        client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9302));
        client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9303));
//        3- 使用客户端client对象创建索引库
        client.admin().indices()
                .prepareCreate("index_hello") // 设置创建索引名称
                .get();//get()为执行方法,前面为设置条件
//        4- 关闭client
        client.close();
    }

    //---索引库添加mapping
    @Test
    public void setMappings() throws Exception{
        //1-创建setting  配置
        Settings settings = Settings.builder().put("cluster.name","my-elasticsearch").build();
        //2-创建client  客户端
        TransportClient client = new PreBuiltTransportClient(settings);
        //设置连接节点的ip、端口 多设置防止节点报废
        client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9301));
        client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9302));
        client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9303));
        // 3-创建一个mapping信息
        XContentBuilder xcBuilder = XContentFactory.jsonBuilder()
                .startObject()
                    .startObject("article")
                        .startObject("properties")
                            .startObject("id")
                                .field("type","long")
                                .field("store",true)
                            .endObject()
                            .startObject("title")
                                .field("type","text")
                                .field("store",true)
                                .field("analyzer","ik_smart")
                            .endObject()
                            .startObject("content")
                                .field("type","text")
                                .field("store",true)
                                .field("analyzer","ik_smart")
                            .endObject()
                        .endObject()
                    .endObject()
                .endObject();
        //4- 使用客户端将mapping信息设置到索引库  也可以使用PutMappingRequest
        client.admin().indices()
                .preparePutMapping("index_hello")//设置添加索引库的名称
                .setType("article") //设置要做映射的type
                .setSource(xcBuilder)  //设置mappings信息
                .get();//get()为执行方法
        // 5-关闭client连接
        client.close();
    }

    //=======================================================================
    public TransportClient client;

    @Before
    public void init() throws Exception{ // 初始化client对象,不然每个方法都要写一遍太麻烦
        //1-创建setting  配置
        Settings settings = Settings.builder().put("cluster.name","my-elasticsearch").build();
        //2-创建client  客户端
        client = new PreBuiltTransportClient(settings);
        //设置连接节点的ip、端口 多设置防止节点报废
        client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9301));
        client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9302));
        client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9303));
    }

    //----创建文档插入数据1:
    @Test
    public void addDocument() throws Exception{
        // 1-创建client对象
        // 2-创建文档对象
        XContentBuilder builder = XContentFactory.jsonBuilder()
                .startObject()
                    .field("id",2)
                    .field("title","一个title-2")
                    .field("content","一个content-2")
                .endObject();
        // 3-文档加入索引库   参数:1=索引库名称 2-类型名称 3-文档id(不设置则会默认生成一个)
//        client.prepareIndex("index_hello","article","1").setSource(builder).get();//get()为执行方法
        //或者 无参方法
        client.prepareIndex()
                .setIndex("index_hello") //设置索引库名称
                .setType("article") //设置类型
                .setId("2") //设置文档id,不设置会默认生成
                .setSource(builder)  //设置文档信息,可以XContentBuilder对象,也可以是json的字符串
                .get();//get()为执行方法
        // 4- 关闭client
        client.close();
    }

    /**
     * ----创建文档插入数据2: 通过实体类转化为json字符串  利用jackson工具类
     * 需要导入jackson的jar包
     *  <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-databind</artifactId>
             <version>2.9.7</version>
             </dependency>
         <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-annotations</artifactId>
             <version>2.9.0</version>
         </dependency>
     * 不管使用XContentBuilder还是json字符串其最后都是要转成json字符串的
     * @throws Exception
     */
    @Test
    public void addDocumentToPojo() throws Exception{
        // 1-创建client对象
        // 2-创建实体类对象并设置属性
        Article article = new Article();
        article.setId(3);
        article.setTitle("这是一个title-3");
        article.setContent("这是一个content-3");
        // 3-实体类对象转换为json格式字符串
        ObjectMapper objectMapper = new ObjectMapper();
        String jsonDocument = objectMapper.writeValueAsString(article);
        // 4-json字符串 即文档写入索引库
        client.prepareIndex("index_hello","article","3")
                .setSource(jsonDocument, XContentType.JSON) //设置json字符串并指定其为一个json数据
                .get();//get()为执行方法
        //关闭client
        client.close();
    }

}

实体类

package com.xiaoai.es;

public class Article {
    private long id;
    private String title;
    private String content;

    public long getId() {return id;}
    public void setId(long id) {this.id = id;}
    public String getTitle() { return title;}
    public void setTitle(String title) {this.title = title;}
    public String getContent() {return content;}
    public void setContent(String content) {this.content = content;}
}

搜索

package com.xiaoai.es;

import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.index.query.QueryBuilder;
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.Before;
import org.junit.Test;

import java.net.InetAddress;
import java.util.Iterator;
import java.util.Map;

public class SearchIndex {

    public TransportClient client;

    @Before
    public void init() throws Exception{
        //1-创建setting  配置
        Settings settings = Settings.builder().put("cluster.name","my-elasticsearch").build();
        //2-创建client  客户端
        client = new PreBuiltTransportClient(settings);
        //设置连接节点的ip、端口 多设置防止节点报废
        client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9301));
        client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9302));
        client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9303));
    }


    //搜索=======================================================================
    /**
     * 1-创建client对象
     * 2-创建查询对象,可以使用QueryBuilders工具类创建QueryBuilder对象
     * 3-client查询
     * 4-得查询结果
     * 5-可以取结果总记录数
     * 6-取查询列表
     * 7-关闭client
     */
    //----搜索1--根据文档id搜索
    @Test
    public void searchById() throws Exception{
        // 1-client  创建client对象
        // 2-QueryBuilders  创建查询对象,可以使用QueryBuilders工具类创建QueryBuilder对象
        QueryBuilder queryBuilder = QueryBuilders.idsQuery().addIds("1","2");//查询id为1和2的数据
        // 3-SearchResponse client查询
        SearchResponse searchResponse = client.prepareSearch("index_hello")
                .setTypes("article")
                .setQuery(queryBuilder)
                .get();
        // 4-SearchHits  得查询结果
        SearchHits hits = searchResponse.getHits();
        // 5-可以取结果总记录数
        System.out.println("查询结果总记录数:"+hits.getTotalHits()); //hits.totalHits也可以
        // 6-取查询列表
        Iterator<SearchHit> iterator = hits.iterator();
        while (iterator.hasNext()){
            SearchHit searchHit = iterator.next();
            // 打印文档对象,一json格式输出
            System.out.println(searchHit.getSourceAsString());
            //取文档属性
            Map<String, Object> document = searchHit.getSource();
            System.out.println(document.get("id"));
            System.out.println(document.get("title"));
            System.out.println(document.get("content"));
            System.out.println("--------------------------------分隔");
        }
        // 7-关闭client
        client.close();
    }

    // 其实查询步骤都差不多,主要是创建Query时不同,可封装方法,传入Query对象就行
    private void search(QueryBuilder queryBuilder) throws Exception{
        // 3-SearchResponse client设置查询信息并执行查询
        SearchResponse searchResponse = client.prepareSearch("index_hello")
                .setTypes("article")
                .setQuery(queryBuilder)
                .setFrom(0)//起始行号,可通过setFrom、setSize两个属性来实现分页。
                .setSize(5)//每页可显示行数
                .get();
        // 4-SearchHits  得查询结果
        SearchHits hits = searchResponse.getHits();
        // 5-可以取结果总记录数
        System.out.println("查询结果总记录数:"+hits.getTotalHits()); //hits.totalHits也可以
        // 6-取查询列表
        Iterator<SearchHit> iterator = hits.iterator();
        while (iterator.hasNext()){
            SearchHit searchHit = iterator.next();
            // 打印文档对象,一json格式输出
            System.out.println(searchHit.getSourceAsString());
            //取文档属性
            Map<String, Object> document = searchHit.getSource();
            System.out.println(document.get("id"));
            System.out.println(document.get("title"));
            System.out.println(document.get("content"));
            System.out.println("--------------------------------分隔");
        }
        // 7-关闭client
        client.close();
    }

    //----搜索2--根据Term查询(即关键字查询)
    @Test
    public void searchByTerm() throws Exception{
        //创建具体查询
        QueryBuilder queryBuilder = QueryBuilders.termQuery("title","一个");//参数:1=要搜索的字段,即默认域field 2=要搜索的关键字
        search(queryBuilder);
    }

    //----搜索3--QueryString查询(先分词,后查询)
    @Test
    public void queryStringSearch() throws Exception{
        //创建具体查询
        QueryBuilder queryBuilder = QueryBuilders.queryStringQuery("这是美好的世界")//参数:1=要搜索的字段,即默认域field 2=要搜索的关键字
                .defaultField("title");//默认搜索域,不指定则全文搜索
        search(queryBuilder);
    }

}

查询es数据_大数据_08

查询es数据_json数据_09

查询es数据_查询es数据_10

小结

  • Settings settings = Settings.builder().put(“cluster.name”,“集群名称”).build() 配置集群名称
  • TransportClient client = new PreBuiltTransportClient(settings) 创建客户端 addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(“IP地址”),节点端口)) 添加节点地址
  • client.admin().indices().prepareCreate(“索引名称”).get() 创建索引
  • client.admin().indices().preparePutMapping(“索引名称”).setType(“类型名称”).setSource(XContentBuilder对象即json数据即maping信息).get() 添加mappings
  • XContentBuilder 可创建一个json数据
  • client.prepareIndex().setIndex(“索引名称”).setType(“类型名称”).setId(“文档id”).setSource(XContentBuilder对象即json数据即文档信息).get() 创建文档、插入数据
  • client.prepareIndex(“index_hello”,“article”,“3”).setSource(实体类转json后的string字符串, XContentType.JSON).get() 创建文档、插入数据(通过建立类型实体类,通过实体类对象转json字符串作为文档的信息)
  • 查询
  • QueryBuilder 创建查询
    - QueryBuilders.idsQuery().addIds(id…) 1-通过id查询
    - QueryBuilders.termQuery(“域”,“关键字”) 2-通过关键字查询
    - QueryBuilders.queryStringQuery(“这是美好的世界”).defaultField(“默认搜索域,不指定则全文检索”) 3-先分词,后查询
  • SearchResponse searchResponse = client.prepareSearch(“index_hello”).setTypes(“article”).setQuery(queryBuilder对象).get() 设置查询条件
  • SearchHits hits = searchResponse对象.getHits() 执行查询
  • .getTotalHits() 查询总记录
  • .iterator() 获取查询结果
  • 客户端对象.close()关闭客户端

高亮显示

//设置查询高亮显示
private void search(QueryBuilder queryBuilder,String highlightField) throws Exception{
    HighlightBuilder highlightBuilder = new HighlightBuilder();
    //高亮显示的字段
    highlightBuilder.field(highlightField);//那个域要高亮显示
    highlightBuilder.preTags("<em>"); //高亮前缀
    highlightBuilder.postTags("<em>");//高楼后缀
    // 3-SearchResponse client查询
    SearchResponse searchResponse = client.prepareSearch("index_hello")
            .setTypes("article")
            .setQuery(queryBuilder)
            .setFrom(0)//数据行从0开始
            .setSize(5)//每页可显示行数
            .highlighter(highlightBuilder)  //设置高亮显示
            .get();
    // 4-SearchHits  得查询结果
    SearchHits hits = searchResponse.getHits();
    // 5-可以取结果总记录数
    System.out.println("查询结果总记录数:"+hits.getTotalHits()); //hits.totalHits也可以
    // 6-取查询列表
    Iterator<SearchHit> iterator = hits.iterator();
    while (iterator.hasNext()){
        SearchHit searchHit = iterator.next();
        // 打印文档对象,一json格式输出
        System.out.println(searchHit.getSourceAsString());
        //取文档属性
        Map<String, Object> document = searchHit.getSource();
        System.out.println(document.get("id"));
        System.out.println(document.get("title"));
        System.out.println(document.get("content"));
        System.out.println("\n*********高亮结果");
        Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
        System.out.println(highlightFields);
        //取高亮结果
        HighlightField field = highlightFields.get(highlightField);
        Text[] fragments = field.getFragments();
        if (fragments!=null){
            String title = fragments[0].toString();
            System.out.println(title);
        }
        System.out.println();
        System.out.println("--------------------------------分隔");
    }
    // 7-关闭client
    client.close();
}

//高亮显示
@Test
public void queryStringSearch_gl() throws Exception{
    //创建具体查询
    QueryBuilder queryBuilder = QueryBuilders.queryStringQuery("这是美好的世界")//参数:1=要搜索的字段,即默认域field 2=要搜索的关键字
            .defaultField("title");//默认搜索域,不指定则全文搜索
    search(queryBuilder,"title");
}

查询es数据_elasticsearch_11

spring data elasticSearch

Spring Data是一个用于简化数据库访问,并支持云服务的开源框架。

相关操作

  • 引入jar
  • 配置application.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:elasticsearch="http://www.springframework.org/schema/data/elasticsearch"
       xsi:schemaLocation="
		http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/data/elasticsearch
		http://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-1.0.xsd
		">
    <!--elastic客户对象的配置-->
    <elasticsearch:transport-client id="esClient" cluster-name="my-elasticsearch" cluster-nodes="127.0.0.1:9301,127.0.0.1:9302,127.0.0.1:9303"/>

    <!--配置包扫描器,扫描dao的接口-->
    <elasticsearch:repositories base-package="com.xiaoai.es.repositories"/>
    <!---->
    <bean id="elasticsearchTemplate" class="org.springframework.data.elasticsearch.core.ElasticsearchTemplate">
        <constructor-arg name="client" ref="esClient"/>
    </bean>

</beans>
  • 创建实体类及映射
@Document(indexName="索引名称",type="类型", shards=分片数量, replicas=备份数量)
public class Item {

    @Id //标识id
    private Long id;

    @Field(type=属性类型(如:FieldType.Text),store=true或false(是否保存),analyzer="属性分词器(如:ik_max_word)")
    private String title; //标题

    其他属性及映射....

    属性对应set和get方法.......

}
  • 创建接口 继承ElasticsearchRepository<待操作实体类类型,主键id类型>
  • 引入接口类对象 引入ElasticsearchTemplate对象;
  • ElasticsearchTemplate对象.createIndex(映射实体类class) 创建索引
  • 接口类对象.save(实体类对象) 创建文档即插入文档信息 根据实体类id是否存在 存在=修改 不存在=新增
  • 接口类对象.deleteById(id) 根据id删除文档 .deleteAll()删除全部文档
  • 接口类对象.findAll 查询所有 .findById()根据id查询