欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Elastic Search搜索实例

程序员文章站 2022-05-13 13:34:42
...
要从现在的公司离职了。记录一下自己针对我们的自己需求所做的搜索。
基于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);
	}

}