Commit e9b52753 authored by 张辰's avatar 张辰

es

parent cf95830f
package com.tanpu.community.api.beans.qo;
import com.tanpu.community.api.beans.vo.ImagesDTO;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
public class ESThemeQo {
@ApiModelProperty(value = "主题ID")
public String themeId;
@ApiModelProperty(value = "标题")
public String title;
@ApiModelProperty(value = "类型 1:讨论无标题 2:长文有标题 3:转发 4:评论")
public Integer themeType;
@ApiModelProperty(value = "内容")
public String content;
@ApiModelProperty(value = "话题名称")
public String topicTitle;
@ApiModelProperty(value = "昵称")
public String nickName;
@ApiModelProperty(value = "作者认证")
public String authLabel;
@ApiModelProperty(value = "转发的主题")
public FormerThemeQo formerTheme;
@ApiModelProperty(value = "评论Id(我的评论列表使用)")
public String commentId;
public Long createTime;
public Long updateTime;
}
......@@ -18,74 +18,74 @@ import java.util.List;
@ApiModel("主题信息流对象")
public class ThemeQo implements Serializable {
private static final long serialVersionUID = -8585648478551597699L;
public static final long serialVersionUID = -8585648478551597699L;
@ApiModelProperty(value = "主题ID")
private String themeId;
public String themeId;
@ApiModelProperty(value = "被转发的主题")
private String formerThemeId;
public String formerThemeId;
@ApiModelProperty(value = "标题")
private String title;
public String title;
@ApiModelProperty(value = "类型 1:讨论无标题 2:长文有标题 3:转发 4:评论")
private Integer themeType;
public Integer themeType;
@ApiModelProperty(value = "内容")
private List<ThemeContentQo> content;
public List<ThemeContentQo> content;
@ApiModelProperty(value = "作者id")
private String authorId;
public String authorId;
@ApiModelProperty(value = "所属的话题")
private String topicId;
public String topicId;
@ApiModelProperty(value = "话题名称")
private String topicTitle;
public String topicTitle;
@ApiModelProperty(value = "昵称")
private String nickName;
public String nickName;
@ApiModelProperty(value = "用户头像")
private String userImg;
public String userImg;
@ApiModelProperty(value = "当前用户是否关注该作者")
private boolean follow;
public boolean follow;
@ApiModelProperty(value = "作者认证")
private String authLabel;
public String authLabel;
@ApiModelProperty(value = "发表时间")
private String upToNowTime;
public String upToNowTime;
@ApiModelProperty(value = "转发量")
private Integer forwardCount;
public Integer forwardCount;
@ApiModelProperty(value = "评论量")
private Integer commentCount;
public Integer commentCount;
@ApiModelProperty(value = "点赞量")
private Integer likeCount;
public Integer likeCount;
@ApiModelProperty(value = "当前用户是否点赞")
private boolean hasLiked;
public boolean hasLiked;
@ApiModelProperty(value = "当前用户是否转发")
private boolean hasForward;
public boolean hasForward;
@ApiModelProperty(value = "图片九宫格")
private List<ImagesDTO> imgList;
public List<ImagesDTO> imgList;
@ApiModelProperty(value = "转发的主题")
private FormerThemeQo formerTheme;
public FormerThemeQo formerTheme;
@ApiModelProperty(value = "评论Id(我的评论列表使用)")
private String commentId;
public String commentId;
private Long createTime;
public Long createTime;
private Long updateTime;
public Long updateTime;
}
package com.tanpu.community.api.beans.req.search;
import com.tanpu.community.api.beans.req.page.Pageable;
import lombok.Data;
@Data
public class ThemeFullSearchReq extends Pageable {
public String keyword;
}
......@@ -9,7 +9,7 @@ import java.time.LocalDateTime;
@Data
@ApiModel(value="TopicDTO对象", description="话题")
public class TopicDO implements Serializable {
public class TopicDTO implements Serializable {
private static final long serialVersionUID = 1L;
......
......@@ -38,6 +38,18 @@
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.10.0</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.10.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
......
......@@ -2,6 +2,7 @@ package com.tanpu.community;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.scheduling.annotation.EnableScheduling;
......@@ -12,6 +13,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
@EnableCaching
@EnableScheduling
@EnableFeignClients
@EnableConfigurationProperties
public class CommunityApplication {
public static void main(String[] args) {
......
package com.tanpu.community.cache;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.type.TypeReference;
import com.tanpu.common.redis.RedisHelper;
import com.tanpu.common.redis.RedisKeyHelper;
import com.tanpu.common.util.JsonUtil;
import com.tanpu.community.util.SpringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
......@@ -20,22 +22,26 @@ public class RedisCache {
String value = get(key);
// todo 考虑缓存穿透的问题.
if (StringUtils.isNotBlank(value)) {
return JSON.parseObject(value, clz);
return JsonUtil.toBean(value, clz);
}
T ret = func.get();
if (ret != null) {
put(key, ret, expireSeconds);
}
return ret;
}
public<T> List<T> getList(String key, Integer expireSeconds, Supplier<List<T>> func, Class<T> clz) {
public<T> T getList(String key, Integer expireSeconds, Supplier<T> func, TypeReference<T> ref) {
String value = get(key);
if (StringUtils.isNotBlank(value)) {
return JSON.parseArray(value, clz);
return JsonUtil.toBean(value, ref);
}
List<T> ret = func.get();
T ret = func.get();
if (ret != null) {
put(key, ret, expireSeconds);
}
return ret;
}
......@@ -45,8 +51,6 @@ public class RedisCache {
}
private void put(String key, Object obj, Integer expireSeconds) {
if (obj == null) return;
key = cacheName + ":" + key;
String value = JSON.toJSONString(obj);
if (expireSeconds == 0) {
......@@ -62,6 +66,7 @@ public class RedisCache {
public Builder cacheName(String cacheName) {
cache.cacheName = cacheName;
cache.redisHelper = SpringUtils.getBean(RedisHelper.class);
return this;
}
public RedisCache build() {
......
package com.tanpu.community.config;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "es")
public class ESConfig {
private String userName;
private String userPasswd;
private String host;
private Integer port;
@Bean
public RestHighLevelClient client() {
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(userName, userPasswd));
RestHighLevelClient restHighLevelClient = new RestHighLevelClient(
RestClient.builder(new HttpHost(host, port)).setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
@Override
public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
}
}));
return restHighLevelClient;
}
}
package com.tanpu.community.controller;public class SearchController {
package com.tanpu.community.controller;
import com.tanpu.common.api.CommonResp;
import com.tanpu.common.auth.UserHolder;
import com.tanpu.common.auth.UserInfoHelper;
import com.tanpu.community.api.beans.qo.ThemeQo;
import com.tanpu.community.api.beans.qo.TopicDetailQo;
import com.tanpu.community.api.beans.req.search.ThemeFullSearchReq;
import com.tanpu.community.manager.ThemeManager;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
@RestController
@Slf4j
@RequestMapping(value = "/api/search")
public class SearchController {
@Resource
private UserHolder userHolder;
@Autowired
private ThemeManager themeManager;
// 内容全文搜索
public CommonResp<List<ThemeQo>> themeSearch(ThemeFullSearchReq req) {
List<ThemeQo> list = themeManager.themeFullSearch(req.keyword, req.getPageNumber(), req.getPageSize(), userHolder.getUserId());
return CommonResp.success(list);
}
}
......@@ -2,6 +2,10 @@ package com.tanpu.community.dao.mapper.community;
import com.tanpu.community.dao.entity.community.ThemeEntity;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* <p>
......
......@@ -36,7 +36,7 @@ public class FileManager {
String originalName = file.getOriginalFilename();
if (data.length==0 || StringUtils.isEmpty(originalName)){
if (data.length == 0 || StringUtils.isEmpty(originalName)){
throw new BizException("上传文件为空");
}
return ossFileService.uploadFile(data, originalName);
......
......@@ -14,6 +14,7 @@ import com.tanpu.community.util.PageUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.stream.Collectors;
......
package com.tanpu.community.manager;public class SearchManager {
}
......@@ -2,6 +2,7 @@ package com.tanpu.community.manager;
import com.tanpu.common.exception.BizException;
import com.tanpu.common.util.JsonUtil;
import com.tanpu.community.api.beans.qo.ESThemeQo;
import com.tanpu.community.api.beans.qo.FormerThemeQo;
import com.tanpu.community.api.beans.qo.ThemeContentQo;
import com.tanpu.community.api.beans.qo.ThemeQo;
......@@ -18,8 +19,10 @@ import com.tanpu.community.dao.entity.community.*;
import com.tanpu.community.dao.entity.user.UserInfoEntity;
import com.tanpu.community.service.*;
import com.tanpu.community.service.BlackListService;
import com.tanpu.community.service.base.ESService;
import com.tanpu.community.util.ConvertUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -62,6 +65,21 @@ public class ThemeManager {
@Autowired
private OSSFileService ossFileService;
@Autowired
private ESService esService;
public List<ThemeQo> themeFullSearch(String keyword, Integer pageNo, Integer pageSize, String userId) {
Integer from = (pageNo - 1) * pageSize;
// 按时间倒叙查询
List<ESThemeQo> themes = esService.queryThemeIdByContentAndTitle(keyword, from, pageSize);
if (themes.isEmpty()) {
return new ArrayList<>();
}
List<ThemeEntity> themeEntities = themeService.queryByThemeIds(themes.stream().map(ESThemeQo::getThemeId).collect(Collectors.toList()));
return convertEntityToQo(themeEntities, userId);
}
@Transactional
public CreateThemeResp publishTheme(CreateThemeReq req, String userId) {
......
package com.tanpu.community.manager;
import com.tanpu.common.exception.BizException;
import com.tanpu.community.api.beans.vo.TopicDO;
import com.tanpu.community.api.beans.vo.TopicDTO;
import com.tanpu.community.api.beans.vo.TopicDataAnalysDTO;
import com.tanpu.community.api.beans.qo.TopicDetailQo;
import com.tanpu.community.api.beans.qo.TopicHotQo;
......@@ -54,7 +54,7 @@ public class TopicManager {
}
//话题详情列表
public List<TopicDO> getAllTopicDetail() {
public List<TopicDTO> getAllTopicDetail() {
return ConvertUtil.topicEntitiesToDTOs(topicService.queryAll());
}
......
......@@ -96,7 +96,6 @@ public class ThemeService {
public List<ThemeEntity> selectAll(String lastId,Integer pageSize) {
LambdaQueryWrapper<ThemeEntity> queryWrapper = new LambdaQueryWrapper<ThemeEntity>();
if (StringUtils.isNotEmpty(lastId)) {
ThemeEntity lastEntity = queryByThemeId(lastId);
if (lastEntity==null) throw new BizException("主题未找到,id:"+lastId);
......@@ -110,7 +109,6 @@ public class ThemeService {
return themeMapper.selectList(queryWrapper);
}
/**
* 根据条件查询最新主题
* @param topidId 话题Id
......
package com.tanpu.community.service.base;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.elasticsearch.client.*;
import javax.annotation.PostConstruct;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Service
public class ESHelper {
@Autowired
private RestHighLevelClient client;
public void insert(String index, String type, String id, String json) {
try {
IndexRequest req = new IndexRequest(index);
if (StringUtils.isNotBlank(id)) {
req.id(type + "_" + id);
}
req.source(json, XContentType.JSON);
IndexResponse resp = client.index(req, RequestOptions.DEFAULT);
validStatus(resp.status(), RestStatus.CREATED);
} catch (IOException e) {
log.error("ES Helper error:{}", ExceptionUtils.getStackTrace(e));
}
}
public void insert(String index, String type, String id, Map<String, Object> data) {
try {
IndexRequest req = new IndexRequest(index);
if (StringUtils.isNotBlank(id)) {
req.id(type + "_" + id);
}
req.source(data);
IndexResponse resp = client.index(req, RequestOptions.DEFAULT);
validStatus(resp.status(), RestStatus.CREATED);
} catch (IOException e) {
log.error("ES Helper error:{}", ExceptionUtils.getStackTrace(e));
}
}
public SearchHit[] selectLike(String index, SearchSourceBuilder builder) {
try {
SearchRequest req = new SearchRequest(index);
req.source(builder);
SearchResponse resp = client.search(req, RequestOptions.DEFAULT);
validStatus(resp.status(), RestStatus.OK);
return resp.getHits().getHits();
} catch (IOException e) {
log.error("ES Helper error:{}", ExceptionUtils.getStackTrace(e));
}
return new SearchHit[]{};
}
private void validStatus(RestStatus status, RestStatus expect) {
if (status != expect) {
log.error("ES Helper fail! status:{}", status.toString());
throw new RuntimeException("ES fail");
}
}
public static void main(String[] args) {
RestClientBuilder builder = RestClient.builder(new HttpHost("42.194.224.208", 9200))
.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
@Override
public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
return httpClientBuilder;
}
});
RestHighLevelClient highClient = new RestHighLevelClient(builder);
ESHelper helper = new ESHelper();
helper.client = highClient;
System.out.println("insert");
Map<String, Object> map = new HashMap<>();
map.put("name", "小行星2");
map.put("context", "这里有一个小行星2");
// helper.insert("test_index", "2", map);
SearchSourceBuilder search = new SearchSourceBuilder();
BoolQueryBuilder boolQb = QueryBuilders.boolQuery();
MatchQueryBuilder matchQb = QueryBuilders.matchQuery("context", "星");
boolQb.must(matchQb);
String[] includes = new String[]{"id"};
String[] excludes = new String[]{};
search.query(boolQb).fetchSource(includes, excludes).from(0).size(50);
SearchHit[] hits = helper.selectLike("test_index", search);
System.out.println(hits.length);
for (SearchHit hit : hits) {
System.out.println(hit.toString());
System.out.println(hit.getFields());
}
}
}
package com.tanpu.community.service.base;public class ESService {
package com.tanpu.community.service.base;
import com.alibaba.fastjson.JSON;
import com.tanpu.community.api.beans.qo.ESThemeQo;
import com.tanpu.community.api.beans.qo.ThemeQo;
import com.tanpu.community.dao.entity.community.ThemeEntity;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class ESService {
@Autowired
private ESHelper helper;
private static final String INDEX_THEME = "theme";
public void insertTheme(ESThemeQo qo) {
helper.insert(INDEX_THEME, String.valueOf(qo.themeType), qo.themeId, JSON.toJSONString(qo));
}
public List<ESThemeQo> queryThemeIdByContentAndTitle(String keyword, int from, int size) {
SearchSourceBuilder search = new SearchSourceBuilder();
BoolQueryBuilder boolQb = QueryBuilders.boolQuery();
MatchQueryBuilder contentQb = QueryBuilders.matchQuery("content", keyword);
MatchQueryBuilder titleQb = QueryBuilders.matchQuery("title", keyword);
boolQb.should(contentQb);
boolQb.should(titleQb);
String[] includes = new String[]{"id", "themeId", "createTime"};
String[] excludes = new String[]{};
search.query(boolQb).fetchSource(includes, excludes).sort("createTime", SortOrder.DESC).from(from).size(size);
SearchHit[] hits = helper.selectLike(INDEX_THEME, search);
return Arrays.stream(hits).map(h -> {
return JSON.parseObject(h.toString(), ESThemeQo.class);
}).collect(Collectors.toList());
}
}
......@@ -3,7 +3,7 @@ package com.tanpu.community.util;
import com.fasterxml.jackson.core.type.TypeReference;
import com.tanpu.common.util.JsonUtil;
import com.tanpu.community.api.beans.vo.ImagesDTO;
import com.tanpu.community.api.beans.vo.TopicDO;
import com.tanpu.community.api.beans.vo.TopicDTO;
import com.tanpu.community.api.beans.qo.*;
import com.tanpu.community.api.beans.req.theme.CreateThemeReq;
import com.tanpu.community.api.beans.req.theme.ThemeContentReq;
......@@ -72,10 +72,10 @@ public class ConvertUtil {
}
public static TopicDO topicEntityToDTO(TopicEntity topicEntity) {
TopicDO topicDO = new TopicDO();
BeanUtils.copyProperties(topicEntity, topicDO);
return topicDO;
public static TopicDTO topicEntityToDTO(TopicEntity topicEntity) {
TopicDTO topicDTO = new TopicDTO();
BeanUtils.copyProperties(topicEntity, topicDTO);
return topicDTO;
}
public static TopicHotQo topicEntityToHotQo(TopicEntity topicEntity) {
......@@ -115,7 +115,7 @@ public class ConvertUtil {
return entities.stream().map(ConvertUtil::commentLv2Entity2Qo).collect(Collectors.toList());
}
public static List<TopicDO> topicEntitiesToDTOs(List<TopicEntity> topicEntities) {
public static List<TopicDTO> topicEntitiesToDTOs(List<TopicEntity> topicEntities) {
return topicEntities.stream().map(ConvertUtil::topicEntityToDTO).collect(Collectors.toList());
}
......
......@@ -75,12 +75,36 @@
<version>1.0.5-SNAPSHOT</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.elasticsearch.client/elasticsearch-rest-high-level-client -->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.10.0</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.10.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
<exclusions>
<exclusion>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
</exclusion>
<exclusion>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment