package com.uas.search.console.service.impl;

import java.io.IOException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.lucene.document.Document;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.NumericRangeQuery;
import org.apache.lucene.search.PrefixQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import com.alibaba.fastjson.JSONObject;
import com.uas.search.console.core.util.FastjsonUtils;
import com.uas.search.console.model.BrandSimpleInfo;
import com.uas.search.console.model.ComponentSimpleInfo;
import com.uas.search.console.model.KindSimpleInfo;
import com.uas.search.console.service.InnerSearchService;
import com.uas.search.console.util.DocumentToObjectUtils;
import com.uas.search.console.util.SearchConstants;
import com.uas.search.console.util.SearchUtils;
import com.uas.search.exception.SearchException;
import com.uas.search.grouping.DistinctGroupCollector;
import com.uas.search.model.PageParams;
import com.uas.search.service.SearchService;

/**
 * 搜索索引
 * 
 * @author sunyj
 * @since 2016年8月5日 下午2:21:26
 */
@Service
public class SearchServiceImpl implements SearchService, InnerSearchService {

	/**
	 * 获取联想词时返回的最大数目
	 */
	private static final int SIMILAR_NUM = 20;

	private static Logger logger = LoggerFactory.getLogger(SearchServiceImpl.class);

	@Override
	public List<Long> getKindIds(String keyword) {
		return getKindIds(keyword, Occur.MUST);
	}

	/**
	 * 根据关键字搜索产品类目id
	 * 
	 * @param keyword
	 *            关键词
	 * @param occur
	 * @return 符合条件的类目id
	 */
	private List<Long> getKindIds(String keyword, Occur occur) {
		if (SearchUtils.isKeywordInvalid(keyword)) {
			throw new SearchException("搜索关键词无效：" + keyword);
		}
		List<Long> ids = new ArrayList<Long>();
		BooleanQuery booleanQuery = SearchUtils.getBooleanQuery(SearchConstants.KIND_NAMECN_FIELD, keyword, occur);
		logger.info(booleanQuery.toString());
		List<Document> documents = SearchUtils.getDocuments(SearchConstants.KIND_TABLE_NAME, booleanQuery);
		for (Document document : documents) {
			ids.add(Long.parseLong(document.get(SearchConstants.KIND_ID_FIELD)));
		}
		return ids;
	}

	@Override
	public List<Map<String, Object>> getKinds(String keyword) {
		if (SearchUtils.isKeywordInvalid(keyword)) {
			throw new SearchException("搜索关键词无效：" + keyword);
		}
		List<Map<String, Object>> kinds = new ArrayList<Map<String, Object>>();
		BooleanQuery booleanQuery = SearchUtils.getBooleanQuery(SearchConstants.KIND_NAMECN_FIELD, keyword);
		logger.info(booleanQuery.toString());
		List<Document> documents = SearchUtils.getDocuments(SearchConstants.KIND_TABLE_NAME, booleanQuery);
		for (Document document : documents) {
			Map<String, Object> kind = new HashMap<String, Object>();
			kind.put("id", Long.parseLong(document.get(SearchConstants.KIND_ID_FIELD)));
			kind.put("nameCn", document.get(SearchConstants.KIND_NAMECN_FIELD));
			kinds.add(kind);
		}
		return kinds;
	}

	@Override
	public List<Long> getBrandIds(String keyword) {
		return getBrandIds(keyword, Occur.MUST);
	}

	/**
	 * 根据关键字搜索产品品牌id
	 * 
	 * @param keyword
	 *            关键词
	 * @param occur
	 * @return 符合条件的品牌id
	 */
	private List<Long> getBrandIds(String keyword, Occur occur) {
		if (SearchUtils.isKeywordInvalid(keyword)) {
			throw new SearchException("搜索关键词无效：" + keyword);
		}
		List<Long> ids = new ArrayList<Long>();
		BooleanQuery booleanQuery = new BooleanQuery();
		booleanQuery.add(SearchUtils.getBooleanQuery(SearchConstants.BRAND_NAMECN_FIELD, keyword, occur), BooleanClause.Occur.SHOULD);
		booleanQuery.add(SearchUtils.getBooleanQuery(SearchConstants.BRAND_NAMEEN_FIELD, keyword, occur), BooleanClause.Occur.SHOULD);
		logger.info(booleanQuery.toString());
		List<Document> documents = SearchUtils.getDocuments(SearchConstants.BRAND_TABLE_NAME, booleanQuery);
		for (Document document : documents) {
			ids.add(Long.parseLong(document.get(SearchConstants.BRAND_ID_FIELD)));
		}
		return ids;
	}

	@Override
	public List<Map<String, Object>> getBrands(String keyword) {
		if (SearchUtils.isKeywordInvalid(keyword)) {
			throw new SearchException("搜索关键词无效：" + keyword);
		}
		List<Map<String, Object>> brands = new ArrayList<Map<String, Object>>();
		BooleanQuery booleanQuery = new BooleanQuery();
		booleanQuery.add(SearchUtils.getBooleanQuery(SearchConstants.BRAND_NAMECN_FIELD, keyword), BooleanClause.Occur.SHOULD);
		booleanQuery.add(SearchUtils.getBooleanQuery(SearchConstants.BRAND_NAMEEN_FIELD, keyword), BooleanClause.Occur.SHOULD);
		logger.info(booleanQuery.toString());
		List<Document> documents = SearchUtils.getDocuments(SearchConstants.BRAND_TABLE_NAME, booleanQuery);
		for (Document document : documents) {
			Map<String, Object> brand = new HashMap<String, Object>();
			brand.put("id", Long.parseLong(document.get(SearchConstants.BRAND_ID_FIELD)));
			brand.put("uuid", document.get(SearchConstants.BRAND_UUID_FIELD));
			brand.put("nameCn", document.get(SearchConstants.BRAND_NAMECN_FIELD));
			brand.put("nameEn", document.get(SearchConstants.BRAND_NAMEEN_FIELD));
			brands.add(brand);
		}
		return brands;
	}

	@Override
	public Map<String, Object> getComponentIds(String keyword, PageParams pageParams) {
		Map<String, Object> searchComponentIds = getComponentIds(keyword, pageParams, null, null);
		return searchComponentIds;
		// TODO 对品牌、类目甚至拼音混合搜索（待完善）
		// int total = (int) searchComponentIds.get("total");
		// if (total != 0) {
		// return searchComponentIds;
		// }
		// List<Long> kindIds = getKindIds(keyword, Occur.SHOULD);
		// List<Long> brandIds = getBrandIds(keyword, Occur.SHOULD);
		// return getComponentIds(null, pageParams, kindIds, brandIds);
	}

	/**
	 * 根据关键词搜索产品
	 * 
	 * @param keyword
	 * @param pageParams
	 * @param kindIds
	 * @param brandIds
	 * @return
	 * @throws SearchException
	 */
	private Map<String, Object> getComponentIds(String keyword, PageParams pageParams, List<Long> kindIds, List<Long> brandIds)
			throws SearchException {
		// 因为器件、属性值的数据量远比类目、品牌大得多，而且器件搜索可能还需进行分页，
		// 所以涉及器件、属性值的搜索，大都不能像类目和品牌一样直接利用SearchUtils.getDocuments方法
		IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.COMPONENT_TABLE_NAME);

		if (pageParams == null) {
			pageParams = new PageParams();
		}
		if (pageParams.getPage() == 0)
			pageParams.setPage(1);
		if (pageParams.getSize() == 0)
			pageParams.setSize(20);

		Map<String, Object> map = new HashMap<String, Object>();
		List<Long> ids = new ArrayList<Long>();
		try {
			BooleanQuery booleanQuery = new BooleanQuery();
			if (!SearchUtils.isKeywordInvalid(keyword)) {
				keyword = keyword.toLowerCase();
				PrefixQuery prefixQuery = new PrefixQuery(new Term(SearchConstants.COMPONENT_CODE_FIELD, keyword));
				booleanQuery.add(prefixQuery, BooleanClause.Occur.MUST);
			}

			Map<String, Object> filters = pageParams.getFilters();
			if (!CollectionUtils.isEmpty(filters)) {
				// 筛选类目
				if (!StringUtils.isEmpty(filters.get(com.uas.search.utils.SearchConstants.COMPONENT_KINDID_KEY))) {
					String kindId = filters.get(com.uas.search.utils.SearchConstants.COMPONENT_KINDID_KEY).toString();
					TermQuery kindQuery = new TermQuery(new Term(SearchConstants.COMPONENT_KINDID_FIELD, kindId));
					booleanQuery.add(kindQuery, BooleanClause.Occur.MUST);
				}

				// 筛选品牌
				if (!StringUtils.isEmpty(filters.get(com.uas.search.utils.SearchConstants.COMPONENT_BRANDID_KEY))) {
					String brandId = filters.get(com.uas.search.utils.SearchConstants.COMPONENT_BRANDID_KEY).toString();
					TermQuery brandQuery = new TermQuery(new Term(SearchConstants.COMPONENT_BRANDID_FIELD, brandId));
					booleanQuery.add(brandQuery, BooleanClause.Occur.MUST);
				}

				// 库存不为0
				if (!StringUtils.isEmpty(filters.get(com.uas.search.utils.SearchConstants.COMPONENT_RESERVE_KEY))) {
					Boolean isReserveNotEmpty = (Boolean) filters.get(com.uas.search.utils.SearchConstants.COMPONENT_RESERVE_KEY);
					if (isReserveNotEmpty) {
						booleanQuery.add(NumericRangeQuery.newDoubleRange(SearchConstants.COMPONENT_RESERVE_FIELD, 0.0, Double.MAX_VALUE,
								false, true), BooleanClause.Occur.MUST);
					}
				}

				// 现货、呆滞库存、样品数量不为0，取或的关系
				if (!StringUtils.isEmpty(filters.get(com.uas.search.utils.SearchConstants.COMPONENT_SAMPLE_QTY_KEY))
						|| !StringUtils.isEmpty(filters.get(com.uas.search.utils.SearchConstants.COMPONENT_ORIGINAL_QTY_KEY))
						|| !StringUtils.isEmpty(filters.get(com.uas.search.utils.SearchConstants.COMPONENT_INACTION_STOCK_QTY_KEY))) {
					BooleanQuery booleanQuery2 = new BooleanQuery();
					if (!StringUtils.isEmpty(filters.get(com.uas.search.utils.SearchConstants.COMPONENT_SAMPLE_QTY_KEY))) {
						booleanQuery2.add(NumericRangeQuery.newDoubleRange(SearchConstants.COMPONENT_SAMPLE_QTY_FIELD, 0.0,
								Double.MAX_VALUE, false, true), BooleanClause.Occur.SHOULD);
					}
					if (!StringUtils.isEmpty(filters.get(com.uas.search.utils.SearchConstants.COMPONENT_ORIGINAL_QTY_KEY))) {
						booleanQuery2.add(NumericRangeQuery.newDoubleRange(SearchConstants.COMPONENT_ORIGINAL_QTY_FIELD, 0.0,
								Double.MAX_VALUE, false, true), BooleanClause.Occur.SHOULD);
					}
					if (!StringUtils.isEmpty(filters.get(com.uas.search.utils.SearchConstants.COMPONENT_INACTION_STOCK_QTY_KEY))) {
						booleanQuery2.add(NumericRangeQuery.newDoubleRange(SearchConstants.COMPONENT_INACTION_STOCK_QTY_FIELD, 0.0,
								Double.MAX_VALUE, false, true), BooleanClause.Occur.SHOULD);
					}
					booleanQuery.add(booleanQuery2, Occur.MUST);
				}

				// 属性过滤
				if (!StringUtils.isEmpty(filters.get(com.uas.search.utils.SearchConstants.COMPONENT_PROPERTIES_KEY))) {
					JSONObject proJSON = FastjsonUtils.parseObject(String.valueOf(filters
							.get(com.uas.search.utils.SearchConstants.COMPONENT_PROPERTIES_KEY)));
					for (String key : proJSON.keySet()) {
						String value = String.valueOf(proJSON.get(key));
						if (!StringUtils.isEmpty(value)) {
							if (!key.startsWith(SearchConstants.COMPONENT_PROPERTY_PREFIX)) {
								key = SearchConstants.COMPONENT_PROPERTY_PREFIX + key;
							}
							TermQuery propertyQuery = new TermQuery(new Term(key, value));
							booleanQuery.add(propertyQuery, BooleanClause.Occur.MUST);
						}
					}
				}
			}
			if (!CollectionUtils.isEmpty(kindIds)) {
				BooleanQuery booleanQuery2 = new BooleanQuery();
				for (Long id : kindIds) {
					booleanQuery2.add(new TermQuery(new Term(SearchConstants.COMPONENT_KINDID_FIELD, id.toString())), Occur.SHOULD);
				}
				booleanQuery.add(booleanQuery2, Occur.MUST);
			}
			if (!CollectionUtils.isEmpty(brandIds)) {
				BooleanQuery booleanQuery2 = new BooleanQuery();
				for (Long id : brandIds) {
					booleanQuery2.add(new TermQuery(new Term(SearchConstants.COMPONENT_BRANDID_FIELD, id.toString())), Occur.SHOULD);
				}
				booleanQuery.add(booleanQuery2, Occur.MUST);
			}
			logger.info(booleanQuery.toString());
			TopDocs hits;
			if (pageParams.getPage() > 1) {// 不是第一页
				TopDocs previousHits = indexSearcher.search(booleanQuery, (pageParams.getPage() - 1) * pageParams.getSize());
				ScoreDoc[] previousScoreDocs = previousHits.scoreDocs;
				ScoreDoc after = previousScoreDocs[previousScoreDocs.length - 1];
				hits = indexSearcher.searchAfter(after, booleanQuery, pageParams.getSize());
			} else {
				hits = indexSearcher.search(booleanQuery, pageParams.getSize());
			}

			ScoreDoc[] scoreDocs = hits.scoreDocs;
			for (ScoreDoc scoreDoc : scoreDocs) {
				// 数据量太大，需要指定将获取的数据（以免载入不必要的数据，降低速度）
				Set<String> fieldsToLoad = new HashSet<>();
				fieldsToLoad.add(SearchConstants.COMPONENT_ID_FIELD);
				Document document = indexSearcher.doc(scoreDoc.doc, fieldsToLoad);
				String componentId = document.get(SearchConstants.COMPONENT_ID_FIELD);
				ids.add(Long.parseLong(componentId));
			}
			map.put("componentIds", ids);
			map.put("page", pageParams.getPage());
			map.put("size", pageParams.getSize());
			map.put("total", hits.totalHits);
		} catch (IOException e) {
			logger.error("", e);
		} finally {
			SearchUtils.releaseIndexSearcher(indexSearcher);
		}
		return map;
	}

	@Override
	public Set<Long> getKindIdsBySearchComponent(String keyword, String brandId) {
		if (SearchUtils.isKeywordInvalid(keyword)) {
			throw new SearchException("搜索关键词无效：" + keyword);
		}
		IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.COMPONENT_TABLE_NAME);

		Set<Long> kindIds = new HashSet<Long>();
		try {
			BooleanQuery booleanQuery = new BooleanQuery();

			keyword = URLDecoder.decode(keyword, "UTF-8");
			keyword = keyword.toLowerCase();
			PrefixQuery prefixQuery = new PrefixQuery(new Term(SearchConstants.COMPONENT_CODE_FIELD, keyword));
			booleanQuery.add(prefixQuery, BooleanClause.Occur.MUST);

			// 筛选品牌
			if (!StringUtils.isEmpty(brandId)) {
				TermQuery brandQuery = new TermQuery(new Term(SearchConstants.COMPONENT_BRANDID_FIELD, brandId));
				booleanQuery.add(brandQuery, BooleanClause.Occur.MUST);
			}
			logger.info(booleanQuery.toString());

			DistinctGroupCollector collector = new DistinctGroupCollector(SearchConstants.COMPONENT_KINDID_FIELD);
			indexSearcher.search(booleanQuery, collector);
			kindIds = collector.getValues();
		} catch (IOException e) {
			logger.error("", e);
		} finally {
			SearchUtils.releaseIndexSearcher(indexSearcher);
		}

		return kindIds;
	}

	@Override
	public List<Map<String, Object>> getKindsBySearchComponent(String keyword, String brandId) {
		Set<Long> kindIds = getKindIdsBySearchComponent(keyword, brandId);
		List<Map<String, Object>> kinds = new ArrayList<Map<String, Object>>();
		if (CollectionUtils.isEmpty(kindIds)) {
			return kinds;
		}

		BooleanQuery booleanQuery = new BooleanQuery();
		for (Long kindId : kindIds) {
			booleanQuery.add(new TermQuery(new Term(SearchConstants.KIND_ID_FIELD, String.valueOf(kindId))), BooleanClause.Occur.SHOULD);
		}
		List<Document> documents = SearchUtils.getDocuments(SearchConstants.KIND_TABLE_NAME, booleanQuery);
		for (Document document : documents) {
			Map<String, Object> kind = new HashMap<String, Object>();
			kind.put("id", Long.parseLong(document.get(SearchConstants.KIND_ID_FIELD)));
			kind.put("nameCn", document.get(SearchConstants.KIND_NAMECN_FIELD));
			kinds.add(kind);
		}
		return kinds;
	}

	@Override
	public Set<Long> getBrandIdsBySearchComponent(String keyword, String kindId) {
		if (SearchUtils.isKeywordInvalid(keyword)) {
			throw new SearchException("搜索关键词无效：" + keyword);
		}
		IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.COMPONENT_TABLE_NAME);

		Set<Long> brandIds = new HashSet<Long>();
		try {
			BooleanQuery booleanQuery = new BooleanQuery();

			keyword = URLDecoder.decode(keyword, "UTF-8");
			keyword = keyword.toLowerCase();
			PrefixQuery prefixQuery = new PrefixQuery(new Term(SearchConstants.COMPONENT_CODE_FIELD, keyword));
			booleanQuery.add(prefixQuery, BooleanClause.Occur.MUST);

			// 筛选类目
			if (!StringUtils.isEmpty(kindId)) {
				TermQuery kindQuery = new TermQuery(new Term(SearchConstants.COMPONENT_KINDID_FIELD, kindId));
				booleanQuery.add(kindQuery, BooleanClause.Occur.MUST);
			}
			logger.info(booleanQuery.toString());

			DistinctGroupCollector collector = new DistinctGroupCollector(SearchConstants.COMPONENT_BRANDID_FIELD);
			indexSearcher.search(booleanQuery, collector);
			brandIds = collector.getValues();
		} catch (IOException e) {
			logger.error("", e);
		} finally {
			SearchUtils.releaseIndexSearcher(indexSearcher);
		}

		return brandIds;
	}

	@Override
	public List<Map<String, Object>> getBrandsBySearchComponent(String keyword, String kindId) {
		Set<Long> brandIds = getBrandIdsBySearchComponent(keyword, kindId);
		List<Map<String, Object>> brands = new ArrayList<Map<String, Object>>();
		if (CollectionUtils.isEmpty(brandIds)) {
			return brands;
		}

		BooleanQuery booleanQuery = new BooleanQuery();
		for (Long brandId : brandIds) {
			booleanQuery.add(new TermQuery(new Term(SearchConstants.BRAND_ID_FIELD, String.valueOf(brandId))), BooleanClause.Occur.SHOULD);
		}
		List<Document> documents = SearchUtils.getDocuments(SearchConstants.BRAND_TABLE_NAME, booleanQuery);
		for (Document document : documents) {
			Map<String, Object> brand = new HashMap<String, Object>();
			brand.put("id", Long.parseLong(document.get(SearchConstants.BRAND_ID_FIELD)));
			brand.put("uuid", document.get(SearchConstants.BRAND_UUID_FIELD));
			brand.put("nameCn", document.get(SearchConstants.BRAND_NAMECN_FIELD));
			brands.add(brand);
		}
		return brands;
	}

	@Override
	public List<String> getSimilarKeywords(String keyword) {
		// 相似的器件原厂型号数量足够，直接返回
		List<String> componentCodes = getSimilarComponentCodes(keyword);
		if (componentCodes != null && componentCodes.size() == SIMILAR_NUM) {
			return componentCodes;
		}

		List<String> result = componentCodes;
		if (result == null) {
			result = new ArrayList<>();
		}

		// 获取相似类目
		List<String> kindNames = getSimilarKindNames(keyword);
		if (!CollectionUtils.isEmpty(kindNames)) {
			result.addAll(kindNames);
			// 如果总的数量超出SIMILAR_NUM，去除多余的元素
			if (result.size() > SIMILAR_NUM) {
				removeElements(result, SIMILAR_NUM);
				return result;
			}
		}

		// 获取相似品牌
		List<String> brandNames = getSimilarBrandNames(keyword);
		if (!CollectionUtils.isEmpty(brandNames)) {
			result.addAll(brandNames);
			if (result.size() > SIMILAR_NUM) {
				removeElements(result, SIMILAR_NUM);
				return result;
			}
		}

		return result;
	}

	@Override
	public List<Map<String, Object>> getSimilarComponents(String componentCode) {
		if (SearchUtils.isKeywordInvalid(componentCode)) {
			throw new SearchException("输入无效：" + componentCode);
		}
		IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.COMPONENT_TABLE_NAME);

		List<Map<String, Object>> components = new ArrayList<>();
		try {
			PrefixQuery prefixQuery = new PrefixQuery(new Term(SearchConstants.COMPONENT_CODE_FIELD, componentCode.toLowerCase()));
			logger.info(prefixQuery.toString());
			TopDocs hits = indexSearcher.search(prefixQuery, SIMILAR_NUM);
			ScoreDoc[] scoreDocs = hits.scoreDocs;
			for (ScoreDoc scoreDoc : scoreDocs) {
				Set<String> fieldsToLoad = new HashSet<>();
				fieldsToLoad.add(SearchConstants.COMPONENT_ID_FIELD);
				fieldsToLoad.add(SearchConstants.COMPONENT_UUID_FIELD);
				fieldsToLoad.add(SearchConstants.COMPONENT_CODE_FIELD);
				Document document = indexSearcher.doc(scoreDoc.doc, fieldsToLoad);
				Map<String, Object> map = new HashMap<>();
				map.put("id", Long.parseLong(document.get(SearchConstants.COMPONENT_ID_FIELD)));
				map.put("uuid", document.get(SearchConstants.COMPONENT_UUID_FIELD));
				map.put("code", document.get(SearchConstants.COMPONENT_CODE_FIELD));
				components.add(map);
			}
		} catch (IOException e) {
			logger.error("", e);
		} finally {
			SearchUtils.releaseIndexSearcher(indexSearcher);
		}
		return components;
	}

	@Override
	public List<Map<String, Object>> getSimilarBrands(String brandName) {
		if (SearchUtils.isKeywordInvalid(brandName)) {
			throw new SearchException("输入无效：" + brandName);
		}
		List<Map<String, Object>> brands = new ArrayList<Map<String, Object>>();
		// 品牌名称带有空格，并且中英文名并无一定顺序，因此对nameCn、nameEn均要搜索
		BooleanQuery booleanQuery = new BooleanQuery();
		String[] keywords = brandName.split(" ");
		for (String keyword : keywords) {
			// 搜索nameCn
			booleanQuery.add(SearchUtils.getBooleanQuery(SearchConstants.BRAND_NAMECN_FIELD, keyword), BooleanClause.Occur.SHOULD);
			// 搜索nameEn
			booleanQuery.add(SearchUtils.getBooleanQuery(SearchConstants.BRAND_NAMEEN_FIELD, keyword), BooleanClause.Occur.SHOULD);
		}
		logger.info(booleanQuery.toString());

		List<Document> documents = SearchUtils.getDocuments(SearchConstants.BRAND_TABLE_NAME, booleanQuery, SIMILAR_NUM);
		for (Document document : documents) {
			Map<String, Object> brand = new HashMap<>();
			brand.put("id", Long.parseLong(document.get(SearchConstants.BRAND_ID_FIELD)));
			brand.put("uuid", document.get(SearchConstants.BRAND_UUID_FIELD));
			brand.put("nameCn", document.get(SearchConstants.BRAND_NAMECN_FIELD));
			brand.put("nameEn", document.get(SearchConstants.BRAND_NAMEEN_FIELD));
			brands.add(brand);
		}
		return brands;
	}

	@Override
	public List<Map<String, Object>> getSimilarKinds(String kindName) {
		return getSimilarKinds(kindName, null, null);
	}

	@Override
	public List<Map<String, Object>> getSimilarLeafKinds(String kindName) {
		return getSimilarKinds(kindName, (short) 1, null);
	}

	@Override
	public List<Map<String, Object>> getSimilarKindsByLevel(String kindName, Short level) {
		return getSimilarKinds(kindName, null, level);
	}

	/**
	 * 根据输入的类目名获取联想词
	 * 
	 * @param kindName
	 *            类目名
	 * @param isLeaf
	 *            是否只获取末级类目
	 * @param level
	 *            指定的类目级别
	 * @return
	 */
	private List<Map<String, Object>> getSimilarKinds(String kindName, Short isLeaf, Short level) {
		if (SearchUtils.isKeywordInvalid(kindName)) {
			throw new SearchException("输入无效：" + kindName);
		}
		List<Map<String, Object>> kinds = new ArrayList<>();
		BooleanQuery booleanQuery = new BooleanQuery();
		booleanQuery.add(SearchUtils.getBooleanQuery(SearchConstants.KIND_NAMECN_FIELD, kindName), BooleanClause.Occur.MUST);

		if (isLeaf != null && isLeaf == 1) {
			booleanQuery.add(new TermQuery(new Term(SearchConstants.KIND_ISLEAF_FIELD, String.valueOf(isLeaf))), BooleanClause.Occur.MUST);
		} else {
			if (level != null && level > 0) {
				booleanQuery
						.add(new TermQuery(new Term(SearchConstants.KIND_LEVEL_FIELD, String.valueOf(level))), BooleanClause.Occur.MUST);
			}
		}
		logger.info(booleanQuery.toString());
		List<Document> documents = SearchUtils.getDocuments(SearchConstants.KIND_TABLE_NAME, booleanQuery, SIMILAR_NUM);
		for (Document document : documents) {
			Map<String, Object> map = new HashMap<>();
			map.put("id", Long.parseLong(document.get(SearchConstants.KIND_ID_FIELD)));
			map.put("nameCn", document.get(SearchConstants.KIND_NAMECN_FIELD));
			map.put("isLeaf", Short.parseShort(document.get(SearchConstants.KIND_ISLEAF_FIELD)));
			map.put("level", Short.parseShort(document.get(SearchConstants.KIND_LEVEL_FIELD)));
			kinds.add(map);
		}
		return kinds;
	}

	@Override
	public List<Map<String, String>> getSimilarPropertyValues(Long kindId, Long propertyId, String keyword, Long topNum) {
		if (kindId == null || propertyId == null) {
			throw new SearchException("类目id和属性id不能为空");
		}
		IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.COMPONENT_TABLE_NAME);

		String propertyIdString = String.valueOf(propertyId);
		if (!propertyIdString.startsWith(SearchConstants.COMPONENT_PROPERTY_PREFIX)) {
			propertyIdString = SearchConstants.COMPONENT_PROPERTY_PREFIX + propertyIdString;
		}
		propertyIdString = propertyIdString + SearchConstants.COMPONENT_PROPERTY_TOKENIZED_SUFFIX;
		if (keyword == null) {
			keyword = "";
		}
		if (topNum == null || topNum < 1) {
			topNum = (long) SIMILAR_NUM;
		}

		List<String> propertyValues = new ArrayList<>();
		try {
			BooleanQuery booleanQuery = new BooleanQuery();
			booleanQuery.add(new TermQuery(new Term(SearchConstants.COMPONENT_KINDID_FIELD, String.valueOf(kindId))),
					BooleanClause.Occur.MUST);
			booleanQuery.add(new PrefixQuery(new Term(propertyIdString, keyword.toLowerCase())), BooleanClause.Occur.MUST);
			logger.info(booleanQuery.toString());
			// 如果只搜索topNum个结果，去除重复的属性值后，数目很可能是不够的
			TopDocs topDocs = indexSearcher.search(booleanQuery, SearchConstants.TOP_NUM);
			ScoreDoc[] scoreDocs = topDocs.scoreDocs;
			for (ScoreDoc scoreDoc : scoreDocs) {
				Set<String> fieldsToLoad = new HashSet<>();
				fieldsToLoad.add(propertyIdString);
				Document document = indexSearcher.doc(scoreDoc.doc, fieldsToLoad);
				String propertyValue = document.get(propertyIdString);
				if (!StringUtils.isEmpty(propertyValue) && !propertyValues.contains(propertyValue)) {
					propertyValues.add(propertyValue);
				}
				if (propertyValues.size() >= topNum) {
					break;
				}
			}
		} catch (IOException e) {
			logger.error("", e);
		} finally {
			SearchUtils.releaseIndexSearcher(indexSearcher);
		}

		List<Map<String, String>> result = new ArrayList<>();
		for (String propertyValue : propertyValues) {
			Map<String, String> map = new HashMap<>();
			map.put("propertyValue", propertyValue);
			result.add(map);
		}
		return result;

	}

	/**
	 * 根据输入获取相似的器件原厂型号
	 * 
	 * @param componentCode
	 * @return
	 */
	private List<String> getSimilarComponentCodes(String componentCode) {
		return getSimilarValues(SearchConstants.COMPONENT_TABLE_NAME, SearchConstants.COMPONENT_CODE_FIELD, componentCode.toLowerCase(),
				QueryType.PREFIX_QUERY);
	}

	/**
	 * 根据输入获取相似的品牌名称
	 * 
	 * @param brandName
	 * @return
	 */
	private List<String> getSimilarBrandNames(String brandName) {
		// 获取相似的中文品牌
		List<String> nameCns = getSimilarValues(SearchConstants.BRAND_TABLE_NAME, SearchConstants.BRAND_NAMECN_FIELD, brandName,
				QueryType.BOOLEAN_QUERY);
		// 相似的中文品牌数量足够，直接返回
		if (nameCns != null && nameCns.size() == SIMILAR_NUM) {
			return nameCns;
		}

		List<String> names = nameCns;
		if (names == null) {
			names = new ArrayList<>();
		}

		// 获取相似的英文品牌
		List<String> nameEns = getSimilarValues(SearchConstants.BRAND_TABLE_NAME, SearchConstants.BRAND_NAMEEN_FIELD, brandName,
				QueryType.BOOLEAN_QUERY);
		if (CollectionUtils.isEmpty(nameEns)) {
			return names;
		}
		for (String name : nameEns) {
			if (!names.contains(name)) {
				names.add(name);
			}
		}
		if (names.size() > SIMILAR_NUM) {
			removeElements(names, SIMILAR_NUM);
		}
		return names;
	}

	/**
	 * 根据输入获取相似的类目名称
	 * 
	 * @param kindName
	 * @return
	 */
	private List<String> getSimilarKindNames(String kindName) {
		return getSimilarValues(SearchConstants.KIND_TABLE_NAME, SearchConstants.KIND_NAMECN_FIELD, kindName, QueryType.BOOLEAN_QUERY);
	}

	private enum QueryType {
		BOOLEAN_QUERY, PREFIX_QUERY
	}

	/**
	 * 根据输入值获取该域相似的值
	 * 
	 * @param tableName
	 * @param field
	 * @param keyword
	 * @return
	 */
	private List<String> getSimilarValues(String tableName, String field, String keyword, QueryType queryType) {
		if (SearchUtils.isKeywordInvalid(keyword)) {
			throw new SearchException("输入无效：" + keyword);
		}
		if (queryType == null) {
			queryType = QueryType.BOOLEAN_QUERY;
		}
		IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(tableName);

		List<String> result = new ArrayList<>();
		try {
			Query query = null;
			if (queryType == QueryType.BOOLEAN_QUERY) {
				query = SearchUtils.getBooleanQuery(field, keyword);
			} else if (queryType == QueryType.PREFIX_QUERY) {
				query = new PrefixQuery(new Term(field, keyword));
			}
			logger.info(query.toString());
			TopDocs hits = indexSearcher.search(query, SIMILAR_NUM);
			ScoreDoc[] scoreDocs = hits.scoreDocs;
			for (ScoreDoc scoreDoc : scoreDocs) {
				Set<String> fieldsToLoad = new HashSet<>();
				fieldsToLoad.add(field);
				Document document = indexSearcher.doc(scoreDoc.doc, fieldsToLoad);
				result.add(document.get(field));
			}
		} catch (IOException e) {
			logger.error("", e);
		} finally {
			SearchUtils.releaseIndexSearcher(indexSearcher);
		}
		return result;
	}

	/**
	 * 删除lists内startIndex（含）后的元素
	 * 
	 * @param lists
	 * @param startIndex
	 */
	private void removeElements(List<String> lists, int startIndex) {
		if (CollectionUtils.isEmpty(lists)) {
			return;
		}
		int listsSize = lists.size();
		for (int i = listsSize - 1; i >= startIndex; i--) {
			lists.remove(i);
		}
	}

	@Override
	public KindSimpleInfo getKind(Long id) {
		return DocumentToObjectUtils
				.toKind(SearchUtils.getDocumentById(SearchConstants.KIND_TABLE_NAME, SearchConstants.KIND_ID_FIELD, id));
	}

	@Override
	public BrandSimpleInfo getBrand(Long id) {
		return DocumentToObjectUtils.toBrand(SearchUtils.getDocumentById(SearchConstants.BRAND_TABLE_NAME, SearchConstants.BRAND_ID_FIELD,
				id));
	}

	@Override
	public ComponentSimpleInfo getComponent(Long id) {
		return DocumentToObjectUtils.toComponent(SearchUtils.getDocumentById(SearchConstants.COMPONENT_TABLE_NAME,
				SearchConstants.COMPONENT_ID_FIELD, id));
	}

}
