tags:
categories:
# 启动es http://192.168.44.128:9200 查看是否启动成功
docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms256m -Xmx256m" elasticsearch:7.6.2
# 设置允许跨域访问 为了elasticsearch-head连接访问
docker exec -it elasticsearch /bin/bash
cd config
echo "
http.cors.enabled: true
http.cors.allow-origin: '*' " >> elasticsearch.yml
docker restart elasticsearch
# 安装elasticsearch-head
docker search elasticsearch-head
docker run -d --name elasticsearh-head -p 9100:9100 mobz/elasticsearch-head:5
# 修改head的Content-Type设置 不修改的话创建索引会报错406
docker exec -it 9fcdebef858b /bin/bash
apt-get update
apt-get install vim
vim _site/vendor.js
# 1. 6886行 contentType: "application/x-www-form-urlencoded 改成 contentType: "application/json;charset=UTF-8"
# 2. 7574行 var inspectData = s.contentType ==`= "application/x-www-form-urlencoded" &&` 改成 var inspectData = s.contentType === "application/json;charset=UTF-8" &&
docker restart 9fcdebef858b
# 安装ik中文分词器 把压缩包拷贝到容器内plugins目录解压:
wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.6.2/elasticsearch-analysis-ik-7.6.2.zip
docker exec -it elasticsearch /bin/bash
cp plugin
unzip elasticsearch-analysis-ik-7.6.2.zip -d ik7.6
rm -rf elasticsearch-analysis-ik-7.6.2.zip
docker restart elasticsearch
POST /_analyze
{
"analyzer": "standard",
"text": "text文本"
}
{
"tokens": [
{
"token": "text",
"start_offset": 0,
"end_offset": 4,
"type": "" ,
"position": 0
},
{
"token": "文",
"start_offset": 4,
"end_offset": 5,
"type": "" ,
"position": 1
},
{
"token": "本",
"start_offset": 5,
"end_offset": 6,
"type": "" ,
"position": 2
}
]
}
POST /_analyze
{
"analyzer": "ik_max_word",
"text": "今天上下班车流量很大很拥堵"
}
{
"tokens": [
{
"token": "今天",
"start_offset": 0,
"end_offset": 2,
"type": "CN_WORD",
"position": 0
},
{
"token": "天上",
"start_offset": 1,
"end_offset": 3,
"type": "CN_WORD",
"position": 1
},
{
"token": "上下班",
"start_offset": 2,
"end_offset": 5,
"type": "CN_WORD",
"position": 2
},
{
"token": "上下",
"start_offset": 2,
"end_offset": 4,
"type": "CN_WORD",
"position": 3
},
{
"token": "下班",
"start_offset": 3,
"end_offset": 5,
"type": "CN_WORD",
"position": 4
},
{
"token": "班车",
"start_offset": 4,
"end_offset": 6,
"type": "CN_WORD",
"position": 5
},
{
"token": "车流量",
"start_offset": 5,
"end_offset": 8,
"type": "CN_WORD",
"position": 6
},
{
"token": "车流",
"start_offset": 5,
"end_offset": 7,
"type": "CN_WORD",
"position": 7
},
{
"token": "流量",
"start_offset": 6,
"end_offset": 8,
"type": "CN_WORD",
"position": 8
},
{
"token": "很大",
"start_offset": 8,
"end_offset": 10,
"type": "CN_WORD",
"position": 9
},
{
"token": "很",
"start_offset": 10,
"end_offset": 11,
"type": "CN_CHAR",
"position": 10
},
{
"token": "拥堵",
"start_offset": 11,
"end_offset": 13,
"type": "CN_WORD",
"position": 11
}
]
}
>
>org.springframework.boot >
>spring-boot-starter-data-elasticsearch >
>
spring:
application:
name: service-search
data:
elasticsearch:
cluster-name: docker-cluster
cluster-nodes: 192.168.44.128:9300
com.imooc.search.pojo.Stu
,索引名称stupackage com.imooc.search.pojo;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
@Document(indexName = "stu", type = "_doc")
public class Stu {
@Id
// @Field
private Long stuId;
@Field
private String name;
@Field
private Integer age;
@Field
private float money;
@Field
private String desc;
public Long getStuId() {
return stuId;
}
public void setStuId(Long stuId) {
this.stuId = stuId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public float getMoney() {
return money;
}
public void setMoney(float money) {
this.money = money;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
@Override
public String toString() {
return "Stu{" +
"stuId=" + stuId +
", name='" + name + '\'' +
", age=" + age +
", money=" + money +
", desc='" + desc + '\'' +
'}';
}
}
@Autowired
private ElasticsearchTemplate esTemplate;
@RequestMapping("hello")
public Object hello() {
return GraceJSONResult.ok();
}
@GetMapping("createIndex")
public Object createIndex() {
esTemplate.createIndex(Stu.class);
return GraceJSONResult.ok();
}
@GetMapping("deleteIndex")
public Object deleteIndex() {
esTemplate.deleteIndex(Stu.class);
return GraceJSONResult.ok();
}
_doc
。这里mapping就相当于表结构# POST /stu/_doc/_mapping 6.x
POST /stu/_mappings 7.x
{
"properties": {
"stuId": {
"type": "long"
},
"name": {
"type": "text",
"analyzer": "ik_max_word"
},
"age": {
"type": "integer"
},
"money": {
"type": "float"
},
"desc": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
@GetMapping("addDoc")
public Object addDoc() {
Stu stu = new Stu();
stu.setStuId(1002l);
stu.setAge(18);
stu.setName("imooc");
stu.setMoney(100.2f);
stu.setDesc("慕课网学习的学生");
IndexQuery query = new IndexQueryBuilder()
.withObject(stu)
.build();
esTemplate.index(query);
return GraceJSONResult.ok();
}
@GetMapping("updateDoc")
public Object updateDoc() {
Map<String, Object> updateMap = new HashMap<>();
updateMap.put("desc", "hello world");
updateMap.put("age", 22);
IndexRequest ir = new IndexRequest();
ir.source(updateMap);
UpdateQuery uq = new UpdateQueryBuilder()
.withClass(Stu.class)
.withId("1001")
.withIndexRequest(ir)
.build();
esTemplate.update(uq);
return GraceJSONResult.ok();
}
@GetMapping("getDoc")
public Object getDoc(String id) {
GetQuery getQuery = new GetQuery();
getQuery.setId(id);
Stu stu = esTemplate.queryForObject(getQuery, Stu.class);
return GraceJSONResult.ok(stu);
}
@GetMapping("deleteDoc")
public Object deleteDoc(String id) {
esTemplate.delete(Stu.class, id);
return GraceJSONResult.ok();
}
POST /articles/_doc/_mapping
{
"properties": {
"id": {
"type": "text"
},
"title": {
"type": "text",
"analyzer": "ik_max_word"
},
"categoryId": {
"type": "integer"
},
"articleType": {
"type": "integer"
},
"articleCover": {
"type": "keyword"
},
"publishUserId": {
"type": "text"
},
"publishTime": {
"type": "date"
}
}
}
model
模块中引入依赖和创建eo实体类com.imooc.pojo.eo.ArticleEO
。 >
>org.springframework.boot >
>spring-boot-starter-data-elasticsearch >
>
data:
mongodb:
uri: mongodb://root:[email protected]:27017
database: imooc-news
elasticsearch:
cluster-name: docker-cluster
cluster-nodes: 192.168.44.128:9300
package com.imooc.pojo.eo;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import java.util.Date;
@Data
@Document(indexName = "articles", type = "_doc")
public class ArticleEO {
@Id
private String id;
@Field
private String title;
@Field
private Integer categoryId;
@Field
private Integer articleType;
@Field
private String articleCover;
@Field
private String publishUserId;
@Field
private Date publishTime;
}
com.imooc.article.service.impl.ArticleServiceImpl
@Autowired
private ElasticsearchTemplate esTemplate;
@Transactional
@Override
public void updateArticleStatus(String articleId, Integer pendingStatus) {
Example example = new Example(Article.class);
Example.Criteria criteria = example.createCriteria();
criteria.andEqualTo("id", articleId);
Article pendingArticle = new Article();
pendingArticle.setArticleStatus(pendingStatus);
int res = articleMapper.updateByExampleSelective(pendingArticle, example);
if (res != 1) {
GraceException.display(ResponseStatusEnum.ARTICLE_REVIEW_ERROR);
}
// 如果审核通过,则查询article,把相应的数据字段信息存入es中
if (pendingStatus == ArticleReviewStatus.SUCCESS.type) {
Article result = articleMapper.selectByPrimaryKey(articleId);
// 如果是即时发布的文章,审核通过后则可以直接存入es中
if (result.getIsAppoint() == ArticleAppointType.IMMEDIATELY.type) {
ArticleEO articleEO = new ArticleEO();
BeanUtils.copyProperties(result, articleEO);
IndexQuery iq = new IndexQueryBuilder().withObject(articleEO).build();
esTemplate.index(iq);
}
// FIXME: 作业:如果是定时发布,此处不会存入到es中,需要在定时的延迟队列中去执行
}
}
deleteArticle
和withdrawArticle
中删除文档。esTemplate.delete(ArticleEO.class, articleId);
POST:http://192.168.44.128:9200/articles/_doc/_search
{
"query": {
"match_all": {}
}
}
POST: http://192.168.44.128:9200/articles/_doc/_search
{
"query": {
"term": {
"categoryId": "分类的id号"
}
}
}
POST: http://192.168.44.128:9200/articles/_doc/_search
{
"query": {
"match": {
"title": "查询关键字"
}
}
}
com.imooc.api.controller.article.ArticlePortalControllerApi
@GetMapping("es/list")
@ApiOperation(value = "首页通过es查询文章列表", notes = "首页查询文章列表", httpMethod = "GET")
public GraceJSONResult eslist(@RequestParam String keyword,
@RequestParam Integer category,
@ApiParam(name = "page", value = "查询下一页的第几页", required = false)
@RequestParam Integer page,
@ApiParam(name = "pageSize", value = "分页的每一页显示的条数", required = false)
@RequestParam Integer pageSize);
com.imooc.article.controller.ArticlePortalController#eslist
@Autowired
private ElasticsearchTemplate esTemplate;
@Override
public GraceJSONResult eslist(String keyword,
Integer category,
Integer page,
Integer pageSize) {
/**
* es查询:
* 1. 首页默认查询,不带参数
* 2. 按照文章分类查询
* 3. 按照关键字查询
*/
// es的页面是从0开始计算的,所以在这里page需要-1
if (page<1) return null;
page--;
Pageable pageable = PageRequest.of(page, pageSize);
SearchQuery query = null;
AggregatedPage<ArticleEO> pagedArticle = null;
// 符合第1种情况
if (StringUtils.isBlank(keyword) && category == null) {
query = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.matchAllQuery())
.withPageable(pageable)
.build();
pagedArticle = esTemplate.queryForPage(query, ArticleEO.class);
}
// 符合第2种情况
if (StringUtils.isBlank(keyword) && category != null) {
query = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.termQuery("categoryId", category))
.withPageable(pageable)
.build();
pagedArticle = esTemplate.queryForPage(query, ArticleEO.class);
}
// 符合第3种情况
// if (StringUtils.isNotBlank(keyword) && category == null) {
// query = new NativeSearchQueryBuilder()
// .withQuery(QueryBuilders.matchQuery("title", keyword))
// .withPageable(pageable)
// .build();
// }
// 基于第3种情况的关键字搜索高亮展示
String searchTitleField = "title";
if (StringUtils.isNotBlank(keyword) && category == null) {
String preTag = "";
String postTag = "";
query = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.matchQuery(searchTitleField, keyword))
.withHighlightFields(new HighlightBuilder.Field(searchTitleField)
.preTags(preTag)
.postTags(postTag)
)
.withPageable(pageable)
.build();
pagedArticle = esTemplate.queryForPage(query, ArticleEO.class, new SearchResultMapper() {
@Override
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
List<ArticleEO> articleHighLightList = new ArrayList<>();
SearchHits hits = response.getHits();
for (SearchHit h : hits) {
HighlightField highlightField = h.getHighlightFields().get(searchTitleField);
String title = highlightField.getFragments()[0].toString();
// 获得其他的字段信息数据,并且重新封装
String articleId = (String)h.getSourceAsMap().get("id");
Integer categoryId = (Integer)h.getSourceAsMap().get("categoryId");
Integer articleType = (Integer)h.getSourceAsMap().get("articleType");
String articleCover = (String)h.getSourceAsMap().get("articleCover");
String publishUserId = (String)h.getSourceAsMap().get("publishUserId");
Long dateLong = (Long)h.getSourceAsMap().get("publishTime");
Date publishTime = new Date(dateLong);
ArticleEO articleEO = new ArticleEO();
articleEO.setId(articleId);
articleEO.setCategoryId(categoryId);
articleEO.setTitle(title);
articleEO.setArticleType(articleType);
articleEO.setArticleCover(articleCover);
articleEO.setPublishUserId(publishUserId);
articleEO.setPublishTime(publishTime);
articleHighLightList.add(articleEO);
}
// 如果使用es7的话,这部分的代码会精简很多
return new AggregatedPageImpl<>((List<T>)articleHighLightList,
pageable,
response.getHits().totalHits);
}
@Override
public <T> T mapSearchHit(SearchHit searchHit, Class<T> type) {
return null;
}
});
}
// 重组文章列表
// AggregatedPage pagedArticle = esTemplate.queryForPage(query, ArticleEO.class);
List<ArticleEO> articEOleList = pagedArticle.getContent();
List<Article> articleList = new ArrayList<>();
for (ArticleEO a : articEOleList) {
// System.out.println(a);
Article article = new Article();
BeanUtils.copyProperties(a, article);
articleList.add(article);
}
// 重新封装成之前的grid格式
PagedGridResult gridResult = new PagedGridResult();
gridResult.setRows(articleList);
gridResult.setPage(page + 1);
gridResult.setTotal(pagedArticle.getTotalPages());
gridResult.setRecords(pagedArticle.getTotalElements());
gridResult = rebuildArticleGrid(gridResult);
return GraceJSONResult.ok(gridResult);
}
com.imooc.pojo.eo.FansEO
package com.imooc.pojo.eo;
import lombok.Data;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import javax.persistence.Id;
@Data
@Document(indexName = "fans", type = "_doc")
public class FansEO {
@Id
private String id;
@Field
private String writerId;
@Field
private String fanId;
@Field
private String face;
@Field
private String fanNickname;
@Field
private Integer sex;
@Field
private String province;
}
data:
mongodb:
uri: mongodb://root:[email protected]:27017
database: imooc-news
elasticsearch:
cluster-name: docker-cluster
cluster-nodes: 192.168.44.128:9300
com.imooc.user.service.impl.MyFanServiceImpl
@Autowired
private ElasticsearchTemplate esTemplate;
// 保存粉丝关系到es中
FansEO fansEO = new FansEO();
BeanUtils.copyProperties(fans, fansEO);
IndexQuery iq = new IndexQueryBuilder().withObject(fansEO).build();
esTemplate.index(iq);
// 删除es中的粉丝关系, DeleteQuery: 根据条件删除
DeleteQuery dq = new DeleteQuery();
dq.setQuery(QueryBuilders.termQuery("writerId", writerId));
dq.setQuery(QueryBuilders.termQuery("fanId", fanId));
esTemplate.delete(dq, FansEO.class);
com.imooc.user.service.MyFanService#queryMyFansESList
public PagedGridResult queryMyFansESList(String writerId,
Integer page,
Integer pageSize);
@Override
public PagedGridResult queryMyFansESList(String writerId,
Integer page,
Integer pageSize) {
page--;
Pageable pageable = PageRequest.of(page, pageSize);
SearchQuery query = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.termQuery("writerId", writerId))
.withPageable(pageable)
.build();
AggregatedPage<FansEO> pagedFans = esTemplate.queryForPage(query, FansEO.class);
PagedGridResult gridResult = new PagedGridResult();
gridResult.setRows(pagedFans.getContent());
gridResult.setPage(page + 1);
gridResult.setTotal(pagedFans.getTotalPages());
gridResult.setRecords(pagedFans.getTotalElements());
return gridResult;
}
GET http://192.168.44.128:9200/fan/_doc/_count
GET http://192.168.44.128:9200/fan/_doc/_count
{
"query":{
"bool":{
"must":[
{
"match":{
"writerId":"2011127T2T7D2HSW"
}
},
{
"term":{
"sex":"0"
}
}
]
}
}
}
GET http://192.168.44.128:9200/fan/_doc/_count
{
"query":{
"bool":{
"must":[
{
"match":{
"writerId":"2009067X5CTFMCH0"
}
},
{
"term":{
"province":"北京"
}
}
]
}
}
}
POST http://192.168.44.128:9200/fans/_doc/_search
{
"size": 0,
"query":{
"match":{
"writerId":"201116760SMSZT2W"
}
},
"aggs": {
"counts": {
"terms": {
"field": "sex"
}
}
}
}
POST http://192.168.44.128:9200/fans/_doc/_search
{
"size": 0,
"query":{
"match":{
"writerId":"201116760SMSZT2W"
}
},
"aggs": {
"counts": {
"terms": {
"field": "province"
}
}
}
}
com.imooc.user.service.impl.MyFanServiceImpl#queryRegionRatioESCounts
@Override
public List<RegionRatioVO> queryRegionRatioESCounts(String writerId) {
TermsAggregationBuilder aggregationBuilder = AggregationBuilders
.terms("region_counts")
.field("province");
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.matchQuery("writerId", writerId))
.addAggregation(aggregationBuilder)
.build();
Aggregations aggs = esTemplate.query(searchQuery, new ResultsExtractor<Aggregations>() {
@Override
public Aggregations extract(SearchResponse response) {
return response.getAggregations();
}
});
Map aggMap = aggs.asMap();
StringTerms strTerms = (StringTerms) aggMap.get("region_counts");
List bucketList = strTerms.getBuckets();
List<RegionRatioVO> list = new ArrayList<>();
for (int i = 0 ; i < bucketList.size() ; i ++) {
StringTerms.Bucket bucket = (StringTerms.Bucket)bucketList.get(i);
Long docCount = bucket.getDocCount();
String key = (String)bucket.getKey();
System.out.println(key);
System.out.println(docCount);
RegionRatioVO regionRatioVO = new RegionRatioVO();
regionRatioVO.setName(key);
regionRatioVO.setValue(docCount.intValue());
list.add(regionRatioVO);
}
return list;
}
com.imooc.user.controller.MyFansController#queryRatioByRegion
@Override
public GraceJSONResult queryRatioByRegion(String writerId) {
return GraceJSONResult.ok(myFanService
.queryRegionRatioESCounts(writerId));
}