ElasticSearch7.6
elasticsearch:
安装:
1 解压,
2 bin
3 启动
目录:
bin 启动文件
config 配置文件
log4j2 日志配置文件
jvm.options java 虚拟机相关的配置
elasticsearch.yml elasticsearch 的配置文件! 默认 9200 端口! 跨域!
lib 相关jar包
logs 日志!
modules 功能模块
plugins 插件!
安装可视化插件:
安装:
1 elasticsearch-head-master,解压
2 npm install npm run start , 需要node环境
3 解决跨域问题,elasticsearch->config->elasticsearch.yml配置:
http.cors.enabled: true
http.cors.allow-origin: "*"
Kibana:
1、解压后端的目录
2、启动
3、访问测试
4、开发工具! (Post、curl、head、谷歌浏览器插件测试!)
5、汉化,i18n.locale: "zh-CN"
ES核心概念
IK分词器插件
安装:
1、https://github.com/medcl/elasticsearch-analysis-ik
2、下载完毕之后,放入到我们的elasticsearch 插件,plugins目录
3、重启观察ES,可以看到ik分词器被加载了!
4、elasticsearch-plugin 可以通过这个命令来查看加载进来的插件
5、使用kibana测试!
其他:
ik_smart 和 ik_max_word,其中 ik_smart 为最少切分,ik_max_word为最细 粒度划分!
config/IKAnalyzer.cfg.xml 添加加载分词规则,main.dic默认加载 (必定添加main)
method | url地址 | 描述 |
---|---|---|
PUT | localhost:9200/索引名称/类型名称/文档id | 创建文档(指定文档id) |
POST | localhost:9200/索引名称/类型名称 | 创建文档(随机文档id) |
_update | llocalhost:9200/索引名称/类型名称/文档id/_update | 修改文档 |
DELETE | localhost:9200/索引名称/类型名称/文档id | 删除文档 |
GET | localhost:9200/索引名称/类型名称/文档id | 查询文档通过文档id |
POST | localhost:9200/索引名称/类型名称/_search | 查询所有数据 |
api操作
添加
PUT /zgc/user/2
{
"name": "狂神说java",
"age" : 10,
"desc": "还行",
"tags": ["技术宅","帅哥","渣男"]
}
PUT更新
空的字段会覆盖,不推荐
PUT /zgc/user/1
{
"name": "狂神说123",
"desc": "一顿操作猛如虎,一看工资2500",
"tags": ["技术宅","温暖","直男"]
}
POST更新
灵活,推荐
POST /zgc/user/1/_update
{
"doc":{
"name": "狂神说222222"
}
}
简单查询
GET /zgc/user/1
1,分词并且搜索词拆分
// 查询文档1,2
POST /zgc/user/_search?q=name:狂神说java
2,分词并且搜索词拆分
GET /zgc/user/_search
{
"query": {
"match": {
"name": "狂神说"
}
}
}
_source指定字段,sort排序,
GET /zgc/user/_search
{
"query": {
"match": {
"name": "狂神说"
}
}
, "_source": ["name","tags","age"]
,"sort": [
{
"age": {
"order": "asc"
}
}
]
,"from": 0
,"size": 2
}
most (and),should (or),must_not (not)
GET /zgc/user/_search
{
"query": {
"bool": {
"must_not": [
{
"match": {
"name": "java"
}
},
{
"match": {
"age": "10"
}
}
]
}
}
}
匹配多个值 or并且搜索词拆分
GET /zgc/user/_search
{
"query": {
"match": {
"tags": "男 帅哥"
}
}
}
term 不能把搜索词拆分
GET /zgc/user/_search
{
"query": {
"term": {
"name": "狂"
}
}
}
filter,过滤器
# query->bool->filter
# filter比query快的原因:
# 1 query:会先比较查询条件,然后计算分值,最后返回文档结果;filter: 1 对结果进行缓存 2 避免计算分值
GET /zgc/user/_search
{
"query": {
"bool": {
"filter": {
"range": {
"age": {
"gte": 10,
"lte": 200
}
}
}
}
}
}
高亮查询
GET /zgc/user/_search
{
"query": {
"match": {
"name": "狂神"
}
}
,"highlight": {
"fields": {
"name":{}
}
}
}
自定义高亮查询
GET /zgc/user/_search
{
"query": {
"match": {
"name": "狂神"
}
}
,"highlight": {
"fields": {
"name":{}
}
,"pre_tags": "<p class='key' style='color:red' >"
,"post_tags": "</p>"
}
}
集成SpringBoot
https://www.elastic.co/guide/en/elasticsearch/client/index.html
maven
<elasticsearch.version>7.6.1</elasticsearch.version>
springboot配置
// 新建ElasticSearchClientConfig文件
@Configuration
public class ElasticSearchClientConfig {
@Bean
public RestHighLevelClient restHighLevelClient() {
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost("127.0.0.1", 9200, "http")
)
);
return client;
}
}
包,搜autoconfigure->搜elasticearch->ElasticsearchRestClientAutoConfiguration
@Import({RestClientBuilderConfiguration.class, RestHighLevelClientConfiguration.class, RestClientFallbackConfiguration.class})
索引操作
@Autowired
private RestHighLevelClient restHighLevelClient;
// 创建索引
@Test
void createIndex() throws IOException {
CreateIndexRequest createIndexRequest = new CreateIndexRequest("zgc_index");
CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(createIndexRequest, RequestOptions.DEFAULT);
System.out.println(createIndexResponse);
}
//org.elasticsearch.client.indices.CreateIndexResponse@998a69c8
// 判断索引是否存在
@Test
void existIndex() throws IOException {
GetIndexRequest getIndexRequest = new GetIndexRequest("zgc_index");
boolean exists = restHighLevelClient.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
System.out.println(exists);
}
//true
// 删除索引
@Test
void delIndex() throws IOException {
DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest("zgc_index");
AcknowledgedResponse delete = restHighLevelClient.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
System.out.println(delete);
System.out.println(delete.isAcknowledged());
}
//org.elasticsearch.action.support.master.AcknowledgedResponse@4ee
//true
文档操作
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private String name;
private int age;
}
// 添加文档(行) put /zgc_index/_doc/1 {xxx}
@Test
void addDocument() throws IOException {
User user = new User("陈小周2", 18);
String string = JSON.toJSONString(user);
IndexRequest indexRequest = new IndexRequest("zgc_index");
indexRequest.source(string, XContentType.JSON);
indexRequest.timeout(TimeValue.timeValueSeconds(1));
indexRequest.id("2"); // 手动指定id
IndexResponse index = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
System.out.println(index.toString());
System.out.println(index.status());
}
//IndexResponse[index=zgc_index,type=_doc,id=2,version=1,result=created,seqNo=3,primaryTerm=1,shards={"total":2,"successful":1,"failed":0}]
//CREATED
// 判断文档是否存在(和判断索引差不多)
@SneakyThrows
@Test
void existDocument() throws IOException {
GetRequest getRequest = new GetRequest("zgc_index", "2");
boolean exists = restHighLevelClient.exists(getRequest, RequestOptions.DEFAULT);
System.out.println(exists);
}
//true
// 获取文档
@Test
void getDocument() throws IOException {
GetRequest getRequest = new GetRequest("zgc_index", "1");
GetResponse documentFields = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
System.out.println(documentFields);
System.out.println(documentFields.getSourceAsString());
}
//{"_index":"zgc_index","_type":"_doc","_id":"1","_version":3,"_seq_no":2,"_primary_term":1,"found":true,"_source":{"age":18,"name":"陈小周"}}
//{"age":18,"name":"陈小周"}
// 更新文档
@Test
void updateDucement() throws IOException {
UpdateRequest updateRequest = new UpdateRequest("zgc_index", "1");
User user = new User("陈大周", 99);
updateRequest.doc(JSON.toJSONString(user), XContentType.JSON);
updateRequest.timeout("1s");
UpdateResponse update = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
System.out.println(update);
System.out.println(update.status());
}
//UpdateResponse[index=zgc_index,type=_doc,id=1,version=4,seqNo=4,primaryTerm=1,result=updated,shards=ShardInfo{total=2, successful=1, failures=[]}]
//OK
// 删除文档
@Test
void delDocument() throws IOException {
DeleteRequest deleteRequest = new DeleteRequest("zgc_index","2");
deleteRequest.timeout("1s");
DeleteResponse delete = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
System.out.println(delete);
System.out.println(delete.status());
}
//DeleteResponse[index=zgc_index,type=_doc,id=2,version=2,result=deleted,shards=ShardInfo{total=2, successful=1, failures=[]}]
//OK
批量插入
// 批量插入
@Test
void bulkDocument() throws IOException {
ArrayList<User> users = new ArrayList<>();
users.add(new User("aa", 10));
users.add(new User("bb", 11));
users.add(new User("cc", 12));
BulkRequest bulkRequest = new BulkRequest();
bulkRequest.timeout("10s");
for (int i = 0; i < users.size(); i++) {
bulkRequest.add(new IndexRequest("zgc_index").id("" + (i + 1)).source(JSON.toJSONString(users.get(i)),XContentType.JSON));
}
BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
System.out.println(bulk);
System.out.println(bulk.status());
System.out.println(bulk.hasFailures()); // 返回false代表成功
}
//org.elasticsearch.action.bulk.BulkResponse@1f39269d
//OK
//false
查询
// 查询
@Test
void search() throws IOException {
// 查询请求
SearchRequest searchRequest = new SearchRequest("zgc_index");
// 查询构造
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 查询条件 1和2
// TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name","aa");
QueryBuilder termQueryBuilder = new TermQueryBuilder("name", "aa");
// searchSourceBuilder.highlighter();
searchSourceBuilder.query(termQueryBuilder);
searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
searchRequest.source(searchSourceBuilder);
SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
System.out.println(search);
System.out.println(JSON.toJSONString(search.getHits()));
System.out.println("----------------------------------");
for (SearchHit hit : search.getHits()) {
System.out.println(hit.getSourceAsMap());
}
System.out.println("===========================================");
for (SearchHit hit : search.getHits().getHits()) {
System.out.println(hit.getSourceAsMap());
}
}
//{"took":12,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":1,"relation":"eq"},"max_score":0.9808291,"hits":[{"_index":"zgc_index","_type":"_doc","_id":"1","_score":0.9808291,"_source":{"age":10,"name":"aa"}}]}}
//{"fragment":true,"hits":[{"fields":{},"fragment":false,"highlightFields":{"$ref":"$.hits[0].fields"},"id":"1","matchedQueries":[],"primaryTerm":0,"rawSortValues":[],"score":0.9808291,"seqNo":-2,"sortValues":[],"sourceAsMap":{"name":"aa","age":10},"sourceAsString":"{\"age\":10,\"name\":\"aa\"}","sourceRef":{"fragment":true},"type":"_doc","version":-1}],"maxScore":0.9808291,"totalHits":{"relation":"EQUAL_TO","value":1}}
//----------------------------------
//{name=aa, age=10}
//===========================================
//{name=aa, age=10}
京东搜索实战
访问静态页面
注意:上文修改ElasticSearch版本包的添加config包和其文件,导入静态资源
server.port=9090
# 关闭thymeleaf的缓存
spring.thymeleaf.cache=false
@Controller
public class Jd {
@GetMapping({"/", "/jd"})
// @ResponseBody
public String jd() {
return "index";
}
}
// 可以访问首页
数据爬取
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.10.2</version>
</dependency>
package com.zgc.esjd3.util;
import com.zgc.esjd3.pojo.Content;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
@Component
public class HtmlParseUtil {
// public static void main(String[] args) throws IOException {
// HtmlParseUtil htmlParseUtil = new HtmlParseUtil();
// htmlParseUtil.parseJd("java");
// }
public ArrayList<Content> parseJd(String keyword) throws IOException {
// url
String url = "https://search.jd.com/Search?keyword=" + keyword;
// 解析网页,返回Document
Document parse = Jsoup.parse(new URL(url), 3000);
Element j_goodsList = parse.getElementById("J_goodsList");
Elements elements = j_goodsList.getElementsByTag("li");
ArrayList<Content> contentArrayList = new ArrayList<>();
for (Element element : elements) {
String img = element.getElementsByTag("img").eq(0).attr("src");
String price = element.getElementsByClass("p-price").eq(0).text();
String title = element.getElementsByClass("p-name").eq(0).text();
if (title.length() != 0 && img != null) {
Content content = new Content(title, img, price);
contentArrayList.add(content);
}
}
System.out.println(contentArrayList);
return contentArrayList;
}
}
数据存进es
package com.zgc.esjd3.service;
import com.alibaba.fastjson.JSON;
import com.zgc.esjd3.pojo.Content;
import com.zgc.esjd3.util.HtmlParseUtil;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.ArrayList;
@Service
public class ContentService {
@Autowired
private RestHighLevelClient restHighLevelClient;
// Jsoup数据放es
public boolean parseContent(String keyword) throws IOException {
BulkRequest bulkRequest = new BulkRequest();
bulkRequest.timeout("3m");
ArrayList<Content> contents = new HtmlParseUtil().parseJd(keyword);
for (int i = 0; i < contents.size(); i++) {
bulkRequest.add(new IndexRequest("jd_index").id("" + (i + 1)).source(JSON.toJSONString(contents.get(i)), XContentType.JSON));
}
BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
return !bulk.hasFailures(); // hasFailures返回false为成功
}
}
@GetMapping("/jdTest/{keyword}")
@ResponseBody
public String jdTest(@PathVariable String keyword) throws IOException {
System.out.println(keyword);
boolean b = contentService.parseContent(keyword);
System.out.println(b);
return keyword;
}
// 访问http://localhost:9090/jdTest/java,成功把数据录入es
实现搜索功能
//分页+搜索+高亮业务编写
public List<Map<String, Object>> searchPage(String keyword, int pageNo, int pageSize) throws IOException {
if (pageNo <= 1) {
pageNo = 1;
}
// 查询请求
SearchRequest searchRequest = new SearchRequest("jd_index");
// 查询构造
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//分页
searchSourceBuilder.from(pageNo);
searchSourceBuilder.size(pageSize);
//精准匹配
TermQueryBuilder queryBuilder = QueryBuilders.termQuery("title", keyword);
searchSourceBuilder.query(queryBuilder);
//高亮
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("title");
highlightBuilder.requireFieldMatch(false); //多处高亮
highlightBuilder.preTags("<span style= 'color:red'>");
highlightBuilder.postTags("</span>");
searchSourceBuilder.highlighter(highlightBuilder);
// 执行搜索
searchRequest.source(searchSourceBuilder);
SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
System.out.println(search);
ArrayList<Map<String, Object>> list = new ArrayList<>();
for (SearchHit hit : search.getHits()) {
//解析高亮的字段,将原来的字段换为我们高亮的字段即可
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
HighlightField title1 = highlightFields.get("title");
Map<String, Object> sourceAsMap = hit.getSourceAsMap();//原来的结果
//解析高亮的字段,将原来的字段换为我们高亮的字段即可
if (title1 != null) {
Text[] fragments = title1.fragments();
String n_title = "";
for (Text text : fragments) {
n_title += text;
}
sourceAsMap.put("title", n_title);
}
list.add(sourceAsMap);
}
return list;
}
@GetMapping({"/", "/jd"})
// @ResponseBody
public String jd() {
return "index";
}
@GetMapping("/search/{keyword}/{pageNo}/{pageSize}")
@ResponseBody
public List<Map<String, Object>> search(@PathVariable String keyword, @PathVariable Integer pageNo, @PathVariable Integer pageSize) throws IOException {
return contentService.searchPage(keyword,pageNo,pageSize);
}
下载 vue:
cnpm install vue
cnpm install axios
把两个文件放入static