소스 검색

搜索方法加Collector

admin 9 년 전
부모
커밋
501144f432

+ 67 - 87
search-console/src/main/java/com/uas/search/console/service/impl/SearchServiceImpl.java

@@ -12,6 +12,7 @@ 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;
@@ -20,7 +21,6 @@ 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.apache.lucene.search.BooleanClause.Occur;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;
@@ -37,6 +37,7 @@ 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;
 
@@ -120,10 +121,8 @@ public class SearchServiceImpl implements SearchService, InnerSearchService {
 		}
 		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);
+		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) {
@@ -139,10 +138,8 @@ public class SearchServiceImpl implements SearchService, InnerSearchService {
 		}
 		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);
+		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) {
@@ -180,8 +177,8 @@ public class SearchServiceImpl implements SearchService, InnerSearchService {
 	 * @return
 	 * @throws SearchException
 	 */
-	private Map<String, Object> getComponentIds(String keyword, PageParams pageParams, List<Long> kindIds,
-			List<Long> brandIds) 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);
@@ -222,45 +219,37 @@ public class SearchServiceImpl implements SearchService, InnerSearchService {
 
 				// 库存不为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);
+					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);
+						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))) {
+						|| !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_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_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);
+					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)));
+					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)) {
@@ -276,24 +265,21 @@ public class SearchServiceImpl implements SearchService, InnerSearchService {
 			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);
+					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);
+					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());
+				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());
@@ -344,18 +330,23 @@ public class SearchServiceImpl implements SearchService, InnerSearchService {
 				booleanQuery.add(brandQuery, BooleanClause.Occur.MUST);
 			}
 			logger.info(booleanQuery.toString());
-
-			TopDocs hits = indexSearcher.search(booleanQuery, SearchConstants.TOP_NUM);
-			ScoreDoc[] scoreDocs = hits.scoreDocs;
-			for (ScoreDoc scoreDoc : scoreDocs) {
-				Set<String> fieldsToLoad = new HashSet<>();
-				fieldsToLoad.add(SearchConstants.COMPONENT_KINDID_FIELD);
-				Document document = indexSearcher.doc(scoreDoc.doc, fieldsToLoad);
-				String cmp_kind_id = document.get(SearchConstants.COMPONENT_KINDID_FIELD);
-				if (!StringUtils.isEmpty(cmp_kind_id)) {
-					kindIds.add(Long.parseLong(cmp_kind_id));
-				}
-			}
+			// TopDocs hits = indexSearcher.search(booleanQuery,
+			// SearchConstants.TOP_NUM);
+			// ScoreDoc[] scoreDocs = hits.scoreDocs;
+			// for (ScoreDoc scoreDoc : scoreDocs) {
+			// Set<String> fieldsToLoad = new HashSet<>();
+			// fieldsToLoad.add(SearchConstants.COMPONENT_KINDID_FIELD);
+			// Document document = indexSearcher.doc(scoreDoc.doc,
+			// fieldsToLoad);
+			// String cmp_kind_id =
+			// document.get(SearchConstants.COMPONENT_KINDID_FIELD);
+			// if (!StringUtils.isEmpty(cmp_kind_id)) {
+			// kindIds.add(Long.parseLong(cmp_kind_id));
+			// }
+			// }
+			DistinctGroupCollector collector = new DistinctGroupCollector(SearchConstants.COMPONENT_KINDID_FIELD);
+			indexSearcher.search(booleanQuery, collector);
+			kindIds = collector.getValues();
 		} catch (IOException e) {
 			logger.error("", e);
 		} finally {
@@ -375,8 +366,7 @@ public class SearchServiceImpl implements SearchService, InnerSearchService {
 
 		BooleanQuery booleanQuery = new BooleanQuery();
 		for (Long kindId : kindIds) {
-			booleanQuery.add(new TermQuery(new Term(SearchConstants.KIND_ID_FIELD, String.valueOf(kindId))),
-					BooleanClause.Occur.SHOULD);
+			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) {
@@ -441,8 +431,7 @@ public class SearchServiceImpl implements SearchService, InnerSearchService {
 
 		BooleanQuery booleanQuery = new BooleanQuery();
 		for (Long brandId : brandIds) {
-			booleanQuery.add(new TermQuery(new Term(SearchConstants.BRAND_ID_FIELD, String.valueOf(brandId))),
-					BooleanClause.Occur.SHOULD);
+			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) {
@@ -501,8 +490,7 @@ public class SearchServiceImpl implements SearchService, InnerSearchService {
 
 		List<Map<String, Object>> components = new ArrayList<>();
 		try {
-			PrefixQuery prefixQuery = new PrefixQuery(
-					new Term(SearchConstants.COMPONENT_CODE_FIELD, componentCode.toLowerCase()));
+			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;
@@ -537,16 +525,13 @@ public class SearchServiceImpl implements SearchService, InnerSearchService {
 		String[] keywords = brandName.split(" ");
 		for (String keyword : keywords) {
 			// 搜索nameCn
-			booleanQuery.add(SearchUtils.getBooleanQuery(SearchConstants.BRAND_NAMECN_FIELD, keyword),
-					BooleanClause.Occur.SHOULD);
+			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);
+			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);
+		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)));
@@ -590,16 +575,14 @@ public class SearchServiceImpl implements SearchService, InnerSearchService {
 		}
 		List<Map<String, Object>> kinds = new ArrayList<>();
 		BooleanQuery booleanQuery = new BooleanQuery();
-		booleanQuery.add(SearchUtils.getBooleanQuery(SearchConstants.KIND_NAMECN_FIELD, kindName),
-				BooleanClause.Occur.MUST);
+		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);
+			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);
+				booleanQuery
+						.add(new TermQuery(new Term(SearchConstants.KIND_LEVEL_FIELD, String.valueOf(level))), BooleanClause.Occur.MUST);
 			}
 		}
 		logger.info(booleanQuery.toString());
@@ -616,8 +599,7 @@ public class SearchServiceImpl implements SearchService, InnerSearchService {
 	}
 
 	@Override
-	public List<Map<String, String>> getSimilarPropertyValues(Long kindId, Long propertyId, String keyword,
-			Long topNum) {
+	public List<Map<String, String>> getSimilarPropertyValues(Long kindId, Long propertyId, String keyword, Long topNum) {
 		if (kindId == null || propertyId == null) {
 			throw new SearchException("类目id和属性id不能为空");
 		}
@@ -640,8 +622,7 @@ public class SearchServiceImpl implements SearchService, InnerSearchService {
 			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);
+			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);
@@ -681,8 +662,8 @@ public class SearchServiceImpl implements SearchService, InnerSearchService {
 	 * @return
 	 */
 	private List<String> getSimilarComponentCodes(String componentCode) {
-		return getSimilarValues(SearchConstants.COMPONENT_TABLE_NAME, SearchConstants.COMPONENT_CODE_FIELD,
-				componentCode.toLowerCase(), QueryType.PREFIX_QUERY);
+		return getSimilarValues(SearchConstants.COMPONENT_TABLE_NAME, SearchConstants.COMPONENT_CODE_FIELD, componentCode.toLowerCase(),
+				QueryType.PREFIX_QUERY);
 	}
 
 	/**
@@ -693,8 +674,8 @@ public class SearchServiceImpl implements SearchService, InnerSearchService {
 	 */
 	private List<String> getSimilarBrandNames(String brandName) {
 		// 获取相似的中文品牌
-		List<String> nameCns = getSimilarValues(SearchConstants.BRAND_TABLE_NAME, SearchConstants.BRAND_NAMECN_FIELD,
-				brandName, QueryType.BOOLEAN_QUERY);
+		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;
@@ -706,8 +687,8 @@ public class SearchServiceImpl implements SearchService, InnerSearchService {
 		}
 
 		// 获取相似的英文品牌
-		List<String> nameEns = getSimilarValues(SearchConstants.BRAND_TABLE_NAME, SearchConstants.BRAND_NAMEEN_FIELD,
-				brandName, QueryType.BOOLEAN_QUERY);
+		List<String> nameEns = getSimilarValues(SearchConstants.BRAND_TABLE_NAME, SearchConstants.BRAND_NAMEEN_FIELD, brandName,
+				QueryType.BOOLEAN_QUERY);
 		if (CollectionUtils.isEmpty(nameEns)) {
 			return names;
 		}
@@ -729,8 +710,7 @@ public class SearchServiceImpl implements SearchService, InnerSearchService {
 	 * @return
 	 */
 	private List<String> getSimilarKindNames(String kindName) {
-		return getSimilarValues(SearchConstants.KIND_TABLE_NAME, SearchConstants.KIND_NAMECN_FIELD, kindName,
-				QueryType.BOOLEAN_QUERY);
+		return getSimilarValues(SearchConstants.KIND_TABLE_NAME, SearchConstants.KIND_NAMECN_FIELD, kindName, QueryType.BOOLEAN_QUERY);
 	}
 
 	private enum QueryType {
@@ -797,14 +777,14 @@ public class SearchServiceImpl implements SearchService, InnerSearchService {
 
 	@Override
 	public KindSimpleInfo getKind(Long id) {
-		return DocumentToObjectUtils.toKind(
-				SearchUtils.getDocumentById(SearchConstants.KIND_TABLE_NAME, SearchConstants.KIND_ID_FIELD, 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));
+		return DocumentToObjectUtils.toBrand(SearchUtils.getDocumentById(SearchConstants.BRAND_TABLE_NAME, SearchConstants.BRAND_ID_FIELD,
+				id));
 	}
 
 	@Override

+ 52 - 0
search-console/src/main/java/com/uas/search/grouping/DistinctGroupCollector.java

@@ -0,0 +1,52 @@
+package com.uas.search.grouping;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.lucene.index.LeafReader;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.search.SimpleCollector;
+
+public class DistinctGroupCollector extends SimpleCollector {
+
+	private final String groupField;
+
+	private final Set<String> fieldsToLoad;
+
+	private LeafReader reader;
+
+	private Set<Long> values = new HashSet<Long>();
+
+	public DistinctGroupCollector(String groupField) {
+		this.groupField = groupField;
+		this.fieldsToLoad = new HashSet<String>();
+		this.fieldsToLoad.add(groupField);
+	}
+
+	@Override
+	public boolean needsScores() {
+		return false;
+	}
+
+	@Override
+	protected void doSetNextReader(LeafReaderContext context) throws IOException {
+		super.doSetNextReader(context);
+		reader = context.reader();
+	}
+
+	@Override
+	public void collect(int doc) throws IOException {
+		if (null != reader) {
+			String value = reader.document(doc, fieldsToLoad).get(groupField);
+			if (null != value) {
+				values.add(Long.parseLong(value));
+			}
+		}
+	}
+
+	public Set<Long> getValues() {
+		return values;
+	}
+
+}