`

Elastic Search搜索实例

阅读更多
要从现在的公司离职了。记录一下自己针对我们的自己需求所做的搜索。
基于Spring data,ES 2.X版本的

package com.xxxx.cms.elasticsearch.domain;

import java.util.Calendar;

import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldIndex;
import org.springframework.data.elasticsearch.annotations.FieldType;

import com.xxxx.cms.content.common.domain.CmsContent;

@Document(indexName = "cms", type = "contentIndex")
public class ContentIndex {

	/** 主键ID */
	@Id
	private Long id;

	/** 标题 */
	@Field(store = true, type = FieldType.String, analyzer = "ik")
	private String title;
	
	/** 标签 */
	@Field(store = true, type = FieldType.String, analyzer = "ik")
	private String[] tags;

	/** 相关股票 */
	@Field(store = true, type = FieldType.String, analyzer = "ik")
	private String[] relatedStocks;

	/** 摘要 */
	@Field(store = true, type = FieldType.String, analyzer = "ik")
	private String description;

	/** 内容 */
	@Field(store = false, type = FieldType.String, analyzer = "ik")
	private String contentTxt;

	// 状态值
	@Field(store = true, type = FieldType.Integer, index = FieldIndex.not_analyzed)
	private Integer status;

	@Field(store = true, type = FieldType.Integer, index = FieldIndex.not_analyzed)
	private Long channelId; // 栏目ID

	@Field(store = true, type = FieldType.Integer, index = FieldIndex.not_analyzed)
	private Integer typeId;
	
	@Field(store = true, type = FieldType.Long, index = FieldIndex.not_analyzed)
	private Long flagBit;

	@Field(store = true, type = FieldType.Integer, index = FieldIndex.not_analyzed)
	private Long topicId;

	/** 创建时间 */
	@Field(store = true, type = FieldType.Date, index = FieldIndex.not_analyzed)
	private Calendar createdDate;

	/** 发布时间 */
	@Field(store = true, type = FieldType.Date, index = FieldIndex.not_analyzed)
	private Calendar releasedDate;
	
	public ContentIndex() {
	}

	public ContentIndex(Long id, Integer status, String title, String[] tags, String[] relatedStocks,
			String description, String contentTxt, Long channelId, Integer typeId, Long flagBit, Long topicId, Calendar createdDate,
			Calendar releasedDate) {
		this.id = id;
		this.status = status;
		this.title = title;
		this.tags = tags;
		this.relatedStocks = relatedStocks;
		this.channelId = channelId;
		this.typeId = typeId;
		this.flagBit = flagBit;
		this.topicId = topicId;
		this.createdDate = createdDate;
		this.releasedDate = releasedDate;
		this.contentTxt = contentTxt;
		if (StringUtils.isBlank(description) && StringUtils.isNotEmpty(contentTxt)) {

			String contTxt = Jsoup.parse(contentTxt).text();
			this.description = contTxt.length() > 200 ? contTxt.substring(0, 200) : contTxt;
		} else {
			this.description = description;
		}
	}

	public ContentIndex(CmsContent cmsContent) {
		this.id = cmsContent.getId();
		this.status = cmsContent.getStatus().getIndex();
		this.title = cmsContent.getTitle();
		this.tags = cmsContent.getESTags();
		this.relatedStocks = cmsContent.getESRelatedStocks();
		this.channelId = cmsContent.getChannelId();
		this.typeId = cmsContent.getTypeId();
		this.flagBit = cmsContent.getFlagBit();
		this.topicId = cmsContent.getTopicId();
		this.createdDate = cmsContent.getCreatedDate();
		this.releasedDate = cmsContent.getReleasedDate();
		this.contentTxt = cmsContent.getContentTxt();
		if (StringUtils.isBlank(cmsContent.getDescription()) && StringUtils.isNotEmpty(contentTxt)) {

			String contTxt = Jsoup.parse(contentTxt).text();
			this.description = contTxt.length() > 200 ? contTxt.substring(0, 200) : contTxt;
		} else {
			this.description = cmsContent.getDescription();
		}
	}

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public Integer getStatus() {
		return status;
	}

	public void setStatus(Integer status) {
		this.status = status;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String[] getTags() {
		return tags;
	}

	public void setTags(String[] tags) {
		this.tags = tags;
	}

	public String[] getRelatedStocks() {
		return relatedStocks;
	}

	public void setRelatedStocks(String[] relatedStocks) {
		this.relatedStocks = relatedStocks;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public String getContentTxt() {
		return contentTxt;
	}

	public void setContentTxt(String contentTxt) {
		this.contentTxt = contentTxt;
	}

	public Long getChannelId() {
		return channelId;
	}

	public void setChannelId(Long channelId) {
		this.channelId = channelId;
	}

	public Integer getTypeId() {
		return typeId;
	}

	public void setTypeId(Integer typeId) {
		this.typeId = typeId;
	}

	public Long getFlagBit() {
		return flagBit;
	}

	public void setFlagBit(Long flagBit) {
		this.flagBit = flagBit;
	}

	public Long getTopicId() {
		return topicId;
	}

	public void setTopicId(Long topicId) {
		this.topicId = topicId;
	}

	public Calendar getCreatedDate() {
		return createdDate;
	}

	public void setCreatedDate(Calendar createdDate) {
		this.createdDate = createdDate;
	}

	public Calendar getReleasedDate() {
		return releasedDate;
	}

	public void setReleasedDate(Calendar releasedDate) {
		this.releasedDate = releasedDate;
	}

}



package com.xxxx.cms.elasticsearch.service.impl;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.TaskExecutor;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.stereotype.Service;

import com.aicai.appmodel.domain.result.ModelResult;
import com.aicai.appmodel.page.DataPage;
import com.xxxx.cms.channel.common.domain.CmsChannelDefine;
import com.xxxx.cms.channel.common.service.CmsChannelDefineService;
import com.xxxx.cms.channel.common.type.ChannelDefineType;
import com.xxxx.cms.config.props.EsProps;
import com.xxxx.cms.content.common.domain.CmsContent;
import com.xxxx.cms.content.common.domain.CmsContentStock;
import com.xxxx.cms.content.common.domain.CmsContentTag;
import com.xxxx.cms.content.common.domain.CmsContentTxt;
import com.xxxx.cms.content.common.type.ContentStatus;
import com.xxxx.cms.content.common.vo.CmsContentQueryVo;
import com.xxxx.cms.content.manager.CmsContentManager;
import com.xxxx.cms.content.manager.CmsContentStockManager;
import com.xxxx.cms.content.manager.CmsContentTagManager;
import com.xxxx.cms.content.manager.CmsContentTxtManager;
import com.xxxx.cms.elasticsearch.common.domain.ContentSearchResult;
import com.xxxx.cms.elasticsearch.common.service.ContentIndexReadService;
import com.xxxx.cms.elasticsearch.constant.ElasticSearchConstant;
import com.xxxx.cms.elasticsearch.domain.ContentIndex;
import com.xxxx.cms.elasticsearch.domain.HelpCenterContentIndex;
import com.xxxx.cms.elasticsearch.manager.ContentIndexManager;
import com.xxxx.cms.elasticsearch.repositories.ContentIndexRepository;
import com.xxxx.cms.elasticsearch.repositories.HelpCenterRepository;

@Service("contentIndexReadService")
public class ContentIndexReadServiceImpl implements ContentIndexReadService, InitializingBean {
	
	private Logger logger = LoggerFactory.getLogger(getClass());

	@Autowired
	private ContentIndexManager contentIndexManager;
	
	@Autowired
    private ContentIndexRepository contentIndexRepository;


	@Autowired
	private HelpCenterRepository helpCenterRepository;
	
	@Autowired
    private ElasticsearchOperations elasticsearchTemplate;
	
	@Autowired
    private CmsContentStockManager cmsContentStockManager;
    
    @Autowired
    private CmsContentTagManager cmsContentTagManager;
    
    @Autowired
    private CmsContentManager cmsContentManager;
    
    @Autowired
    private CmsContentTxtManager cmsContentTxtManager;

    @Autowired
	private CmsChannelDefineService cmsChannelDefineService;
    
    @Autowired
	private TaskExecutor taskExecutor;

    @Autowired
    private EsProps esProps;
    
    private final int page_size = 1000;
    
    @SuppressWarnings("unchecked")
	@Override
	public void afterPropertiesSet() throws Exception {
    	
		if (esProps.isInit() == false)
			return;
		
    	boolean delContentIdxFlag = elasticsearchTemplate.deleteIndex(ElasticSearchConstant.CMS_INDEX);
		logger.warn("ES删除ContentIndex索引:{}", delContentIdxFlag);
		if (delContentIdxFlag == false) {
			
			logger.error("ES删除ContentIndex索引失败,重建索引退出");
			return;
		}
		
		boolean createContentIdxFlag = elasticsearchTemplate.createIndex(ElasticSearchConstant.CMS_INDEX);
		logger.warn("ES创建cms索引:{}", createContentIdxFlag);
		if (createContentIdxFlag == false) {
			
			logger.error("ES删除ContentIndex索引失败,重建索引退出");
			return;
		}
		
		boolean contentIndexMappingFlag = elasticsearchTemplate.putMapping(ContentIndex.class);
		logger.warn("ES创建cms:contentIndex映射:{}", contentIndexMappingFlag);
		elasticsearchTemplate.getMapping(ElasticSearchConstant.CMS_INDEX, ElasticSearchConstant.CMS_CONTENT_TYPE).entrySet()
				.forEach(obj -> logger.warn("ES创建ContentIndex索引,映射关系为:{}", obj));
		
		boolean helpCenterIndexMappingFlag = elasticsearchTemplate.putMapping(HelpCenterContentIndex.class);
		logger.warn("ES创建cms:helpCenterIndex映射:{}", helpCenterIndexMappingFlag);
		elasticsearchTemplate.getMapping(ElasticSearchConstant.CMS_INDEX, ElasticSearchConstant.CMS_HELP_CENTER_TYPE).entrySet()
				.forEach(obj -> logger.warn("ES创建helpCenterIndex索引,映射关系为:{}", obj));
		
		indexInit();
		helpCenterInit();
	}
    
	private void indexInit() {

		taskExecutor.execute(() -> {

			logger.info("初始化contentIndex开始");
			LocalDateTime beginTime = LocalDateTime.now();
			contentIndexRepository.deleteAll();
			DataPage<CmsContent> dataPage = new DataPage<>();
			dataPage.setPageSize(page_size);
			dataPage.setOrder("ASC");
			dataPage.setOrderBy("released_date");
			Calendar beginReleasedDate = Calendar.getInstance();
			// 只初始化一年内的资讯
			beginReleasedDate.setTimeInMillis(LocalDateTime.now().minusYears(1).toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
			CmsContentQueryVo queryVo = new CmsContentQueryVo();
			queryVo.setStatus(ContentStatus.PUBLISH);
			for (int i = 1;; i++) {

				dataPage.setPageNo(1);
				beginReleasedDate.add(Calendar.SECOND, 1);
				queryVo.setBeginReleasedDate(beginReleasedDate);
				DataPage<CmsContent> contentPage = cmsContentManager.queryByVo(dataPage, queryVo);
				logger.info("create page:{}, pageSize:{}, total:{}", i, contentPage.getPageSize(), contentPage.getTotalCount());
				List<CmsContent> contents = contentPage.getDataList();
				if (CollectionUtils.isEmpty(contents)) {
					break;
				}

				List<Long> contIdList = contents.stream().map(CmsContent::getId).collect(Collectors.toList());
				List<CmsContentTxt> txtList = cmsContentTxtManager.query(contIdList);
				List<CmsContentStock> stockList = cmsContentStockManager.queryByContentIdList(contIdList);
				List<CmsContentTag> tagList = cmsContentTagManager.queryByIdList(contIdList);
				List<ContentIndex> indexs = new ArrayList<>();
				for (CmsContent c : contents) {

					if (CollectionUtils.isNotEmpty(txtList)) {

						txtList.stream().filter(txt -> txt.getContentId().longValue() == c.getId().longValue()).findAny().ifPresent(txt -> {
							c.setContentTxt(txt.getTxt());
							if (StringUtils.isEmpty(c.getDescription())) {

								String contTxt = Jsoup.parse(c.getContentTxt()).text();
								String txtStr = contTxt.length() > 200 ? contTxt.substring(0, 200) : contTxt;
								c.setDescription(txtStr);
							}
						});
					}
					if (CollectionUtils.isNotEmpty(tagList)) {

						List<String> tagStrList = tagList.stream().filter(tag -> tag.getContentId().longValue() == c.getId().longValue())
								.map(CmsContentTag::getTagName).filter(StringUtils::isNotBlank).collect(Collectors.toList());
						if (CollectionUtils.isNotEmpty(tagStrList) && tagStrList.toArray() != null && tagStrList.toArray() instanceof String[]) {

							c.setESTags((String[]) tagStrList.toArray());
						}
					}
					if (CollectionUtils.isNotEmpty(stockList)) {

						List<String> stockStrList = stockList.stream().filter(stock -> stock.getContentId().longValue() == c.getId().longValue())
								.map(CmsContentStock::getStockCode).filter(StringUtils::isNotBlank).collect(Collectors.toList());
						if (CollectionUtils.isNotEmpty(stockStrList) && stockStrList.toArray() != null && stockStrList.toArray() instanceof String[]) {

							c.setESRelatedStocks((String[]) stockStrList.toArray());
						}
					}

					ContentIndex index = new ContentIndex(c);
					indexs.add(index);
				}
				contentIndexRepository.save(indexs);
				beginReleasedDate = contents.get(contents.size() - 1).getReleasedDate();
			}
			LocalDateTime endTime = LocalDateTime.now();
			logger.info("初始化contentIndex结束,共耗时:{} min", String.valueOf(Duration.between(beginTime, endTime).toMinutes()));
		});
	}

	private void helpCenterInit() {
		
		taskExecutor.execute(() -> {
			
			logger.info("初始化 help-center 开始");
			
			LocalDateTime beginTime = LocalDateTime.now();
			helpCenterRepository.deleteAll();
			DataPage<CmsContent> dataPage = new DataPage<>();
			dataPage.setPageSize(page_size);
			dataPage.setOrder("ASC");
			dataPage.setOrderBy("released_date");
			Calendar beginReleasedDate = Calendar.getInstance();
			beginReleasedDate.set(1970, 10, 10, 10, 10, 10);
			CmsContentQueryVo queryVo = new CmsContentQueryVo();
			queryVo.setStatus(ContentStatus.PUBLISH);

			ModelResult<List<CmsChannelDefine>> modelResult = cmsChannelDefineService
					.queryLastChildByChannel(ChannelDefineType.NEW_HELP_CENTER.getIndex().longValue());
			List<CmsChannelDefine> cmsChannelDefines = modelResult.getModel();
			List<Long> ids = new ArrayList<>();
			cmsChannelDefines.stream().forEach(p -> {
				ids.add(p.getId());
			});

			logger.info("helpCenterInit ids:{}", ids.size());

			for (int i = 1; ; i++) {
				
				dataPage.setPageNo(1);
				beginReleasedDate.add(Calendar.SECOND, 1);
				queryVo.setBeginReleasedDate(beginReleasedDate);
				queryVo.setChannelIds(ids);
				DataPage<CmsContent> contentPage = cmsContentManager.queryByVo(dataPage, queryVo);
				logger.info("helpCenterInit create page:{}, pageSize:{}, total:{}", i, contentPage.getPageSize(), contentPage.getTotalCount());
				List<CmsContent> contents = contentPage.getDataList();
				if (CollectionUtils.isEmpty(contents)) {
					break;
				}

				List<Long> contIdList = contents.stream()
						.map(CmsContent::getId)
						.collect(Collectors.toList());
				List<CmsContentTxt> txtList = cmsContentTxtManager.query(contIdList);
				List<CmsContentStock> stockList = cmsContentStockManager.queryByContentIdList(contIdList);
				List<CmsContentTag> tagList = cmsContentTagManager.queryByIdList(contIdList);
				List<HelpCenterContentIndex> indexs = new ArrayList<>();
				for(CmsContent c : contents) {

					if (CollectionUtils.isNotEmpty(txtList)) {

						txtList.stream()
								.filter(txt -> txt.getContentId().longValue() == c.getId().longValue())
								.findAny()
								.ifPresent(txt -> {
									c.setContentTxt(txt.getTxt());
									if (StringUtils.isEmpty(c.getDescription())) {

										String contTxt = Jsoup.parse(c.getContentTxt()).text();
										String txtStr = contTxt.length() > 200 ? contTxt.substring(0, 200) : contTxt;
										c.setDescription(txtStr);
									}
								});
					}
					if (CollectionUtils.isNotEmpty(tagList)) {

						List<String> tagStrList = tagList.stream()
								.filter(tag -> tag.getContentId().longValue() == c.getId().longValue())
								.map(CmsContentTag::getTagName)
								.filter(StringUtils::isNotBlank)
								.collect(Collectors.toList());
						if (CollectionUtils.isNotEmpty(tagStrList) && tagStrList.toArray() != null && tagStrList.toArray() instanceof String[]) {

							c.setESTags((String[]) tagStrList.toArray());
						}
					}
					if (CollectionUtils.isNotEmpty(stockList)) {

						List<String> stockStrList = stockList.stream()
								.filter(stock -> stock.getContentId().longValue() == c.getId().longValue())
								.map(CmsContentStock::getStockCode)
								.filter(StringUtils::isNotBlank)
								.collect(Collectors.toList());
						if (CollectionUtils.isNotEmpty(stockStrList) && stockStrList.toArray() != null && stockStrList.toArray() instanceof String[]) {

							c.setESRelatedStocks((String[])stockStrList.toArray());
						}
					}

					HelpCenterContentIndex index = new HelpCenterContentIndex(c);
					indexs.add(index);

				}
				helpCenterRepository.save(indexs);
				beginReleasedDate = contents.get(contents.size() - 1).getReleasedDate();
			}
			LocalDateTime endTime = LocalDateTime.now();
			logger.info("初始化helpCenterInit结束,共耗时:{} min", String.valueOf(Duration.between(beginTime, endTime).toMinutes()));
		});
	}

	@Override
	public DataPage<ContentSearchResult> searchContentByKeyword(String keyword, List<String> filterField,
			DataPage<ContentSearchResult> dataPage) {

		DataPage<ContentSearchResult> reDataPage = contentIndexManager.searchContentByKeyword(keyword, filterField, dataPage);
//		if (dataPage.getPageNo() < 2) {
//			List<ContentSearchResult> dataList = dataPage.getDataList();
//			if (CollectionUtils.isNotEmpty(dataList)) {
//				dataList.sort((c1, c2) -> c2.getReleasedDate().compareTo(c1.getReleasedDate()));
//			}
//		}
		
		return reDataPage;
	}
	
	@Override
	public List<ContentSearchResult> searchByKeyword(String keyword, int pageSize, int pageNo) {
		
		Objects.requireNonNull(keyword);
		
		return contentIndexManager.searchByKeyword(keyword, pageSize, pageNo);
	}

	@Override
	public DataPage<ContentSearchResult> searchContentByTags(DataPage<ContentSearchResult> dataPage, Long id,
			Long channelId, String[] tags) {

		return contentIndexManager.searchContentByTags(dataPage, id, channelId, tags);
	}

	@Override
	public DataPage<ContentSearchResult> searchContentByKeyword(String keyword, List<String> filterField,
					DataPage<ContentSearchResult> dataPage, String[] esIndexs, String[] esTypes) {
		return contentIndexManager.searchContentByKeyword(keyword, filterField, dataPage, esIndexs, esIndexs);
	}

	@Override
	public List<ContentSearchResult> searchByKeyword(String keyword, int pageSize, int pageNo, String[] esIndexs, String[] esTypes) {
		return contentIndexManager.searchByKeyword(keyword, pageSize, pageNo, esIndexs, esTypes);
	}

	@Override
	public DataPage<ContentSearchResult> searchContentByTags(DataPage<ContentSearchResult> dataPage,
							Long id, Long channelId, String[] tags, String[] esIndexs, String[] esTypes) {
		return contentIndexManager.searchContentByTags(dataPage, id, channelId, tags, esIndexs, esTypes);
	}

}




package com.xxxx.cms.elasticsearch.manager.impl;

import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.matchPhraseQuery;
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
import static org.elasticsearch.index.query.QueryBuilders.multiMatchQuery;
import static org.elasticsearch.index.query.QueryBuilders.rangeQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchResponse;
//import org.elasticsearch.common.base.Strings;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
//import org.elasticsearch.index.query.FilterBuilders;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.MultiMatchQueryBuilder.Type;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.highlight.HighlightBuilder;
import org.elasticsearch.search.highlight.HighlightField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Repository;

import com.xxxx.appmodel.domain.result.ModelResult;
import com.xxxx.appmodel.page.DataPage;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.xxxx.cms.channel.common.domain.CmsChannelDefine;
import com.xxxx.cms.channel.common.service.CmsChannelDefineService;
import com.xxxx.cms.channel.common.type.ChannelDefineType;
import com.xxxx.cms.channel.manager.CmsChannelDefineManager;
import com.xxxx.cms.config.constant.BaseConstant;
import com.xxxx.cms.content.common.domain.CmsContent;
import com.xxxx.cms.content.common.domain.CmsContentTag;
import com.xxxx.cms.content.common.type.ContentStatus;
import com.xxxx.cms.content.common.utils.ContentFlagBitOperUtils;
import com.xxxx.cms.content.manager.CmsContentTagManager;
import com.xxxx.cms.elasticsearch.common.domain.ContentSearchResult;
import com.xxxx.cms.elasticsearch.constant.CacheConstant;
import com.xxxx.cms.elasticsearch.constant.ElasticSearchConstant;
import com.xxxx.cms.elasticsearch.domain.ContentIndex;
import com.xxxx.cms.elasticsearch.domain.HelpCenterContentIndex;
import com.xxxx.cms.elasticsearch.manager.ContentIndexManager;
import com.xxxx.cms.elasticsearch.repositories.ContentIndexRepository;
import com.xxxx.cms.elasticsearch.repositories.HelpCenterRepository;
import com.xxxx.cms.tag.common.domain.CmsTagInfo;
import com.xxxx.cms.tag.common.service.CmsTagInfoService;
import com.xxxx.cms.tag.common.type.TagInfoType;

import redis.clients.jedis.JedisCluster;

@Repository
public class ContentIndexManagerImpl implements ContentIndexManager {

	private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private ElasticsearchOperations elasticsearchTemplate;

    @Autowired
    private ContentIndexRepository contentIndexRepository;

	@Autowired
    private HelpCenterRepository helpCenterRepository;

    @Resource(name="cmsChannelDefineManager")
    private CmsChannelDefineManager cmsChannelDefineManager;

    @Resource(name="jedisClusterClient")
    private JedisCluster jedisClusterClient;

    @Autowired
	@Qualifier("cmsChannelDefineService")
    private CmsChannelDefineService cmsChannelDefineService;
    
    @Autowired
    @Qualifier("cmsTagInfoService")
    private CmsTagInfoService cmsTagInfoService;
    
    @Autowired
    private CmsContentTagManager cmsContentTagManager;
    
	protected static final String REDIS_CACHE_PREFIX = CacheConstant.ES_PREFIX + "ContentIndexManager" + BaseConstant.REDIS_CACHE_SEPERATOR;
	
	public static final String ID_FIELD = "id";
	public static final String TITLE_FIELD = "title";
	public static final String DESCRIPTION_FIELD = "description";
	public static final String CONTENT_TXT_FIELD = "contentTxt";
	public static final String RELEASED_DATE_FIELD = "releasedDate";
	public static final String CHANNEL_ID_FIELD = "channelId";
	public static final String STATUS_FIELD = "status";
	public static final String FLAG_BIT_FIELD = "flagBit";
	public static final String TAGS_FIELD = "tags";
	public static final String RELATED_STOCKS_FIELD = "relatedStocks";
	
	public static final String ANALYZER_NAME = "ik";

    @Override
    public void createCmsContentIndex(CmsContent cmsContent) {

        ContentIndex snsMsgIndex = new ContentIndex(cmsContent);
        contentIndexRepository.save(snsMsgIndex);
    }

    @Override
	public DataPage<ContentSearchResult> searchContentByKeyword(String keyword, List<String> filterField, DataPage<ContentSearchResult> dataPage) {

		return searchContentByKeyword(keyword, filterField, dataPage, null, null);
	}

    @Override
	public List<ContentSearchResult> searchByKeyword(String keyword, int pageSize, int pageNo) {

    	List<ContentSearchResult> searchList = searchByKeyword(keyword, pageSize, pageNo, null, null);
    	//投教标签
    	setupHelpTag(searchList);
    	
    	return searchList;
	}

    private void setupHelpTag(List<ContentSearchResult> searchList) {
    	
		if (!CollectionUtils.isEmpty(searchList)) {
			// 查询各篇文章关联的投教标签
			List<CmsTagInfo> helpTagList = cmsTagInfoService.queryChildrenByParentId(TagInfoType.HELP_TAG.getIndex());
			List<Integer> idList = helpTagList.stream().map(CmsTagInfo::getId).collect(Collectors.toList());
			List<Long> contentIdList = searchList.stream().map(ContentSearchResult::getId).collect(Collectors.toList());
			
			Map<String, Object> param = new HashMap<>();
			param.put("contentIdList", contentIdList);
			param.put("tagIdList", idList);
			List<CmsContentTag> contentTagList = cmsContentTagManager.query(param);
			Map<Long,List<CmsContentTag>> tagListMap = new HashMap<>();
			if (!CollectionUtils.isEmpty(contentTagList)) {

				for (CmsContentTag tag : contentTagList) {
					
					List<CmsContentTag> tagList = tagListMap.get(tag.getContentId());
					if (tagList == null) {
						tagList = new ArrayList<CmsContentTag>();
					}
					tagList.add(tag);
					tagListMap.put(tag.getContentId(), tagList);
				}
			}
			for (ContentSearchResult content : searchList) {

				if (tagListMap.get(content.getId()) == null) {
					content.setTagList(new ArrayList<>());
				} else {
					content.setTagList(tagListMap.get(content.getId()));
				}
			}
		}
	}
    
	@Override
    public DataPage<ContentSearchResult> searchContentByTags(DataPage<ContentSearchResult> dataPage, Long id, Long channelId, String[] tags) {

		return searchContentByTags(dataPage, id, channelId, tags , null , null);
    }

	@Override
	public DataPage<ContentSearchResult> searchContentByKeyword(String keyword, List<String> filterField, DataPage<ContentSearchResult> dataPage, 
			String [] esIndexs,  String []  esTypes) {
		
		logger.info("执行搜索:{}", keyword);

		NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
		nativeSearchQueryBuilder.withIndices(esIndexs == null || esIndexs.length == 0 ? new String[] { ElasticSearchConstant.CMS_INDEX } : esIndexs)
				.withTypes(esTypes == null || esTypes.length == 0 ? new String[] { ElasticSearchConstant.CMS_CONTENT_TYPE } : esTypes);
		LocalDateTime todayBeginTime = LocalDate.now().atTime(0, 0, 0),
				todayEndTime = LocalDate.now().atTime(23, 59, 59),
				now = LocalDateTime.now();
		final BoolQueryBuilder boolQuery = boolQuery();
		if (CollectionUtils.isEmpty(filterField)) {

			boolQuery.should(termQuery(TITLE_FIELD, keyword));
			boolQuery.should(matchPhraseQuery(TITLE_FIELD, keyword).analyzer(ANALYZER_NAME).slop(1));
			boolQuery.should(matchQuery(TITLE_FIELD, keyword).analyzer(ANALYZER_NAME));
			// 控制内容的相关度
			BoolQueryBuilder txtQuery = boolQuery();
			txtQuery.should(matchPhraseQuery(CONTENT_TXT_FIELD, keyword).analyzer(ANALYZER_NAME).slop(5));
			txtQuery.should(matchQuery(CONTENT_TXT_FIELD, keyword).analyzer(ANALYZER_NAME).minimumShouldMatch("30%"));
			boolQuery.should(txtQuery);
			boolQuery.must(multiMatchQuery(keyword, DESCRIPTION_FIELD + "^2", TAGS_FIELD, RELATED_STOCKS_FIELD)
							.type(Type.CROSS_FIELDS)
							.analyzer(ANALYZER_NAME));
		} else {

			if (filterField.contains(TITLE_FIELD)) {
				boolQuery.should(termQuery(TITLE_FIELD, keyword));
				boolQuery.should(matchPhraseQuery(TITLE_FIELD, keyword).analyzer(ANALYZER_NAME).slop(3));
				// boolQuery.should(matchQuery(TITLE_FIELD, keyword).operator(Operator.AND).analyzer(WORD_SEPERATOR_NAME));
				boolQuery.should(matchQuery(TITLE_FIELD, keyword).analyzer(ANALYZER_NAME).boost(1.2f));
			}
			if (filterField.contains(DESCRIPTION_FIELD)) {
				BoolQueryBuilder descBoolQuery = boolQuery();
				descBoolQuery.should(matchPhraseQuery(DESCRIPTION_FIELD, keyword).analyzer(ANALYZER_NAME).slop(3));
				descBoolQuery.should(matchQuery(DESCRIPTION_FIELD, keyword).analyzer(ANALYZER_NAME));
				boolQuery.should(descBoolQuery);
			}
			if (filterField.contains(CONTENT_TXT_FIELD)) {
				boolQuery.should(matchPhraseQuery(CONTENT_TXT_FIELD, keyword).analyzer(ANALYZER_NAME).slop(5));
				boolQuery.should(matchQuery(CONTENT_TXT_FIELD, keyword).analyzer(ANALYZER_NAME).minimumShouldMatch("20%"));
			}
			if (filterField.contains(TAGS_FIELD)) {
				boolQuery.should(matchPhraseQuery(TAGS_FIELD, keyword).analyzer(ANALYZER_NAME));
				boolQuery.should(matchQuery(TAGS_FIELD, keyword).analyzer(ANALYZER_NAME));
			}
			if (filterField.contains(RELATED_STOCKS_FIELD)) {
				boolQuery.should(matchPhraseQuery(RELATED_STOCKS_FIELD, keyword).analyzer(ANALYZER_NAME));
			}
		}

		this.general4PeriodQuery(boolQuery, todayBeginTime);
		this.general4ChannelQuery(boolQuery);
		nativeSearchQueryBuilder.withQuery(boolQuery);

		String cacheKey = REDIS_CACHE_PREFIX + "searchContentByKeyword" + BaseConstant.REDIS_CACHE_SEPERATOR + "investmentChannelList",
				cacheStr = jedisClusterClient.get(cacheKey);
		// 缓存60分钟
		int cacheSecond = 60 * 60;
		ArrayList<Long> searchChanList = new ArrayList<>();
		List<Long> investmentChanList = null;
		if (StringUtils.isNotEmpty(cacheStr)) {

			investmentChanList = JSONArray.parseArray(cacheStr, Long.class);
			searchChanList.addAll(investmentChanList);
		} else {

			List<CmsChannelDefine> investmentChannelList = cmsChannelDefineManager
					.selectAllChildInvestmentChannel(ChannelDefineType.INVESTMENT_CHANNEL.getIndex().longValue());
			if (CollectionUtils.isNotEmpty(investmentChannelList)) {
				investmentChanList = investmentChannelList.stream()
						.filter(CmsChannelDefine::getIsDisplay)
						.map(CmsChannelDefine::getId)
						.collect(Collectors.toList());
				cacheStr = JSON.toJSONString(investmentChanList);
				jedisClusterClient.setex(cacheKey, cacheSecond, cacheStr);
				searchChanList.addAll(investmentChanList);
			}
		}
		searchChanList.addAll(
				Arrays.asList(
						ChannelDefineType.COMB_CHANNEL.getIndex().longValue(),
						ChannelDefineType.CHINESE_STOCK_CHANNEL.getIndex().longValue(),
						ChannelDefineType.SINAFIN_CHANNEL.getIndex().longValue(),
						ChannelDefineType.ZHITONGFIN_YW_CHANNEL.getIndex().longValue()
				)
		);

		this.general4FilterOper(nativeSearchQueryBuilder, searchChanList, todayBeginTime, todayEndTime);
		this.general4HighLightOper(nativeSearchQueryBuilder, keyword);
		this.general4PageOper(nativeSearchQueryBuilder, dataPage.getPageNo() - 1, dataPage.getPageSize());
//		nativeSearchQueryBuilder.withSearchType(SearchType.DFS_QUERY_THEN_FETCH);// 默认是query then fetch,使用默认查询方式

		SearchQuery searchQuery = nativeSearchQueryBuilder.build();
//		Long totalCount = elasticsearchTemplate.count(searchQuery, ContentIndex.class);
		List<ContentSearchResult> contentIndexList = this.generalQueryExtractOper(searchQuery);
//		List<ContentSearchResult> contentIndexList = elasticsearchTemplate.queryForList(searchQuery, ContentSearchResult.class);

		dataPage.setDataList(contentIndexList);
//		dataPage.setTotalCount(totalCount);
		dataPage.setTotalCount(100);
		LocalDateTime excuteEndTime = LocalDateTime.now();
		logger.info("搜索执行时间:{}毫秒", Duration.between(now, excuteEndTime).toMillis());
		return dataPage;
	}

	private BoolQueryBuilder general4PeriodQuery(final BoolQueryBuilder boolQuery, LocalDateTime todayBeginTime) {
		
		LocalDateTime now = LocalDateTime.now();
		LocalDateTime threeDaysAgo = todayBeginTime.minusDays(3L),
				oneWeekAgo = todayBeginTime.minusWeeks(1L),
				threeMonthsAgo = todayBeginTime.minusMonths(3L),
				halfOfYearAgo = todayBeginTime.minusMonths(6L);
		// 3天内的资讯相关度更高
		boolQuery.should(rangeQuery(RELEASED_DATE_FIELD)
				.gt(threeDaysAgo.toInstant(ZoneOffset.ofHours(8)).toEpochMilli())
				.lte(now.toInstant(ZoneOffset.ofHours(8)).toEpochMilli())
				.boost(15f));
		// 一周内的资讯相关度更高
		boolQuery.should(rangeQuery(RELEASED_DATE_FIELD)
				.gt(oneWeekAgo.toInstant(ZoneOffset.ofHours(8)).toEpochMilli())
				.lte(threeDaysAgo.toInstant(ZoneOffset.ofHours(8)).toEpochMilli())
				.boost(10f));
		// 3个月内的资讯相关度更高
		boolQuery.should(rangeQuery(RELEASED_DATE_FIELD)
				.gt(threeMonthsAgo.toInstant(ZoneOffset.ofHours(8)).toEpochMilli())
				.lte(oneWeekAgo.toInstant(ZoneOffset.ofHours(8)).toEpochMilli()));
		// 半年以前的相关度降低
		boolQuery.should(rangeQuery(RELEASED_DATE_FIELD)
				.lte(halfOfYearAgo.toInstant(ZoneOffset.ofHours(8)).toEpochMilli())
				.boost(0.6f));
		
		return boolQuery;
	}
	
	private BoolQueryBuilder general4ChannelQuery(final BoolQueryBuilder boolQuery) {

		// 提高个股资讯的相关度
//		boolQuery.should(termQuery(CHANNEL_ID_FIELD, ChannelDefineType.CHINESE_STOCK_CHANNEL.getIndex().longValue()));
		// 提高自产相关度
//		boolQuery.should(termQuery(CHANNEL_ID_FIELD, ChannelDefineType.COMB_CHANNEL.getIndex().longValue()));
		// 由于暂时不知道ES是否支持位操作,就从业务角度上来说给一个具体值就算了
		boolQuery.should(termQuery(FLAG_BIT_FIELD, ContentFlagBitOperUtils.HELP_CENTER_VAL).boost(1.6f));
		
		return boolQuery;
	}
	
	private NativeSearchQueryBuilder general4QueryOper(final NativeSearchQueryBuilder natvSechQBuilder, String keyword, LocalDateTime todayBeginTime) {
		
		final BoolQueryBuilder boolQuery = boolQuery();
		String minimumShouldMatchStrategy = "3<75%";
		
//		boolQuery.should(matchQuery(TITLE_FIELD, keyword).analyzer(ANALYZER_NAME));
//		boolQuery.should(matchQuery(TITLE_FIELD, keyword).analyzer(ANALYZER_NAME).operator(Operator.AND));
//		boolQuery.should(multiMatchQuery(keyword, DESCRIPTION_FIELD, CONTENT_TXT_FIELD).tieBreaker(0.4f).minimumShouldMatch(minimumShouldMatchStrategy));
//		DisMaxQueryBuilder titleAndDescrptionMaxQuery = disMaxQuery().add(matchQuery(TITLE_FIELD, keyword).analyzer(ANALYZER_NAME))
//				.add(matchQuery(DESCRIPTION_FIELD, keyword).analyzer(ANALYZER_NAME))
//				.tieBreaker(0.4f)
//				.queryName("disMaxQueryTest");
//		boolQuery.should(titleAndDescrptionMaxQuery);
		
//		boolQuery.should(termQuery(TITLE_FIELD, keyword).boost(5f));
		boolQuery.should(matchQuery(TITLE_FIELD, keyword).analyzer(ANALYZER_NAME));
		boolQuery.should(matchPhraseQuery(TITLE_FIELD, keyword).analyzer(ANALYZER_NAME).type(MatchQueryBuilder.Type.PHRASE_PREFIX).boost(5f));
		BoolQueryBuilder titleBoolQuery = boolQuery();
		titleBoolQuery.should(termQuery(TITLE_FIELD, keyword));
		titleBoolQuery.should(matchPhraseQuery(TITLE_FIELD, keyword).analyzer(ANALYZER_NAME).slop(1));
//		boolQuery.should(matchPhrasePrefixQuery(TITLE_FIELD, keyword).analyzer(ANALYZER_NAME).slop(10).maxExpansions(20));
		titleBoolQuery.should(matchQuery(TITLE_FIELD, keyword).analyzer(ANALYZER_NAME));
		boolQuery.should(titleBoolQuery);
		
		BoolQueryBuilder descBoolQuery = boolQuery();
		descBoolQuery.should(matchPhraseQuery(DESCRIPTION_FIELD, keyword).analyzer(ANALYZER_NAME));
		descBoolQuery.should(matchQuery(DESCRIPTION_FIELD, keyword).analyzer(ANALYZER_NAME));
		boolQuery.should(descBoolQuery);
		
//		multiMatchQuery(DESCRIPTION_FIELD, keyword).type(MultiMatchQueryBuilder.Type.MOST_FIELDS);
		boolQuery.should(matchPhraseQuery(CONTENT_TXT_FIELD, keyword).analyzer(ANALYZER_NAME));
		boolQuery.should(matchQuery(CONTENT_TXT_FIELD, keyword).analyzer(ANALYZER_NAME));
		BoolQueryBuilder contTxtBoolQuery = boolQuery();
		contTxtBoolQuery.should(matchPhraseQuery(CONTENT_TXT_FIELD, keyword).analyzer(ANALYZER_NAME).slop(2).minimumShouldMatch(minimumShouldMatchStrategy));
//		contTxtBoolQuery.should(matchPhraseQuery(CONTENT_TXT_FIELD, keyword).analyzer(ANALYZER_NAME).slop(2));
		contTxtBoolQuery.should(matchQuery(CONTENT_TXT_FIELD, keyword).analyzer(ANALYZER_NAME));
		boolQuery.should(contTxtBoolQuery);
		
		this.general4ChannelQuery(boolQuery);
		this.general4PeriodQuery(boolQuery, todayBeginTime);
		natvSechQBuilder.withQuery(boolQuery);
		
		return natvSechQBuilder;
	}
	
	private NativeSearchQueryBuilder general4FilterOper(final NativeSearchQueryBuilder natvSechQBuilder, ArrayList<Long> searchChanlList,
			LocalDateTime todayBeginTime, LocalDateTime todayEndTime) {
		
		BoolQueryBuilder filterQuery = boolQuery();
		// 只搜索12个月内的资讯信息
		LocalDateTime searchStartTime = todayBeginTime.minusMonths(12L);
		filterQuery.must(rangeQuery(RELEASED_DATE_FIELD)
				.gte(searchStartTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli())
				.lte(todayEndTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli()));
		if (CollectionUtils.isNotEmpty(searchChanlList)) {
			
			filterQuery.must(termsQuery(CHANNEL_ID_FIELD, searchChanlList.toArray()));
		}
		filterQuery.must(termQuery(STATUS_FIELD, ContentStatus.PUBLISH.getIndex().intValue()));
		natvSechQBuilder.withFilter(filterQuery);
		
		return natvSechQBuilder;
	}
	
	private NativeSearchQueryBuilder general4HighLightOper(final NativeSearchQueryBuilder natvSechQBuilder, String keyword) {
		
		String preTagStr = "<font color=#ff8200>",
				postTagStr = "</font>";
		
		HighlightBuilder.Field title = new HighlightBuilder.Field(TITLE_FIELD);
		title.preTags(preTagStr);
		title.postTags(postTagStr);
		HighlightBuilder.Field des = new HighlightBuilder.Field(DESCRIPTION_FIELD);
		des.preTags(preTagStr);
		des.postTags(postTagStr);
		natvSechQBuilder.withHighlightFields(title, des);
		
		return natvSechQBuilder;
	}
	
	private NativeSearchQueryBuilder general4PageOper(final NativeSearchQueryBuilder natvSechQBuilder, int pageNo, int pageSize) {
		
		// spring data的接口是从第0页开始,不要问我为什么
		natvSechQBuilder.withPageable(new PageRequest(pageNo, pageSize));
		natvSechQBuilder.withMinScore(0.5f);// 相关度不低于50%
		
		return natvSechQBuilder;
	}
	
	private List<ContentSearchResult> generalQueryExtractOper(final SearchQuery searchQuery) {

		return elasticsearchTemplate.query(searchQuery, (SearchResponse response) -> {

			List<ContentSearchResult> results = new ArrayList<ContentSearchResult>();
			for (SearchHit hit : response.getHits()) {

				ContentSearchResult result = JSON.parseObject(hit.getSourceAsString(), ContentSearchResult.class);
				Map<String, HighlightField> hlFields = hit.getHighlightFields();
				HighlightField titleHLField = hlFields.get(TITLE_FIELD);
				if (titleHLField != null) {

					// 取得定义的高亮标签
					Text[] titleTexts = titleHLField.getFragments();
					// 为title串值增加自定义的高亮标签
					String titleStr = "";
					for (Text text : titleTexts) {
						titleStr += text;
					}
					// 将追加了高亮标签的串值重新填充到对应的对象
					result.setTitle(titleStr);
				}
				// 从设定的高亮域中取得指定域
				HighlightField descHLField = hlFields.get(DESCRIPTION_FIELD);
				if (descHLField != null) {
					// 取得定义的高亮标签
					Text[] descTexts = descHLField.fragments();
					// 为title串值增加自定义的高亮标签
					String desc = "";
					for (Text text : descTexts) {
						desc += text;
					}
					// 将追加了高亮标签的串值重新填充到对应的对象
					result.setDescription(desc);
				}
				logger.info("contentId:{}", result.getId());
				results.add(result);
			}
			return results;
		});
	}
	
	@Override
	public List<ContentSearchResult> searchByKeyword(String keyword, int pageSize, int pageNo, String[] esIndexs, String[] esTypes) {

		logger.info("searchByKeywords => 搜索关键词:【{}】 => 开始", keyword);
		LocalDateTime todayBeginTime = LocalDate.now().atStartOfDay(),
				todayEndTime = todayBeginTime.plusDays(1L).minusSeconds(1L),
				now = LocalDateTime.now();

		NativeSearchQueryBuilder natvSechQBuilder = new NativeSearchQueryBuilder();
		esIndexs = esIndexs == null ? new String[] { ElasticSearchConstant.CMS_INDEX } : esIndexs;
		esTypes = esTypes == null ? new String[] { ElasticSearchConstant.CMS_CONTENT_TYPE } : esTypes;
		natvSechQBuilder.withIndices(esIndexs).withTypes(esTypes);
		
//		this.general4QueryParam(natvSechQBuilder, keyword, todayBeginTime);// 实用但不直观的写法
		natvSechQBuilder = this.general4QueryOper(natvSechQBuilder, keyword, todayBeginTime);
		
		String cacheKey = REDIS_CACHE_PREFIX + "searchContentByKeyword" + BaseConstant.REDIS_CACHE_SEPERATOR + "investmentChannelList",
				cacheStr = jedisClusterClient.get(cacheKey);
		// 缓存60分钟
		int cacheSecond = 60 * 60;
		ArrayList<Long> searchChanlList = new ArrayList<>();
		esTypes = new String[] { ElasticSearchConstant.CMS_CONTENT_TYPE, ElasticSearchConstant.CMS_HELP_CENTER_TYPE };// 过滤时增加帮助中心
		for (int i = 0; i < esTypes.length; i++) {
			
			String types = esTypes[i];
			if (types.equals(ElasticSearchConstant.CMS_CONTENT_TYPE)) {
				
				List<Long> investCollegeChanlIdList = null;
				if (StringUtils.isNotEmpty(cacheStr)) {

					investCollegeChanlIdList = JSONArray.parseArray(cacheStr, Long.class);
					searchChanlList.addAll(investCollegeChanlIdList);
				} else {

					List<CmsChannelDefine> investCollegeChnlList = cmsChannelDefineManager
							.selectAllChildInvestmentChannel(ChannelDefineType.INVESTMENT_CHANNEL.getIndex().longValue());
					if (CollectionUtils.isNotEmpty(investCollegeChnlList)) {
						
						investCollegeChanlIdList = investCollegeChnlList.stream()
								.filter(CmsChannelDefine::getIsDisplay)
								.map(CmsChannelDefine::getId)
								.collect(Collectors.toList());
						cacheStr = JSON.toJSONString(investCollegeChanlIdList);
						jedisClusterClient.setex(cacheKey, cacheSecond, cacheStr);
						searchChanlList.addAll(investCollegeChanlIdList);
					}
				}
				searchChanlList.addAll(
						Arrays.asList(ChannelDefineType.COMB_CHANNEL.getIndex().longValue(), ChannelDefineType.CHINESE_STOCK_CHANNEL.getIndex().longValue(),
								ChannelDefineType.SINAFIN_CHANNEL.getIndex().longValue(), ChannelDefineType.ZHITONGFIN_YW_CHANNEL.getIndex().longValue()));
			} else if (types.equals(ElasticSearchConstant.CMS_HELP_CENTER_TYPE)) {

				String cmsCacheKey = cacheKey + "cmshelpcenter";
				cacheStr = jedisClusterClient.get(cmsCacheKey);

				List<Long> cmsHelpCenterLastChild = null;
				if (StringUtils.isNotEmpty(cacheStr)) {
					
					cmsHelpCenterLastChild = JSONArray.parseArray(cacheStr, Long.class);
					searchChanlList.addAll(cmsHelpCenterLastChild);
				} else {
					
					ModelResult<List<CmsChannelDefine>> listModelResult = cmsChannelDefineService
							.queryAllChildByParentId(ChannelDefineType.NEW_HELP_CENTER.getIndex().longValue());
					List<CmsChannelDefine> cmsChannelDefines = listModelResult.getModel();
					if (CollectionUtils.isNotEmpty(cmsChannelDefines)) {
						cmsHelpCenterLastChild = cmsChannelDefines.stream()
								.filter(CmsChannelDefine::getIsDisplay)
								.map(CmsChannelDefine::getId)
								.collect(Collectors.toList());
						cacheStr = JSON.toJSONString(cmsHelpCenterLastChild);
						jedisClusterClient.setex(cacheKey, cacheSecond, cacheStr);
						searchChanlList.addAll(cmsHelpCenterLastChild);
					}
				}
			}
		}

//		this.general4FilterParam(natvSechQBuilder, searchChanlList, todayBeginTime, todayEndTime);// 实用但不直观的写法
		natvSechQBuilder = this.general4FilterOper(natvSechQBuilder, searchChanlList, todayBeginTime, todayEndTime);

//		this.generalHighLightOper(natvSechQBuilder, keyword, pageNo, pageSize);// 实用但不直观的写法
		natvSechQBuilder = this.general4HighLightOper(natvSechQBuilder, keyword);
		
//		this.general4PageOper(natvSechQBuilder, pageNo, pageSize);// 实用但不直观的写法
		natvSechQBuilder = this.general4PageOper(natvSechQBuilder, pageNo, pageSize);
		
//		nativeSearchQueryBuilder.withSearchType(SearchType.DFS_QUERY_THEN_FETCH);// 默认是query then fetch,使用默认查询方式
		SearchQuery searchQuery = natvSechQBuilder.build();
		
		Sort sort = new Sort(Direction.DESC, RELEASED_DATE_FIELD);
		searchQuery.addSort(sort);

		List<ContentSearchResult> searchResults = this.generalQueryExtractOper(searchQuery);

		LocalDateTime excuteEndTime = LocalDateTime.now();
		logger.info("搜索执行时间:{}毫秒", Duration.between(now, excuteEndTime).toMillis());
		return searchResults;
	}

	@Override
	public DataPage<ContentSearchResult> searchContentByTags(DataPage<ContentSearchResult> dataPage, Long id, Long channelId, String[] tags, 
			String[] esIndexs, String[] esTypes) {
		
		NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
		nativeSearchQueryBuilder.withIndices(esIndexs == null || esIndexs.length == 0 ? new String[] { ElasticSearchConstant.CMS_INDEX } : esIndexs)
				.withTypes(esTypes == null || esTypes.length == 0 ? new String[] { ElasticSearchConstant.CMS_CONTENT_TYPE } : esTypes);
		BoolQueryBuilder boolQuery = boolQuery();
		for (String tag : tags) {
			boolQuery.should(matchPhraseQuery(TAGS_FIELD, tag));
		}
		//MatchQueryBuilder  matchBuilder=new MatchQueryBuilder("channelId", channelId);
		MatchQueryBuilder matchBuilderId = new MatchQueryBuilder(ID_FIELD, id);
		//boolQuery.must(matchBuilder);
		boolQuery.mustNot(matchBuilderId);
        /*       TermsQueryBuilder termsQuery = new TermsQueryBuilder("channelId",channelId+"");
               boolQuery.must(termsQuery);*/
		// TODO
		nativeSearchQueryBuilder.withFilter(QueryBuilders.termQuery(CHANNEL_ID_FIELD, channelId));
//        nativeSearchQueryBuilder
//                .withFilter(FilterBuilders.andFilter(FilterBuilders.termFilter("channelId", channelId)));
		this.general4PageOper(nativeSearchQueryBuilder, dataPage.getPageNo() - 1, dataPage.getPageSize());
		nativeSearchQueryBuilder.withQuery(boolQuery);
		SearchQuery searchQuery = nativeSearchQueryBuilder.build();
		Sort sort = new Sort(Direction.DESC, RELEASED_DATE_FIELD);
		searchQuery.addSort(sort);
		Long totalCount = elasticsearchTemplate.count(searchQuery, ContentIndex.class);
		List<ContentSearchResult> contentIndexList = elasticsearchTemplate.queryForList(searchQuery, ContentSearchResult.class);
		dataPage.setDataList(contentIndexList);
		dataPage.setTotalCount(totalCount);
		return dataPage;
	}

	@Override
	public void createHelpCenterContentIndex(CmsContent cmsContent) {
		
		HelpCenterContentIndex helpCenterContentIndex = new HelpCenterContentIndex(cmsContent);
		helpCenterRepository.save(helpCenterContentIndex);
	}

}

分享到:
评论

相关推荐

    Elasticsearch7.0实例精解 源代码.zip

    Elasticsearch 7.0 实例精解源代码是一份深度解析Elasticsearch 7.0版本核心功能和实际应用的资源集合。这份压缩包包含了详细的示例代码,旨在帮助开发者更好地理解和掌握Elasticsearch的强大功能。以下是基于...

    JAVA实现ElasticSearch的简单实例

    Java实现Elasticsearch的简单实例主要涉及以下几个关键知识点: 1. **Elasticsearch基础**:Elasticsearch(ES)是一个开源的、分布式全文搜索引擎,它提供了实时数据分析的能力,广泛用于日志分析、监控、搜索应用...

    基于.netcore搜索封装ElasticSearch.zip

    这个名为"基于.netcore搜索封装ElasticSearch.zip"的压缩包,显然包含了一个针对.NET Core平台的Elasticsearch客户端库,方便开发者在.NET Core应用中集成和操作Elasticsearch。 Elasticsearch是一个开源的分布式...

    elasticsearch集成spring开发实例

    **Elasticsearch 集成 Spring 开发实例详解** 在现代大数据分析和实时搜索领域,Elasticsearch(简称 ES)已经成为了广泛使用的工具。它是一个分布式、RESTful 风格的搜索和数据分析引擎,能够处理大量数据并提供...

    python处理elasticsearch实例

    要使用Python处理Elasticsearch实例,可以使用elasticsearch库来与Elasticsearch进行交互。以下是一个简单的描述: 导入相关库:首先需要导入必要的Python库,如elasticsearch(用于与Elasticsearch建立连接和执行...

    ElasticSearch代码实例C#

    本篇文章将详细探讨如何在C#中使用Elasticsearch,并通过代码实例来展示其基本操作。 首先,安装Nest或Elasticsearch.Net库。在Visual Studio中,可以利用NuGet包管理器或命令行工具dotnet add package命令安装Nest...

    Elastic Search搭建使用教程.pdf(内含ElasticSearch教程权威指南)

    Node是Elasticsearch中的单个实例,可以单独运行,也可以在集群中作为数据节点或协调节点。Cluster是由多个Node组成的,每个Cluster都有一个唯一的集群名称,默认为"elasticsearch"。Index是Elasticsearch中管理数据...

    springboot+ElasticSearch搜索引擎

    Spring Boot简化了Java应用程序的开发过程,而Elasticsearch则是一个强大的分布式搜索引擎。这个压缩包文件"springboot-elasticSearch"似乎提供了一个预配置的项目,允许开发者快速集成这两者,实现高效的全文搜索...

    elasticsearch常用版本

    Elasticsearch是一款强大的开源搜索引擎,广泛应用于大数据分析和实时搜索领域。它基于Lucene库,提供了分布式、全文检索、近实时...在实际使用中,确保正确配置和管理Elasticsearch实例,是实现高效搜索和分析的关键。

    ElasticSearch5.1.1实例学习

    这是最近在学习ElasticSearch的过程中,自己练习和积累的一些Java代码,使用ElasticSearch5.1.1版本 + IntelliJ IDEA开发,在代码中调用了ElasticSearch5.1.1中的最新的API。

    基于Spring框架开源ElasticSearch搜索

    本书以实例讲述如何在Spring框架之上搭建ElasticSearch开发,以及如何利用JPA建立、更新和删除索引,如何配置ElasticSearch Server的applicationContext等。

    ES客户端+谷歌浏览器插件+Multi-Elasticsearch-Head

    它基于Elasticsearch-Head或者Kibana的Dev Tools Console概念,但是增加了同时连接和管理多个Elasticsearch实例或集群的能力。这意味着用户可以通过一个统一的界面执行搜索、浏览索引、管理文档、监控集群状态以及...

    Elasticsearch实例代码

    Elasticsearch实例代码,简单演示Elasticsearch5.5下建立索引,查询,删除等操作。

    elasticsearch7.17.10-最新支持Java1.8版本

    6. **多租户**:一个Elasticsearch实例可以支持多个索引,每个索引有自己的设置和映射,实现资源隔离。 **Elasticsearch 7.17.10的新特性和改进:** 1. **性能优化**:此版本可能包含了针对查询速度、索引速度以及...

    elasticsearch实战

    这对于负责管理Elasticsearch实例的开发者和运维人员来说至关重要。 值得注意的是,本书适合那些已经具备一定编程和服务器管理经验的读者。作者通过具体案例和代码示例,帮助读者更好地理解和应用Elasticsearch的...

    基于Java8的SSM+Elasticsearch全文检索的个人博客系统.zip

    Elasticsearch是一款高性能的开源搜索引擎,特别适合进行全文检索。它基于Lucene库,但提供了更高级别的API和集群管理功能。在博客系统中,Elasticsearch可以存储和索引博客文章内容,以便快速地进行关键词搜索。 ...

    elasticsearch java操作的api实例

    Elasticsearch是一个强大的开源搜索引擎,基于Lucene,它提供了分布式、实时、可扩展的数据存储和检索功能。在Java环境中操作Elasticsearch,我们通常会利用官方提供的Java API,这是一个非常全面且强大的工具集,让...

    Go-go-elasticsearch:Elasticsearch官方的go语言客户端

    Elasticsearch是目前广泛应用的开源搜索引擎和分析引擎,尤其在日志分析、全文检索、实时数据存储等领域有着显著的优势。而`go-elasticsearch`则是Elasticsearch官方提供的用Go语言编写的客户端库,它为Go开发者提供...

Global site tag (gtag.js) - Google Analytics