|
|
@@ -1073,6 +1073,230 @@ public class SearchServiceImpl implements SearchService {
|
|
|
return new Sort(sortFields);
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ public Map<String, Object> getPCBGoodsIds(String keyword, PageParams pageParams) throws IOException {
|
|
|
+ keyword = recursivelyGetPCBGoodsIds(keyword);
|
|
|
+ // 因为器件、属性值等的数据量远比类目、品牌大得多,而且器件搜索可能还需进行分页,
|
|
|
+ // 所以涉及器件、属性值的搜索,大都不能像类目和品牌一样直接利用SearchUtils.getDocuments方法
|
|
|
+ IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.PCB_GOODS_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> pcbIds = new ArrayList<>();
|
|
|
+ List<Long> goIds = new ArrayList<>();
|
|
|
+ try {
|
|
|
+ BooleanQuery booleanQuery = queryPCBGoods(keyword);
|
|
|
+ setPCBGoodsFilter(pageParams.getFilters(), booleanQuery);
|
|
|
+ logger.info(booleanQuery.toString());
|
|
|
+
|
|
|
+ Sort sort = sortPCBGoods(keyword, pageParams.getSort());
|
|
|
+ TopDocs hits;
|
|
|
+ if (pageParams.getPage() > 1) {// 不是第一页
|
|
|
+ TopDocs previousHits = indexSearcher.search(booleanQuery,
|
|
|
+ (pageParams.getPage() - 1) * pageParams.getSize(), sort, true, false);
|
|
|
+ int totalHits = previousHits.totalHits;
|
|
|
+ if ((pageParams.getPage() - 1) * pageParams.getSize() >= totalHits) {
|
|
|
+ return map;
|
|
|
+ }
|
|
|
+ ScoreDoc[] previousScoreDocs = previousHits.scoreDocs;
|
|
|
+ ScoreDoc after = previousScoreDocs[previousScoreDocs.length - 1];
|
|
|
+ hits = indexSearcher.searchAfter(after, booleanQuery, pageParams.getSize(), sort, true, false);
|
|
|
+ } else {
|
|
|
+ hits = indexSearcher.search(booleanQuery, pageParams.getSize(), sort, true, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 数据量太大,需要指定将获取的数据(以免载入不必要的数据,降低速度)
|
|
|
+ Set<String> fieldsToLoad = new HashSet<>();
|
|
|
+ fieldsToLoad.add(SearchConstants.PCB_GOODS_PCB_ID_FIELD);
|
|
|
+ fieldsToLoad.add(SearchConstants.PCB_GOODS_GO_ID_FIELD);
|
|
|
+ ScoreDoc[] scoreDocs = hits.scoreDocs;
|
|
|
+ for (ScoreDoc scoreDoc : scoreDocs) {
|
|
|
+ Document document = indexSearcher.doc(scoreDoc.doc, fieldsToLoad);
|
|
|
+ String pcbId = document.get(SearchConstants.PCB_GOODS_PCB_ID_FIELD);
|
|
|
+ pcbIds.add(StringUtils.isEmpty(pcbId) || pcbId.equals(ObjectToDocumentUtils.NULL_VALUE) ? null : Long.valueOf(pcbId));
|
|
|
+ String goId = document.get(SearchConstants.PCB_GOODS_GO_ID_FIELD);
|
|
|
+ goIds.add(StringUtils.isEmpty(goId) || goId.equals(ObjectToDocumentUtils.NULL_VALUE) ? null : Long.valueOf(goId));
|
|
|
+ }
|
|
|
+ map.put("pcbIds", pcbIds);
|
|
|
+ map.put("goodsIds", goIds);
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 递归查询 PCB 批次(如果没有结果,则降低精度,直至长度为 1)
|
|
|
+ *
|
|
|
+ * @param keyword 关键词
|
|
|
+ * @return 最后一次搜索的关键词
|
|
|
+ */
|
|
|
+ private String recursivelyGetPCBGoodsIds(String keyword) throws IOException {
|
|
|
+ IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.PCB_GOODS_TABLE_NAME);
|
|
|
+ try {
|
|
|
+ BooleanQuery booleanQuery = queryPCBGoods(keyword);
|
|
|
+ logger.info(booleanQuery.toString());
|
|
|
+ TotalHitCountCollector collector = new TotalHitCountCollector();
|
|
|
+ indexSearcher.search(booleanQuery, collector);
|
|
|
+ // 如果没有结果,则降低精度,直至 keyword 长度为 1
|
|
|
+ if (collector.getTotalHits() < 1 && !SearchUtils.isKeywordInvalid(keyword) && keyword.length() > 1) {
|
|
|
+ return recursivelyGetPCBGoodsIds(keyword.substring(0, keyword.length() - 1));
|
|
|
+ }
|
|
|
+ return keyword;
|
|
|
+ } finally {
|
|
|
+ SearchUtils.releaseIndexSearcher(indexSearcher);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置 PCB 批次过滤条件
|
|
|
+ *
|
|
|
+ * @param filters 指定的过滤条件
|
|
|
+ * @param query 原查询
|
|
|
+ */
|
|
|
+ private void setPCBGoodsFilter(Map<FilterField, Object> filters, BooleanQuery query) {
|
|
|
+ Object status;
|
|
|
+ // 筛选状态
|
|
|
+ if (!CollectionUtils.isEmpty(filters) && !StringUtils.isEmpty(filters.get(FilterField.GOODS_STATUS))) {
|
|
|
+ // 如果明确指定了状态,则直接过滤批次(结果中不包括没有批次的 PCB)
|
|
|
+ status = filters.get(FilterField.GOODS_STATUS);
|
|
|
+ filter(status, SearchConstants.PCB_GOODS_GO_STATUS_FIELD, query);
|
|
|
+ } else {
|
|
|
+ // 如果未明确指定状态,则使用默认状态分情况进行过滤(结果中包括没有批次的 PCB)
|
|
|
+ status = Arrays.asList(TradeGoods.VALID_STATUS);
|
|
|
+ // 批次 id 不为空时,对状态过滤
|
|
|
+ Query goNullQuery = SearchUtils.getNullQuery(SearchConstants.PCB_GOODS_GO_ID_FIELD);
|
|
|
+ BooleanQuery q1 = new BooleanQuery();
|
|
|
+ q1.add(goNullQuery, Occur.MUST_NOT);
|
|
|
+ filter(status, SearchConstants.PCB_GOODS_GO_STATUS_FIELD, q1);
|
|
|
+ // 或者批次 id 为空(此时是PCB)
|
|
|
+ BooleanQuery q2 = new BooleanQuery();
|
|
|
+ q2.add(SearchUtils.getNullQuery(SearchConstants.PCB_GOODS_PCB_ID_FIELD), Occur.MUST_NOT);
|
|
|
+ q2.add(goNullQuery, Occur.MUST);
|
|
|
+
|
|
|
+ BooleanQuery booleanQuery = new BooleanQuery();
|
|
|
+ booleanQuery.add(q1, Occur.SHOULD);
|
|
|
+ booleanQuery.add(q2, Occur.SHOULD);
|
|
|
+ query.add(booleanQuery, Occur.FILTER);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (CollectionUtils.isEmpty(filters)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 筛选类目
|
|
|
+ if (!StringUtils.isEmpty(filters.get(FilterField.GOODS_KINDID))) {
|
|
|
+ filter(filters.get(FilterField.GOODS_KINDID), SearchConstants.PCB_GOODS_KI_ID_FIELD, query);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 筛选品牌
|
|
|
+ if (!StringUtils.isEmpty(filters.get(FilterField.GOODS_BRANDID))) {
|
|
|
+ filter(filters.get(FilterField.GOODS_BRANDID), SearchConstants.PCB_GOODS_BR_ID_FIELD, query);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 筛选货币
|
|
|
+ if (!StringUtils.isEmpty(filters.get(FilterField.GOODS_CRNAME))) {
|
|
|
+ filter(filters.get(FilterField.GOODS_CRNAME), SearchConstants.PCB_GOODS_CRNAME_FIELD, query);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 价格筛选
|
|
|
+ Object minPriceRmb = filters.get(FilterField.GOODS_MINPRICERMB);
|
|
|
+ Object maxPriceRmb = filters.get(FilterField.GOODS_MAXPRICERMB);
|
|
|
+ Object minPriceUsd = filters.get(FilterField.GOODS_MINPRICEUSD);
|
|
|
+ Object maxPriceUsd = filters.get(FilterField.GOODS_MAXPRICEUSD);
|
|
|
+ // 筛选人民币价格
|
|
|
+ if (!StringUtils.isEmpty(minPriceRmb) || !StringUtils.isEmpty(maxPriceRmb)) {
|
|
|
+ Double minPrice = null;
|
|
|
+ Double maxPrice = null;
|
|
|
+ if (!StringUtils.isEmpty(minPriceRmb)) {
|
|
|
+ minPrice = Double.valueOf(minPriceRmb.toString());
|
|
|
+ }
|
|
|
+ if (!StringUtils.isEmpty(maxPriceRmb)) {
|
|
|
+ maxPrice = Double.valueOf(maxPriceRmb.toString());
|
|
|
+ }
|
|
|
+ query.add(NumericRangeQuery.newDoubleRange(SearchConstants.PCB_GOODS_GO_MINPRICERMB_FIELD,
|
|
|
+ minPrice, maxPrice, true, true), BooleanClause.Occur.FILTER);
|
|
|
+ }
|
|
|
+ // 筛选美元价格
|
|
|
+ if (!StringUtils.isEmpty(minPriceUsd) || !StringUtils.isEmpty(maxPriceUsd)) {
|
|
|
+ Double minPrice = null;
|
|
|
+ Double maxPrice = null;
|
|
|
+ if (!StringUtils.isEmpty(minPriceUsd)) {
|
|
|
+ minPrice = Double.valueOf(minPriceUsd.toString());
|
|
|
+ }
|
|
|
+ if (!StringUtils.isEmpty(maxPriceUsd)) {
|
|
|
+ maxPrice = Double.valueOf(maxPriceUsd.toString());
|
|
|
+ }
|
|
|
+ query.add(NumericRangeQuery.newDoubleRange(SearchConstants.PCB_GOODS_GO_MINPRICEUSD_FIELD,
|
|
|
+ minPrice, maxPrice, true, true), BooleanClause.Occur.FILTER);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * PCB 批次排序规则
|
|
|
+ */
|
|
|
+ private Sort sortPCBGoods(String keyword, com.uas.search.constant.model.Sort sort) {
|
|
|
+ List<SortField> sortFieldList = new ArrayList<>();
|
|
|
+ sortFieldList.add(SortField.FIELD_SCORE);
|
|
|
+ if (sort != null) {
|
|
|
+ com.uas.search.constant.model.Sort.Field field = sort.getField();
|
|
|
+ if (field == null) {
|
|
|
+ throw new IllegalArgumentException("排序字段不可为空:" + sort);
|
|
|
+ }
|
|
|
+ boolean reverse = sort.isReverse();
|
|
|
+ if (field == RESERVE) {
|
|
|
+ // 库存
|
|
|
+ sortFieldList.addAll(Arrays.asList(
|
|
|
+ // 降序时,默认值为最小值;升序时,默认值为最大值
|
|
|
+ sortField(SearchConstants.PCB_GOODS_GO_RESERVE_FIELD, Type.DOUBLE, reverse, reverse ? Double.MIN_VALUE : Double.MAX_VALUE),
|
|
|
+ sortField(SearchConstants.PCB_GOODS_GO_MINPRICERMB_FIELD, Type.DOUBLE, false, Double.MAX_VALUE),
|
|
|
+ sortField(SearchConstants.PCB_GOODS_GO_MINPRICEUSD_FIELD, Type.DOUBLE, false, Double.MAX_VALUE)
|
|
|
+ ));
|
|
|
+ } else if (field == PRICE) {
|
|
|
+ // 价格
|
|
|
+ sortFieldList.addAll(Arrays.asList(
|
|
|
+ // 降序时,默认值为最小值;升序时,默认值为最大值
|
|
|
+ sortField(SearchConstants.PCB_GOODS_GO_MINPRICERMB_FIELD, Type.DOUBLE, reverse, reverse ? Double.MIN_VALUE : Double.MAX_VALUE),
|
|
|
+ sortField(SearchConstants.PCB_GOODS_GO_MINPRICEUSD_FIELD, Type.DOUBLE, reverse, reverse ? Double.MIN_VALUE : Double.MAX_VALUE),
|
|
|
+ sortField(SearchConstants.PCB_GOODS_GO_RESERVE_FIELD, Type.DOUBLE, true, Double.MIN_VALUE)
|
|
|
+ ));
|
|
|
+ } else {
|
|
|
+ throw new IllegalArgumentException("不支持该排序方式:" + field);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 默认(综合排序)
|
|
|
+ sortFieldList.addAll(Arrays.asList(
|
|
|
+ sortField(SearchConstants.PCB_GOODS_GO_MINPRICERMB_FIELD, Type.DOUBLE, false, Double.MAX_VALUE),
|
|
|
+ sortField(SearchConstants.PCB_GOODS_GO_MINPRICEUSD_FIELD, Type.DOUBLE, false, Double.MAX_VALUE),
|
|
|
+ sortField(SearchConstants.PCB_GOODS_GO_RESERVE_FIELD, Type.DOUBLE, true, Double.MIN_VALUE),
|
|
|
+ sortField(SearchConstants.PCB_GOODS_GO_MINDELIVERY_FIELD, Type.LONG, false, Long.MAX_VALUE)
|
|
|
+ ));
|
|
|
+ }
|
|
|
+ sortFieldList.addAll(Arrays.asList(
|
|
|
+ // 如果仍然无法得到正确结果,就根据按照型号等顺序严格排列
|
|
|
+ new SortField(SearchConstants.PCB_GOODS_PR_PCMPCODE_FIELD, new StringFieldComparatorSource(keyword, false)),
|
|
|
+ new SortField(SearchConstants.PCB_GOODS_BR_NAME_EN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(keyword, false)),
|
|
|
+ new SortField(SearchConstants.PCB_GOODS_BR_NAME_CN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(keyword, false)),
|
|
|
+ new SortField(SearchConstants.PCB_GOODS_KI_NAME_CN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(keyword, false)),
|
|
|
+ sortField(SearchConstants.PCB_GOODS_GO_UPDATE_DATE_FIELD, Type.LONG, true, Long.MIN_VALUE)
|
|
|
+ ));
|
|
|
+ SortField[] sortFields = new SortField[sortFieldList.size()];
|
|
|
+ sortFieldList.toArray(sortFields);
|
|
|
+ return new Sort(sortFields);
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
public List<Map<String, Object>> collectBySearchGoods(String keyword, CollectField collectedField,
|
|
|
Map<FilterField, Object> filters) throws IOException {
|
|
|
@@ -1086,7 +1310,6 @@ public class SearchServiceImpl implements SearchService {
|
|
|
result = collectBySearchGoods(keyword, null, true, collectedField, filters);
|
|
|
}
|
|
|
return result;
|
|
|
-
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -1202,6 +1425,82 @@ public class SearchServiceImpl implements SearchService {
|
|
|
return booleanQuery;
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ public List<Map<String, Object>> collectBySearchPCBGoods(String keyword, CollectField collectedField,
|
|
|
+ Map<FilterField, Object> filters) throws IOException {
|
|
|
+ keyword = recursivelyGetPCBGoodsIds(keyword);
|
|
|
+ if (collectedField == null) {
|
|
|
+ throw new IllegalArgumentException("参数为空:collectedField");
|
|
|
+ }
|
|
|
+ IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.PCB_GOODS_TABLE_NAME);
|
|
|
+
|
|
|
+ List<Map<String, Object>> result = new ArrayList<>();
|
|
|
+ try {
|
|
|
+ BooleanQuery booleanQuery = queryPCBGoods(keyword);
|
|
|
+ setPCBGoodsFilter(filters, booleanQuery);
|
|
|
+ logger.info(booleanQuery.toString());
|
|
|
+
|
|
|
+ // 统计
|
|
|
+ String uniqueField;
|
|
|
+ Set<String> fieldsToLoad = new HashSet<>();
|
|
|
+ switch (collectedField) {
|
|
|
+ case GOODS_KIND:
|
|
|
+ uniqueField = SearchConstants.PCB_GOODS_KI_ID_FIELD;
|
|
|
+ fieldsToLoad.add(SearchConstants.PCB_GOODS_KI_ID_FIELD);
|
|
|
+ fieldsToLoad.add(SearchConstants.PCB_GOODS_KI_NAME_CN_FIELD);
|
|
|
+ break;
|
|
|
+ case GOODS_BRAND:
|
|
|
+ uniqueField = SearchConstants.PCB_GOODS_BR_ID_FIELD;
|
|
|
+ fieldsToLoad.add(SearchConstants.PCB_GOODS_BR_ID_FIELD);
|
|
|
+ fieldsToLoad.add(SearchConstants.PCB_GOODS_BR_UUID_FIELD);
|
|
|
+ fieldsToLoad.add(SearchConstants.PCB_GOODS_BR_NAME_CN_FIELD);
|
|
|
+ fieldsToLoad.add(SearchConstants.PCB_GOODS_BR_NAME_EN_FIELD);
|
|
|
+ break;
|
|
|
+ case GOODS_CRNAME:
|
|
|
+ uniqueField = SearchConstants.PCB_GOODS_CRNAME_FIELD;
|
|
|
+ fieldsToLoad.add(SearchConstants.PCB_GOODS_CRNAME_FIELD);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ throw new IllegalArgumentException("不支持该统计字段:" + collectedField);
|
|
|
+ }
|
|
|
+ GoodsGroupCollector collector = new GoodsGroupCollector(uniqueField, fieldsToLoad);
|
|
|
+ indexSearcher.search(booleanQuery, collector);
|
|
|
+ result = collector.getValues();
|
|
|
+ } catch (IOException e) {
|
|
|
+ logger.error("", e);
|
|
|
+ } finally {
|
|
|
+ SearchUtils.releaseIndexSearcher(indexSearcher);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取查询 PCB 批次的query
|
|
|
+ *
|
|
|
+ * @param keyword
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private BooleanQuery queryPCBGoods(String keyword) {
|
|
|
+ BooleanQuery booleanQuery = new BooleanQuery();
|
|
|
+ if (!SearchUtils.isKeywordInvalid(keyword)) {
|
|
|
+ booleanQuery.add(setPCBGoodsBoost(keyword), BooleanClause.Occur.MUST);
|
|
|
+ }
|
|
|
+ return booleanQuery;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 搜索 PCB,设置boost
|
|
|
+ */
|
|
|
+ private BooleanQuery setPCBGoodsBoost(String keyword) {
|
|
|
+ BooleanQuery booleanQuery = new BooleanQuery();
|
|
|
+ // 前缀搜索(字段并未分词,进行分词搜索时,会有边界问题,如搜索 'BC807-40,215')
|
|
|
+ booleanQuery.add(new PrefixQuery(new Term(SearchConstants.PCB_GOODS_PR_PCMPCODE_FIELD, keyword.toLowerCase())), BooleanClause.Occur.SHOULD);
|
|
|
+ // PCB
|
|
|
+ booleanQuery.add(createQuery(SearchConstants.PCB_GOODS_PR_PCMPCODE_FIELD, keyword, 100), Occur.SHOULD);
|
|
|
+ return booleanQuery;
|
|
|
+ }
|
|
|
+
|
|
|
private BooleanQuery createQuery(String field, String keyword, float boost){
|
|
|
return createQuery(field, keyword, false, boost);
|
|
|
}
|
|
|
@@ -1274,6 +1573,12 @@ public class SearchServiceImpl implements SearchService {
|
|
|
SearchUtils.getDocumentById(SearchConstants.GOODS_TABLE_NAME, SearchConstants.GOODS_GO_ID_FIELD, id));
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ public PCBGoods getPCBGoods(String id) throws IOException {
|
|
|
+ return DocumentToObjectUtils.toPCBGoods(
|
|
|
+ SearchUtils.getDocumentById(SearchConstants.PCB_GOODS_TABLE_NAME, SearchConstants.PCB_GOODS_GO_ID_FIELD, id));
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
public SPage<Object> getObjects(String tableName, String keyword, String field, Boolean tokenized, @NotEmpty("page") Integer page, @NotEmpty("size") Integer size) throws IOException {
|
|
|
if (keyword == null) {
|