搜索引擎:Elasticsearch

(1)Elasticsearch和Solr的区别

Elasticsearch和Solr都是基于Lucene的分布式搜索引擎,它们具有高效、可扩展、分布式的特点。Elasticsearch主要适用于实时搜索、分析和数据可视化,Solr主要适用于企业级搜索。

Elasticsearch在大数据存储和实时搜索方面性能更优秀,Solr在文本分析、搜索语义理解等方面性能更强。Elasticsearch默认情况下集成了近实时搜索的功能,可以在几秒钟内从文档变更时就对新文档进行索引,而Solr需要手动设置使其能够支持实时搜索。

实例:假如有100万条数据需要搜索,使用Elasticsearch进行搜索,响应时间通常在毫秒或者数秒之间。而使用Solr进行搜索,其响应时间通常在数秒或者数十秒之间。

Elasticsearch和Solr在查询语法方面有所不同。Solr支持丰富的查询语法,能够满足更多复杂的查询需求;而Elasticsearch则采用了面向文档的查询方式,让用户能够更加方便地进行查询。同时,Elasticsearch支持通过API进行搜索以及通过Kibana进行数据可视化。

实例:假如有如下文本:“The quick brown fox jumps over the lazy dog”,我们可以用Solr进行复杂查询,如 “fox OR dog” 或 “The AND fox”,而在Elasticsearch中,我们可以更加直接地查询:“fox” 或者 “dog”。

Elasticsearch是一个开源项目,拥有庞大的社区。其API和插件的文档十分丰富,能够满足几乎所有的开发和使用需求。Solr也是一个开源项目,但是相较于Elasticsearch的社区,Solr的社区相对较小,在一些新特性的实现和开发方面可能会有所落后。

实例:如果你使用Elasticsearch中遇到了问题,你可以很容易地在论坛或者社区中找到支持和帮助,而如果你在使用Solr中遇到了问题,可能需要花费更长的时间来等待社区的响应。

(2)Elasticsearch配置

(1)【解压:elasticsearch-8.4.0】

配置:

elasticsearch.yml跨域配置

http.cors.enabled: true

http.cors.allow-origin: "*"

elasticsearch.yml访问不到9200解决

xpack.security.enabled: false

运行:elasticsearch.bat

访问:192.168.3.49:9200

(2)【解压:elasticsearch-head-master】

安装:nodejs

问题解决:

1.删除C:Users用户下的.npmrc文件

2.npm cache clean --force

3.npm install -g cnpm --registery=https://registery.npm.taobao.org

运行:npm install / npm run start

访问:192.168.3.49:9100

(3)【解压:kibana-8.4.0】

配置:kibana.yml配置中文

          i18n.locale: "zh-CN"

启动:kibana.bat

(4)【解压:ik分词器D:elasticsearch-8.4.0pluginsik】

(5)测试入门

最小切面

GET _analyze

{

  "analyzer": "ik_smart",

  "text": "中国共产党"

}

最细力度划分

GET _analyze

{

  "analyzer": "ik_max_word",

  "text": "中国共产党"

}

(6)自定义字典

【IKAnalyzer.cfg.xml】

<!--用户可以在这里配置自己的扩展字典 -->

<entry key="ext_dict">zhaoyang.dic</entry>

【zhaoyang.dic】

赵阳

赵哥

大连赵哥

(3)索引操作

(1)创建索引

ES 软件的索引可以类比为 MySQL 中表的概念,创建一个索引,类似于创建一个表

ES 不允许修改索引

# 创建索引

# PUT+索引名

PUT myindex

(2)查询指定索引

根据索引名称查询指定索引,如果查询到,会返回索引的详细信息

# 查询索引

# GET 索引名称

GET myindex

(3)查询所有索引

这里请求路径中的_cat 表示查看的意思,indices表示索引,所以整体含义就是查看当前 ES 服务器中的所有索引

# 查询索引

GET _cat/indices

(4)删除索引

删除索引

删除指定已存在的索引

# 删除索引

# DELETE+索引名称

DELETE test_inde

(4)文档操作

(1)创建文档

这里的文档可以类比为关系型数据库中的表数据,添加的数据格式为 JSON 格式

如果在创建数据时,指定唯一性标识,那么请求范式 POST,PUT 都可以

如果没有指定数据唯一性标识,只能使用 POST 请求

# 创建文档

# 创建文档

POST myindex/_doc/001

{

  "id" : 1001,

  "name" : "zhangsan",

  "age" : 30

}

POST myindex/_doc/002

{

  "id" : 1002,

  "name" : "lisi",

  "age" : 18

}

POST myindex/_doc/003

{

  "id" : 1004,

  "name" : "wangwu",

  "age" : 30

}

POST myindex/_doc/004

{

  "id" : 1004,

  "name" : "zhaoliu",

  "age" : 35

}

(2)查询文档

根据唯一性标识可以查询对应的文档

# 查询文档

GET myindex/_doc/001

(3)修改文档

修改文档本质上和新增文档是一样的,如果存在就修改,如果不存在就新增

# 修改文档

PUT myindex/_doc/001

{

  "age":20

}

(4)删除文档

删除一个文档不会立即从磁盘上移除,它只是被标记成已删除(逻辑删除)

# 删除文档

DELETE myindex/_doc/001

(5)查询所有文档

# 查询所有文档

GET myindex/_search

(5)数据搜索

(1)匹配查询文档

这里的查询表示文档数据中 JSON 对象数据中的 name 属性是lisi

GET myindex/_search

{

  "query": {

  "match": {

    "name": "lisi" #不会查出li si 此时查询关键字是lisi 而li si 的关键词是两个【li】【si】匹配不上

  }

  }

}

GET myindex/_search

{

  "query": {

    "term": {

      "name": {

        "value": "li si" #会查出li si 不会查出lisi  此时关键字是li si

      }

    }

  }

}

(2)匹配查询字段

默认情况下,Elasticsearch 在搜索的结果中,会把文档中保存在_source 的所有字段都返回。如果我们只想获取其中的部分字段,我们可以添加_source 的过滤

GET myindex/_search

{

  "_source": ["name","age"],

  "query": {

    "term": {

      "name": {

        "value": "lisi"

      }

    }

  }

}

(3)组合"or"

GET myindex/_search

{

  "_source": ["name","age"],

  "query": {

    "bool": {

      "should": [

        {

          "match": {

            "name": "lisi"

          }

        },

        {

          "match": {

            "age": 35

          }

        }

      ]

    }

  }

}

(4)排序

GET myindex/_search

{

  "query": {

    "match_all": {}

  },

  "sort": [

    {

      "age": {

        "order": "desc"

      }

    }

  ]

}

(5)分页

GET myindex/_search

{

  "query": {

    "match_all": {}

  },

  "from": 0,

  "size": 2

}

(6)分组

GET myindex/_search

{

"aggs": {

  "ageGroup": {

    "terms": {

      "field": "age"

    }

  }

},

"size": 0 #只显示分组信息 不显示源信息

}

(7)平均值

GET myindex/_search

{

"aggs": {

  "ageAvg": {

    "avg": {

      "field": "age"

    }

  }

},

"size": 0

}

(8)求和

GET myindex/_search

{

"aggs": {

  "ageGroup": {

    "terms": {

      "field": "age"

    },

    "aggs": {

      "ageSum": {

        "sum": {

          "field": "age"

        }

      }

    }

  }

},

"size": 0

}

(9)TopN

GET myindex/_search

{

"aggs": {

  "Top3": {

    "top_hits": {

      "sort": [

        {

          "age": {

            "order": "desc"

          }

        }

        ],

      "size": 3

    }

  }

},

"size": 0

}

(6)SpringBoot+Elasticsearch

        <dependency>

            <groupId>co.elastic.clients</groupId>

            <artifactId>elasticsearch-java</artifactId>

            <version>8.1.0</version>

        </dependency>

@Configuration

public class ElasticSearchConfig {

    @Bean

    public ElasticsearchClient elasticsearchClient(){

        RestClient client = RestClient.builder(new HttpHost("localhost", 9200,"http")).build();

        ElasticsearchTransport transport = new RestClientTransport(client,new JacksonJsonpMapper());

        return new ElasticsearchClient(transport);

    }

}

/**

*

* springBoot整合ElasticSearch8.x版本

*

*/

@SpringBootTest

class EsApplicationTests {

    @Autowired

    private ElasticsearchClient client;

    // 索引 CRUD

    // (1)增加index

    @Test

    public void createTest() throws IOException {

        CreateIndexResponse indexResponse = client.indices().create(c -> c.index("user"));

    }

    // (2)查询Index

    @Test

    public void queryTest() throws IOException {

        GetIndexResponse getIndexResponse = client.indices().get(i -> i.index("user"));

    }

    // (3)判断index是否存在

    @Test

    public void existsTest() throws IOException {

        BooleanResponse booleanResponse = client.indices().exists(e -> e.index("user"));

        System.out.println(booleanResponse.value());

    }

    // (4)删除index

    @Test

    public void deleteTest() throws IOException {

        DeleteIndexResponse deleteIndexResponse = client.indices().delete(d -> d.index("user"));

        System.out.println(deleteIndexResponse.acknowledged());

    }

    // Document CRUD

    // (1)插入document

    @Test

    public void addDocumentTest() throws IOException {

        User user = new User("user1", 10);

        IndexResponse indexResponse = client.index(i -> i

                .index("user")

                //设置id

                .id("1")

                //传入user对象

                .document(user));

    }

    // (2)更新Document

    @Test

    public void updateDocumentTest() throws IOException {

        UpdateResponse<User> updateResponse = client.update(u -> u

                        .index("user")

                        .id("1")

                        .doc(new User("user2", 13))

                , User.class);

    }

    // (3)判断Document是否存在

    @Test

    public void existDocumentTest() throws IOException {

        BooleanResponse indexResponse = client.exists(e -> e.index("user").id("1"));

        System.out.println(indexResponse.value());

    }

    // (4)查询Document

    @Test

    public void getDocumentTest() throws IOException {

        GetResponse<User> getResponse = client.get(g -> g

                        .index("user")

                        .id("1")

                , User.class

        );

        System.out.println(getResponse.source());

    }

    // (5)删除Document

    @Test

    public void deleteDocumentTest() throws IOException {

        DeleteResponse deleteResponse = client.delete(d -> d

                .index("user")

                .id("1")

        );

        System.out.println(deleteResponse.id());

    }

    // (6)批量插入Document

    @Test

    public void bulkTest() throws IOException {

        List<User> userList = new ArrayList<>();

        userList.add(new User("user1", 11));

        userList.add(new User("user2", 12));

        userList.add(new User("user3", 13));

        userList.add(new User("user4", 14));

        userList.add(new User("user5", 15));

        List<BulkOperation> bulkOperationArrayList = new ArrayList<>();

        //遍历添加到bulk中

        for(User user : userList){

            bulkOperationArrayList.add(BulkOperation.of(o->o.index(i->i.document(user))));

        }

        BulkResponse bulkResponse = client.bulk(b -> b.index("user")

                .operations(bulkOperationArrayList));

    }

    // (7)查询

    @Test

    public void searchTest() throws IOException {

        SearchResponse<User> search = client.search(s -> s

                .index("user")

                //查询name字段包含hello的document(不使用分词器精确查找)

                .query(q -> q

                        .term(t -> t

                                .field("name")

                                .value(v -> v.stringValue("hello"))

                        ))

                //分页查询,从第0页开始查询3个document

                .from(0)

                .size(3)

                //按age降序排序

                .sort(f->f.field(o->o.field("age").order(SortOrder.Desc))),User.class

        );

        for (Hit<User> hit : search.hits().hits()) {

            System.out.println(hit.source());

        }

    }

}