SearchServiceImpl.java 96 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261
  1. package com.uas.search.service.impl;
  2. import static com.uas.search.constant.model.Sort.Field.PRICE;
  3. import static com.uas.search.constant.model.Sort.Field.RESERVE;
  4. import static com.uas.search.util.SearchUtils.getDocuments;
  5. import static com.uas.search.util.SearchUtils.releaseIndexSearcher;
  6. import com.alibaba.fastjson.JSONObject;
  7. import com.uas.search.annotation.NotEmpty;
  8. import com.uas.search.constant.SearchConstants;
  9. import com.uas.search.constant.model.CollectField;
  10. import com.uas.search.constant.model.PageParams;
  11. import com.uas.search.constant.model.PageParams.FilterField;
  12. import com.uas.search.constant.model.SPage;
  13. import com.uas.search.grouping.DistinctGroupCollector;
  14. import com.uas.search.grouping.GoodsGroupCollector;
  15. import com.uas.search.model.Brand;
  16. import com.uas.search.model.Component;
  17. import com.uas.search.model.Goods;
  18. import com.uas.search.model.Kind;
  19. import com.uas.search.model.PCBGoods;
  20. import com.uas.search.model.TradeGoods;
  21. import com.uas.search.model.V_Products;
  22. import com.uas.search.service.SearchService;
  23. import com.uas.search.sort.StringFieldComparatorSource;
  24. import com.uas.search.util.CollectionUtils;
  25. import com.uas.search.util.DocumentToObjectUtils;
  26. import com.uas.search.util.ObjectToDocumentUtils;
  27. import com.uas.search.util.SearchUtils;
  28. import com.uas.search.util.StringUtils;
  29. import java.io.IOException;
  30. import java.net.URLDecoder;
  31. import java.util.ArrayList;
  32. import java.util.Arrays;
  33. import java.util.Collections;
  34. import java.util.Comparator;
  35. import java.util.HashMap;
  36. import java.util.HashSet;
  37. import java.util.List;
  38. import java.util.Map;
  39. import java.util.Set;
  40. import org.apache.lucene.document.Document;
  41. import org.apache.lucene.index.Term;
  42. import org.apache.lucene.sandbox.queries.DuplicateFilter;
  43. import org.apache.lucene.search.BooleanClause;
  44. import org.apache.lucene.search.BooleanClause.Occur;
  45. import org.apache.lucene.search.BooleanQuery;
  46. import org.apache.lucene.search.IndexSearcher;
  47. import org.apache.lucene.search.NumericRangeQuery;
  48. import org.apache.lucene.search.PrefixQuery;
  49. import org.apache.lucene.search.Query;
  50. import org.apache.lucene.search.ScoreDoc;
  51. import org.apache.lucene.search.Sort;
  52. import org.apache.lucene.search.SortField;
  53. import org.apache.lucene.search.SortField.Type;
  54. import org.apache.lucene.search.TermQuery;
  55. import org.apache.lucene.search.TopDocs;
  56. import org.apache.lucene.search.TotalHitCountCollector;
  57. import org.slf4j.Logger;
  58. import org.slf4j.LoggerFactory;
  59. import org.springframework.stereotype.Service;
  60. /**
  61. * 搜索索引
  62. *
  63. * @author sunyj
  64. * @since 2016年8月5日 下午2:21:26
  65. */
  66. @Service
  67. public class SearchServiceImpl implements SearchService {
  68. /**
  69. * 获取联想词时返回的最大数目
  70. */
  71. private static final int SIMILAR_NUM = 20;
  72. /**
  73. * 获取联想词时返回的最大数目
  74. */
  75. private static final int SIMILAR_NUM_EIGHT = 8;
  76. /**
  77. * 所有物料
  78. */
  79. private static final String ALL_PRODUCTS = "all";
  80. /**
  81. * 标准物料
  82. */
  83. private static final String STANDARD_PRODUCTS = "standard";
  84. /**
  85. * 非标物料
  86. */
  87. private static final String NONSTANDARD_PRODUCTS = "nStandard";
  88. /**
  89. * 默认的页码
  90. */
  91. private static final int PAGE_INDEX = 1;
  92. /**
  93. * 默认每页的大小
  94. */
  95. private static final int PAGE_SIZE = 20;
  96. /**
  97. * 品牌、类目分词可能重复,品牌每页取100个
  98. */
  99. private static final int DUPLICATE_PAGE_SIZE = 100;
  100. private static Logger logger = LoggerFactory.getLogger(SearchServiceImpl.class);
  101. /**
  102. * 查询所有物料
  103. *
  104. * @param keyword 关键词
  105. * @param page 页码
  106. * @param size 尺寸
  107. * @param enUU 企业UU
  108. * @param type all 全部 standard 标准 nStandard 非标
  109. * @return idPage
  110. * @throws IOException 输入异常
  111. */
  112. @Override
  113. public SPage<Long> getProductIds(Long enUU, String keyword, Integer page, Integer size, String type) throws IOException {
  114. List<Long> ids = new ArrayList<>();
  115. SPage<Document> documents = getProductDocuments(enUU, keyword, page, size, type);
  116. SPage<Long> sPage = new SPage<>(documents.getTotalPage(), documents.getTotalElement(), documents.getPage(),
  117. documents.getSize(), documents.isFirst(), documents.isLast());
  118. for (Document document : documents.getContent()) {
  119. ids.add(Long.parseLong(document.get(SearchConstants.PRODUCT_PRIVATE_ID_FIELD)));
  120. }
  121. sPage.setContent(ids);
  122. return sPage;
  123. }
  124. private SPage<Document> getProductDocuments(Long enUU, String keyword, Integer page, Integer size, String type) throws IOException {
  125. // if (SearchUtils.isKeywordInvalid(keyword)) {
  126. // throw new IllegalArgumentException("搜索关键词无效:" + keyword);
  127. // }
  128. BooleanQuery q1 = new BooleanQuery();
  129. if (null != enUU) {
  130. q1.add(new TermQuery(new Term(SearchConstants.PRODUCT_PRIVATE_ENUU_FIELD, String.valueOf(enUU))), BooleanClause.Occur.MUST);
  131. }
  132. if (STANDARD_PRODUCTS.equals(type)) {
  133. q1.add(new TermQuery(new Term(SearchConstants.PRODUCT_PRIVATE_STANDARD_FIELD, String.valueOf(1))), BooleanClause.Occur.MUST);
  134. } else if (NONSTANDARD_PRODUCTS.equals(type)) {
  135. q1.add(new TermQuery(new Term(SearchConstants.PRODUCT_PRIVATE_STANDARD_FIELD, String.valueOf(0))), BooleanClause.Occur.MUST);
  136. }
  137. q1.add(new TermQuery(new Term(SearchConstants.PRODUCT_PRIVATE_B2CENABLED_FIELD, String.valueOf(1))), BooleanClause.Occur.MUST);
  138. BooleanQuery booleanQuery = new BooleanQuery();
  139. booleanQuery.add(q1, BooleanClause.Occur.MUST);
  140. if (!StringUtils.isEmpty(keyword)) {
  141. BooleanQuery q2 = new BooleanQuery();
  142. q2.add(createQuery(SearchConstants.PRODUCT_PRIVATE_KIND_FIELD, keyword, true,1), BooleanClause.Occur.SHOULD);
  143. q2.add(createQuery(SearchConstants.PRODUCT_PRIVATE_PBRANDEN_FIELD, keyword, true,1), BooleanClause.Occur.SHOULD);
  144. q2.add(createQuery(SearchConstants.PRODUCT_PRIVATE_PCMPCODE_FIELD, keyword, true,1), BooleanClause.Occur.SHOULD);
  145. booleanQuery.add(q2, BooleanClause.Occur.MUST);
  146. }
  147. logger.info(booleanQuery.toString());
  148. return getDocuments(SearchConstants.PRODUCTS_PRIVATE_TABLE_NAME, booleanQuery, new Sort(sortProduct(keyword)), page, size);
  149. }
  150. /**
  151. * @return 物料排序规则
  152. */
  153. private SortField sortProduct(String keyword) {
  154. // id
  155. return new SortField(SearchConstants.PRODUCT_PRIVATE_ID_FIELD, Type.LONG, true);
  156. }
  157. @Override
  158. public SPage<Long> getKindIds(String keyword, Integer page, Integer size) throws IOException {
  159. List<Long> ids = new ArrayList<>();
  160. SPage<Document> documents = getKindDocuments(keyword, page, size);;
  161. SPage<Long> sPage = new SPage<>(documents.getTotalPage(), documents.getTotalElement(), documents.getPage(),
  162. documents.getSize(), documents.isFirst(), documents.isLast());
  163. for (Document document : documents.getContent()) {
  164. ids.add(Long.parseLong(document.get(SearchConstants.KIND_ID_FIELD)));
  165. }
  166. sPage.setContent(ids);
  167. return sPage;
  168. }
  169. @Override
  170. public SPage<Map<String, Object>> getKinds(String keyword, Integer page, Integer size) throws IOException {
  171. List<Map<String, Object>> kinds = new ArrayList<>();
  172. SPage<Document> documents = getKindDocuments(keyword, page, size);
  173. SPage<Map<String, Object>> sPage = new SPage<>(documents.getTotalPage(),
  174. documents.getTotalElement(), documents.getPage(), documents.getSize(), documents.isFirst(),
  175. documents.isLast());
  176. for (Document document : documents.getContent()) {
  177. Map<String, Object> kind = new HashMap<>();
  178. kind.put("id", Long.parseLong(document.get(SearchConstants.KIND_ID_FIELD)));
  179. kind.put("nameCn", document.get(SearchConstants.KIND_NAMECN_FIELD));
  180. kinds.add(kind);
  181. }
  182. sPage.setContent(kinds);
  183. return sPage;
  184. }
  185. private SPage<Document> getKindDocuments(String keyword, Integer page, Integer size) throws IOException {
  186. if (SearchUtils.isKeywordInvalid(keyword)) {
  187. throw new IllegalArgumentException("搜索关键词无效:" + keyword);
  188. }
  189. BooleanQuery booleanQuery = SearchUtils.getBooleanQuery(SearchConstants.KIND_NAMECN_FIELD, keyword);
  190. logger.info(booleanQuery.toString());
  191. return getDocuments(SearchConstants.KIND_TABLE_NAME, booleanQuery, new Sort(sortKind(keyword)), page, size);
  192. }
  193. /**
  194. * @return 类目排序规则
  195. */
  196. private SortField[] sortKind(String keyword) {
  197. // 分数 > 访问量 > 搜索次数
  198. return new SortField[]{
  199. sortField(SearchConstants.KIND_VISIT_COUNT_FIELD, Type.LONG, true, Long.MIN_VALUE),
  200. sortField(SearchConstants.KIND_SEARCH_COUNT_FIELD, Type.LONG, true, Long.MIN_VALUE),
  201. new SortField(SearchConstants.KIND_NAMECN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(keyword))
  202. };
  203. }
  204. /**
  205. * 构造 SortField
  206. *
  207. * @param field Name of field to sort by. Can be <code>null</code> if
  208. * <code>type</code> is SCORE or DOC.
  209. * @param type Type of values in the terms.
  210. * @param reverse True if natural order should be reversed.
  211. * @param missingValue Used for 'sortMissingFirst/Last'
  212. * @return SortField
  213. */
  214. private SortField sortField(String field, Type type, boolean reverse, Object missingValue) {
  215. SortField sortField = new SortField(field, type, reverse);
  216. sortField.setMissingValue(missingValue);
  217. return sortField;
  218. }
  219. @Override
  220. public SPage<Long> getBrandIds(String keyword, Integer page, Integer size) throws IOException {
  221. List<Long> ids = new ArrayList<>();
  222. SPage<Document> documents = getBrandDocuments(keyword, page, size);
  223. SPage<Long> sPage = new SPage<>(documents.getTotalPage(), documents.getTotalElement(), documents.getPage(),
  224. documents.getSize(), documents.isFirst(), documents.isLast());
  225. for (Document document : documents.getContent()) {
  226. ids.add(Long.parseLong(document.get(SearchConstants.BRAND_ID_FIELD)));
  227. }
  228. sPage.setContent(ids);
  229. return sPage;
  230. }
  231. @Override
  232. public SPage<Map<String, Object>> getBrands(String keyword, Integer page, Integer size) throws IOException {
  233. List<Map<String, Object>> brands = new ArrayList<>();
  234. SPage<Document> documents = getBrandDocuments(keyword, page, size);
  235. SPage<Map<String, Object>> sPage = new SPage<Map<String, Object>>(documents.getTotalPage(),
  236. documents.getTotalElement(), documents.getPage(), documents.getSize(), documents.isFirst(),
  237. documents.isLast());
  238. for (Document document : documents.getContent()) {
  239. Map<String, Object> brand = new HashMap<String, Object>();
  240. brand.put("id", Long.parseLong(document.get(SearchConstants.BRAND_ID_FIELD)));
  241. brand.put("uuid", document.get(SearchConstants.BRAND_UUID_FIELD));
  242. brand.put("nameCn", document.get(SearchConstants.BRAND_NAMECN_FIELD));
  243. brand.put("nameEn", document.get(SearchConstants.BRAND_NAMEEN_FIELD));
  244. brands.add(brand);
  245. }
  246. sPage.setContent(brands);
  247. return sPage;
  248. }
  249. @Override
  250. public SPage<Map<String, Object>> getBrandsAndSellers(String keyword, Integer page, Integer size)
  251. throws IOException {
  252. List<Map<String, Object>> content = new ArrayList<>();
  253. Map<String, Object> map = new HashMap<>();
  254. List<Long> brandIds = new ArrayList<>();
  255. SPage<Object> stock = null;
  256. SPage<Object> futures = null;
  257. // 精准匹配
  258. SPage<Long> documents = getBrandIds(keyword, page, size, false);
  259. SPage<Map<String, Object>> sPage = new SPage<>(1, 1, 1, documents.getSize(), true, true);
  260. // 精准匹配成功
  261. if (!CollectionUtils.isEmpty(documents.getContent())) {
  262. Map<String, List<String>> keywordFeildsMap = new HashMap<>();
  263. List<String> goodsFeilds = new ArrayList<>();
  264. goodsFeilds.add(SearchConstants.GOODS_PR_PBRAND_EN_UNTOKENIZED_FIELD);
  265. goodsFeilds.add(SearchConstants.GOODS_PR_PBRAND_CN_UNTOKENIZED_FIELD);
  266. List<String> productsFeilds = new ArrayList<>();
  267. productsFeilds.add(SearchConstants.PRODUCT_PRIVATE_PBRAND_CN_FIELD);
  268. productsFeilds.add(SearchConstants.PRODUCT_PRIVATE_PBRAND_EN_FIELD);
  269. keywordFeildsMap.put("goods", goodsFeilds);
  270. keywordFeildsMap.put("products", productsFeilds);
  271. brandIds.add(documents.getContent().get(0));
  272. // 获取卖家信息
  273. Map<String, SPage<Object>> sellers = querySellers(keyword, keywordFeildsMap, page, size, false, SearchConstants.BRAND);
  274. stock = sellers.get("stock");
  275. futures = sellers.get("futures");
  276. map.put("stock", stock);
  277. map.put("futures", futures);
  278. // 模糊匹配
  279. } else {
  280. SPage<Long> brandPage = getBrandIds(keyword, page, size, true);
  281. brandIds = brandPage.getContent();
  282. sPage = new SPage<>(brandPage.getTotalPage(), brandPage.getTotalElement(), brandPage.getPage(), brandPage.getSize(), brandPage.isFirst(), brandPage.isLast());
  283. }
  284. map.put("brandIds", brandIds);
  285. content.add(map);
  286. sPage.setContent(content);
  287. return sPage;
  288. }
  289. @Override
  290. public Map<String, Object> getSellersWithKind(String keyword, Integer page, Integer size)
  291. throws IOException {
  292. Map<String, Object> map = new HashMap<>();
  293. SPage<Object> stock = null;
  294. SPage<Object> futures = null;
  295. Map<String, List<String>> keywordFeildsMap = new HashMap<>();
  296. List<String> goodsFeilds = new ArrayList<>();
  297. goodsFeilds.add(SearchConstants.GOODS_PR_KIND_FIELD);
  298. List<String> productsFeilds = new ArrayList<>();
  299. productsFeilds.add(SearchConstants.PRODUCT_PRIVATE_KIND_FIELD);
  300. keywordFeildsMap.put("goods", goodsFeilds);
  301. keywordFeildsMap.put("products", productsFeilds);
  302. Map<String, SPage<Object>> sellers = querySellers(keyword, keywordFeildsMap, page, size, true, SearchConstants.KIND);
  303. stock = sellers.get("stock");
  304. futures = sellers.get("futures");
  305. map.put("stock", stock);
  306. map.put("futures", futures);
  307. return map;
  308. }
  309. /**
  310. * 获取品牌id
  311. * @param keyword
  312. * @param page
  313. * @param size
  314. * @return
  315. */
  316. private SPage<Long> getBrandIds(String keyword, Integer page, Integer size, Boolean tokenized) throws IOException{
  317. List<Long> brandIds = new ArrayList<>();
  318. if (SearchUtils.isKeywordInvalid(keyword)) {
  319. throw new IllegalArgumentException("搜索关键词无效:" + keyword);
  320. }
  321. BooleanQuery booleanQuery = new BooleanQuery();
  322. if (tokenized) {
  323. booleanQuery.add(SearchUtils.getBooleanQuery(SearchConstants.BRAND_NAMECN_FIELD, keyword),
  324. BooleanClause.Occur.SHOULD);
  325. booleanQuery.add(SearchUtils.getBooleanQuery(SearchConstants.BRAND_NAMEEN_FIELD, keyword),
  326. BooleanClause.Occur.SHOULD);
  327. } else {
  328. booleanQuery.add(new TermQuery(new Term(SearchConstants.BRAND_NAMECN_UNTOKENIZED_FIELD, keyword.toLowerCase())), Occur.SHOULD);
  329. booleanQuery.add(new TermQuery(new Term(SearchConstants.BRAND_NAMEEN_UNTOKENIZED_FIELD, keyword.toLowerCase())), Occur.SHOULD);
  330. }
  331. logger.info(booleanQuery.toString());
  332. SPage<Document> documents = getDocuments(SearchConstants.BRAND_TABLE_NAME, booleanQuery, new Sort(sortBrand(keyword)), page,
  333. size);
  334. SPage<Long> sPage = new SPage<Long>(documents.getTotalPage(),
  335. documents.getTotalElement(), documents.getPage(), documents.getSize(), documents.isFirst(),
  336. documents.isLast());
  337. for (Document document : documents.getContent()) {
  338. brandIds.add(Long.parseLong(document.get(SearchConstants.BRAND_ID_FIELD)));
  339. }
  340. sPage.setContent(brandIds);
  341. return sPage;
  342. }
  343. /**
  344. * 获取品牌Document
  345. * @param keyword 关键词
  346. * @param page 页码
  347. * @param size 页数
  348. * @return
  349. * @throws IOException
  350. */
  351. private SPage<Document> getBrandDocuments(String keyword, Integer page, Integer size) throws IOException {
  352. if (SearchUtils.isKeywordInvalid(keyword)) {
  353. throw new IllegalArgumentException("搜索关键词无效:" + keyword);
  354. }
  355. BooleanQuery booleanQuery = new BooleanQuery();
  356. booleanQuery.add(SearchUtils.getBooleanQuery(SearchConstants.BRAND_NAMECN_FIELD, keyword),
  357. BooleanClause.Occur.SHOULD);
  358. booleanQuery.add(SearchUtils.getBooleanQuery(SearchConstants.BRAND_NAMEEN_FIELD, keyword),
  359. BooleanClause.Occur.SHOULD);
  360. logger.info(booleanQuery.toString());
  361. return getDocuments(SearchConstants.BRAND_TABLE_NAME, booleanQuery, new Sort(sortBrand(keyword)), page,
  362. size);
  363. }
  364. /**
  365. * @return 品牌排序规则
  366. */
  367. private SortField[] sortBrand(String keyword) {
  368. // 自定义排序 > 权重 > 访问量 > 搜索次数 > 分数
  369. // 分数排序放在最后,是因为有的中英文名称相同,分数翻倍,但实际匹配度并不高
  370. return new SortField[]{
  371. sortField(SearchConstants.BRAND_WEIGHT_FIELD, Type.DOUBLE, true, Double.MIN_VALUE),
  372. sortField(SearchConstants.BRAND_VISIT_COUNT_FIELD, Type.LONG, true, Long.MIN_VALUE),
  373. sortField(SearchConstants.BRAND_SEARCH_COUNT_FIELD, Type.LONG, true, Long.MIN_VALUE),
  374. new SortField(SearchConstants.BRAND_NAMEEN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(keyword, true)),
  375. new SortField(SearchConstants.BRAND_NAMECN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(keyword))
  376. };
  377. }
  378. @Override
  379. public Map<String, Object> getComponentIds(String keyword, PageParams pageParams) {
  380. Map<String, Object> searchComponentIds = getComponentIds(keyword, pageParams, null, null);
  381. return searchComponentIds;
  382. // TODO 对品牌、类目甚至拼音混合搜索(待完善)
  383. // int total = (int) searchComponentIds.get("total");
  384. // if (total != 0) {
  385. // return searchComponentIds;
  386. // }
  387. // List<Long> kindIds = getKindIds(keyword, Occur.SHOULD);
  388. // List<Long> brandIds = getBrandIds(keyword, Occur.SHOULD);
  389. // return getComponentIds(null, pageParams, kindIds, brandIds);
  390. }
  391. /**
  392. * 根据关键词搜索产品
  393. *
  394. * @param keyword
  395. * @param pageParams
  396. * @param kindIds
  397. * @param brandIds
  398. * @return
  399. */
  400. private Map<String, Object> getComponentIds(String keyword, PageParams pageParams, List<Long> kindIds,
  401. List<Long> brandIds) {
  402. // 因为器件、属性值的数据量远比类目、品牌大得多,而且器件搜索可能还需进行分页,
  403. // 所以涉及器件、属性值的搜索,大都不能像类目和品牌一样直接利用SearchUtils.getDocuments方法
  404. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.COMPONENT_TABLE_NAME);
  405. if (pageParams == null) {
  406. pageParams = new PageParams();
  407. }
  408. if (pageParams.getPage() <= 0)
  409. pageParams.setPage(1);
  410. if (pageParams.getSize() <= 0)
  411. pageParams.setSize(20);
  412. Map<String, Object> map = new HashMap<>();
  413. List<Long> ids = new ArrayList<>();
  414. try {
  415. BooleanQuery booleanQuery = new BooleanQuery();
  416. if (!SearchUtils.isKeywordInvalid(keyword)) {
  417. booleanQuery.add(setBoost(keyword), BooleanClause.Occur.MUST);
  418. }
  419. Map<FilterField, Object> filters = pageParams.getFilters();
  420. if (!CollectionUtils.isEmpty(filters)) {
  421. // 筛选类目
  422. if (!StringUtils.isEmpty(filters.get(FilterField.COMPONENT_KINDID))) {
  423. String kindId = filters.get(FilterField.COMPONENT_KINDID).toString();
  424. TermQuery kindQuery = new TermQuery(new Term(SearchConstants.COMPONENT_KI_ID_FIELD, kindId));
  425. booleanQuery.add(kindQuery, BooleanClause.Occur.MUST);
  426. }
  427. // 筛选品牌
  428. if (!StringUtils.isEmpty(filters.get(FilterField.COMPONENT_BRANDID))) {
  429. String brandId = filters.get(FilterField.COMPONENT_BRANDID).toString();
  430. TermQuery brandQuery = new TermQuery(new Term(SearchConstants.COMPONENT_BR_ID_FIELD, brandId));
  431. booleanQuery.add(brandQuery, BooleanClause.Occur.MUST);
  432. }
  433. // 库存不为0
  434. if (!StringUtils.isEmpty(filters.get(FilterField.COMPONENT_HAS_RESERVE))) {
  435. Boolean isReserveNotEmpty = (Boolean) filters.get(FilterField.COMPONENT_HAS_RESERVE);
  436. if (isReserveNotEmpty) {
  437. booleanQuery.add(NumericRangeQuery.newDoubleRange(SearchConstants.COMPONENT_RESERVE_FIELD, 0.0,
  438. Double.MAX_VALUE, false, true), BooleanClause.Occur.MUST);
  439. }
  440. }
  441. // 现货、呆滞库存、样品数量不为0,取或的关系
  442. if (!StringUtils.isEmpty(filters.get(FilterField.COMPONENT_HAS_SAMPLE))
  443. || !StringUtils.isEmpty(filters.get(FilterField.COMPONENT_HAS_ORIGINAL))
  444. || !StringUtils.isEmpty(filters.get(FilterField.COMPONENT_HAS_INACTION_STOCK))) {
  445. BooleanQuery booleanQuery2 = new BooleanQuery();
  446. if (!StringUtils.isEmpty(filters.get(FilterField.COMPONENT_HAS_SAMPLE))) {
  447. booleanQuery2.add(NumericRangeQuery.newDoubleRange(SearchConstants.COMPONENT_SAMPLE_QTY_FIELD,
  448. 0.0, Double.MAX_VALUE, false, true), BooleanClause.Occur.SHOULD);
  449. }
  450. if (!StringUtils.isEmpty(filters.get(FilterField.COMPONENT_HAS_ORIGINAL))) {
  451. booleanQuery2.add(NumericRangeQuery.newDoubleRange(SearchConstants.COMPONENT_ORIGINAL_QTY_FIELD,
  452. 0.0, Double.MAX_VALUE, false, true), BooleanClause.Occur.SHOULD);
  453. }
  454. if (!StringUtils.isEmpty(filters.get(FilterField.COMPONENT_HAS_INACTION_STOCK))) {
  455. booleanQuery2.add(
  456. NumericRangeQuery.newDoubleRange(SearchConstants.COMPONENT_INACTION_STOCK_QTY_FIELD,
  457. 0.0, Double.MAX_VALUE, false, true),
  458. BooleanClause.Occur.SHOULD);
  459. }
  460. booleanQuery.add(booleanQuery2, Occur.MUST);
  461. }
  462. // 属性过滤
  463. if (!StringUtils.isEmpty(filters.get(FilterField.COMPONENT_PROPERTIES))) {
  464. JSONObject proJSON = JSONObject
  465. .parseObject(String.valueOf(filters.get(FilterField.COMPONENT_PROPERTIES)));
  466. for (String key : proJSON.keySet()) {
  467. String value = String.valueOf(proJSON.get(key));
  468. if (!StringUtils.isEmpty(value)) {
  469. if (!key.startsWith(SearchConstants.COMPONENT_PROPERTY_PREFIX)) {
  470. key = SearchConstants.COMPONENT_PROPERTY_PREFIX + key;
  471. }
  472. TermQuery propertyQuery = new TermQuery(new Term(key, value));
  473. booleanQuery.add(propertyQuery, BooleanClause.Occur.MUST);
  474. }
  475. }
  476. }
  477. }
  478. if (!CollectionUtils.isEmpty(kindIds)) {
  479. BooleanQuery booleanQuery2 = new BooleanQuery();
  480. for (Long id : kindIds) {
  481. booleanQuery2.add(new TermQuery(new Term(SearchConstants.COMPONENT_KI_ID_FIELD, id.toString())),
  482. Occur.SHOULD);
  483. }
  484. booleanQuery.add(booleanQuery2, Occur.MUST);
  485. }
  486. if (!CollectionUtils.isEmpty(brandIds)) {
  487. BooleanQuery booleanQuery2 = new BooleanQuery();
  488. for (Long id : brandIds) {
  489. booleanQuery2.add(new TermQuery(new Term(SearchConstants.COMPONENT_BR_ID_FIELD, id.toString())),
  490. Occur.SHOULD);
  491. }
  492. booleanQuery.add(booleanQuery2, Occur.MUST);
  493. }
  494. logger.info(booleanQuery.toString());
  495. Sort sort = new Sort(sortComponent(keyword));
  496. TopDocs hits;
  497. if (pageParams.getPage() > 1) {// 不是第一页
  498. TopDocs previousHits = indexSearcher.search(booleanQuery,
  499. (pageParams.getPage() - 1) * pageParams.getSize(), sort, true, false);
  500. ScoreDoc[] previousScoreDocs = previousHits.scoreDocs;
  501. ScoreDoc after = previousScoreDocs[previousScoreDocs.length - 1];
  502. hits = indexSearcher.searchAfter(after, booleanQuery, pageParams.getSize(), sort, true, false);
  503. } else {
  504. hits = indexSearcher.search(booleanQuery, pageParams.getSize(), sort, true, false);
  505. }
  506. ScoreDoc[] scoreDocs = hits.scoreDocs;
  507. for (ScoreDoc scoreDoc : scoreDocs) {
  508. // 数据量太大,需要指定将获取的数据(以免载入不必要的数据,降低速度)
  509. Set<String> fieldsToLoad = new HashSet<>();
  510. fieldsToLoad.add(SearchConstants.COMPONENT_ID_FIELD);
  511. Document document = indexSearcher.doc(scoreDoc.doc, fieldsToLoad);
  512. String componentId = document.get(SearchConstants.COMPONENT_ID_FIELD);
  513. ids.add(Long.parseLong(componentId));
  514. // System.out.println(componentId + "\t" + scoreDoc.score);
  515. }
  516. map.put("componentIds", ids);
  517. map.put("page", pageParams.getPage());
  518. map.put("size", pageParams.getSize());
  519. map.put("total", hits.totalHits);
  520. } catch (IOException e) {
  521. logger.error("", e);
  522. } finally {
  523. releaseIndexSearcher(indexSearcher);
  524. }
  525. return map;
  526. }
  527. /**
  528. * 同时搜索器件、类目、品牌,并设置boost
  529. *
  530. * @param keyword
  531. * @return
  532. */
  533. private Query setBoost(String keyword) {
  534. BooleanQuery booleanQuery = new BooleanQuery();
  535. PrefixQuery prefixQuery = new PrefixQuery(
  536. new Term(SearchConstants.COMPONENT_CODE_FIELD, keyword.toLowerCase()));
  537. prefixQuery.setBoost(100);
  538. booleanQuery.add(prefixQuery, BooleanClause.Occur.SHOULD);
  539. booleanQuery.add(createQuery(SearchConstants.COMPONENT_BR_NAMECN_FIELD, keyword, 10), BooleanClause.Occur.SHOULD);
  540. booleanQuery.add(createQuery(SearchConstants.COMPONENT_BR_NAMEEN_FIELD, keyword, 10), BooleanClause.Occur.SHOULD);
  541. booleanQuery.add(createQuery(SearchConstants.COMPONENT_KI_NAME_FIELD, keyword, 1), BooleanClause.Occur.SHOULD);
  542. return booleanQuery;
  543. }
  544. /**
  545. * @return 器件排序规则
  546. */
  547. private SortField[] sortComponent(String keyword) {
  548. // 分数 > 器件(访问量 > 搜索次数) > 品牌(权重 > 访问量 > 搜索次数) > 类目(访问量 > 搜索次数)
  549. return new SortField[]{
  550. sortField(SearchConstants.COMPONENT_WEIGHT_FIELD, Type.DOUBLE, true, Double.MIN_VALUE),
  551. sortField(SearchConstants.COMPONENT_VISIT_COUNT_FIELD, Type.LONG, true, Long.MIN_VALUE),
  552. sortField(SearchConstants.COMPONENT_SEARCH_COUNT_FIELD, Type.LONG, true, Long.MIN_VALUE),
  553. sortField(SearchConstants.COMPONENT_BR_WEIGHT_FIELD, Type.DOUBLE, true, Double.MIN_VALUE),
  554. sortField(SearchConstants.COMPONENT_BR_VISIT_COUNT_FIELD, Type.LONG, true, Long.MIN_VALUE),
  555. sortField(SearchConstants.COMPONENT_BR_SEARCH_COUNT_FIELD, Type.LONG, true, Long.MIN_VALUE),
  556. sortField(SearchConstants.COMPONENT_KI_VISIT_COUNT_FIELD, Type.LONG, true, Long.MIN_VALUE),
  557. sortField(SearchConstants.COMPONENT_KI_SEARCH_COUNT_FIELD, Type.LONG, true, Long.MIN_VALUE),
  558. new SortField(SearchConstants.COMPONENT_CODE_FIELD, new StringFieldComparatorSource(keyword, true)),
  559. new SortField(SearchConstants.COMPONENT_BR_NAMEEN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(keyword, true)),
  560. new SortField(SearchConstants.COMPONENT_BR_NAMECN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(keyword, true)),
  561. new SortField(SearchConstants.COMPONENT_KI_NAME_UNTOKENIZED_FIELD, new StringFieldComparatorSource(keyword))
  562. };
  563. }
  564. @Override
  565. public Set<Long> getKindIdsBySearchComponent(String keyword, String brandId) {
  566. Query filter = null;
  567. // 筛选品牌
  568. if (!StringUtils.isEmpty(brandId)) {
  569. filter = new TermQuery(new Term(SearchConstants.COMPONENT_BR_ID_FIELD, brandId));
  570. }
  571. return collectBySearchComponent(keyword, filter, SearchConstants.COMPONENT_KI_ID_FIELD).getValues();
  572. }
  573. @Override
  574. public Set<Map<String, Object>> getKindsBySearchComponent(String keyword, String brandId) {
  575. Query filter = null;
  576. // 筛选品牌
  577. if (!StringUtils.isEmpty(brandId)) {
  578. filter = new TermQuery(new Term(SearchConstants.COMPONENT_BR_ID_FIELD, brandId));
  579. }
  580. return collectBySearchComponent(keyword, filter, SearchConstants.KIND_ID_FIELD,
  581. new DistinctGroupCollector.CollectField(SearchConstants.KIND_ID_FIELD, "id"),
  582. new DistinctGroupCollector.CollectField(SearchConstants.KIND_NAMECN_FIELD, "nameCn"))
  583. .getCollectValues();
  584. }
  585. @Override
  586. public Set<Long> getBrandIdsBySearchComponent(String keyword, String kindId) {
  587. Query filter = null;
  588. // 筛选类目
  589. if (!StringUtils.isEmpty(kindId)) {
  590. filter = new TermQuery(new Term(SearchConstants.COMPONENT_KI_ID_FIELD, kindId));
  591. }
  592. return collectBySearchComponent(keyword, filter, SearchConstants.COMPONENT_BR_ID_FIELD).getValues();
  593. }
  594. @Override
  595. public Set<Map<String, Object>> getBrandsBySearchComponent(String keyword, String kindId) {
  596. Query filter = null;
  597. // 筛选类目
  598. if (!StringUtils.isEmpty(kindId)) {
  599. filter = new TermQuery(new Term(SearchConstants.COMPONENT_KI_ID_FIELD, kindId));
  600. }
  601. return collectBySearchComponent(keyword, filter, SearchConstants.COMPONENT_BR_ID_FIELD,
  602. new DistinctGroupCollector.CollectField(SearchConstants.COMPONENT_BR_ID_FIELD, "id"),
  603. new DistinctGroupCollector.CollectField(SearchConstants.COMPONENT_BR_UUID_FIELD, "uuid"),
  604. new DistinctGroupCollector.CollectField(SearchConstants.COMPONENT_BR_NAMECN_FIELD, "nameCn"))
  605. .getCollectValues();
  606. }
  607. /**
  608. * 搜索器件时统计指定信息
  609. *
  610. * @param keyword 关键词
  611. * @param filter 过滤条件
  612. * @param groupField 统计的单个字段(多为 id)
  613. * @param collectFields 统计的多个字段(详细信息),可为空
  614. * @return 统计信息
  615. */
  616. private DistinctGroupCollector collectBySearchComponent(String keyword, Query filter, String groupField, DistinctGroupCollector.CollectField... collectFields){
  617. if (SearchUtils.isKeywordInvalid(keyword)) {
  618. throw new IllegalArgumentException("搜索关键词无效:" + keyword);
  619. }
  620. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.COMPONENT_TABLE_NAME);
  621. try {
  622. BooleanQuery booleanQuery = new BooleanQuery();
  623. keyword = URLDecoder.decode(keyword, "UTF-8");
  624. booleanQuery.add(setBoost(keyword), BooleanClause.Occur.MUST);
  625. if(filter != null){
  626. booleanQuery.add(filter, Occur.FILTER);
  627. }
  628. logger.info(booleanQuery.toString());
  629. DistinctGroupCollector collector = new DistinctGroupCollector(groupField, collectFields);
  630. indexSearcher.search(booleanQuery, collector);
  631. return collector;
  632. } catch (IOException e) {
  633. throw new IllegalStateException("统计失败", e);
  634. } finally {
  635. releaseIndexSearcher(indexSearcher);
  636. }
  637. }
  638. @Override
  639. public List<String> getSimilarKeywords(String keyword, Integer size) {
  640. size = size == null || size < 1 ? SIMILAR_NUM : size;
  641. List<String> result = new ArrayList<>();
  642. // 相似的器件原厂型号数量足够,直接返回
  643. List<String> componentCodes = getSimilarComponentCodes(keyword, size);
  644. result.addAll(componentCodes);
  645. removeDuplicate(result);
  646. if (result.size() == size) {
  647. return result;
  648. }
  649. // 获取相似类目
  650. List<String> kindNames = getSimilarKindNames(keyword, size);
  651. if (!CollectionUtils.isEmpty(kindNames)) {
  652. result.addAll(kindNames);
  653. removeDuplicate(result);
  654. // 如果总的数量超出SIMILAR_NUM,去除多余的元素
  655. if (result.size() > size) {
  656. removeElements(result, size);
  657. return result;
  658. }
  659. }
  660. // 获取相似品牌
  661. List<String> brandNames = getSimilarBrandNames(keyword, size);
  662. if (!CollectionUtils.isEmpty(brandNames)) {
  663. result.addAll(brandNames);
  664. removeDuplicate(result);
  665. if (result.size() > size) {
  666. removeElements(result, size);
  667. return result;
  668. }
  669. }
  670. return result;
  671. }
  672. @Override
  673. public List<Map<String, Object>> getSimilarComponents(String componentCode, Integer size) {
  674. size = size == null || size < 1 ? SIMILAR_NUM : size;
  675. if (SearchUtils.isKeywordInvalid(componentCode)) {
  676. throw new IllegalArgumentException("输入无效:" + componentCode);
  677. }
  678. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.COMPONENT_TABLE_NAME);
  679. List<Map<String, Object>> components = new ArrayList<>();
  680. try {
  681. PrefixQuery prefixQuery = new PrefixQuery(
  682. new Term(SearchConstants.COMPONENT_CODE_FIELD, componentCode.toLowerCase()));
  683. logger.info(prefixQuery.toString());
  684. Sort sort = new Sort(new SortField(SearchConstants.COMPONENT_CODE_FIELD, new StringFieldComparatorSource(componentCode)));
  685. TopDocs hits = indexSearcher.search(prefixQuery, size, sort);
  686. ScoreDoc[] scoreDocs = hits.scoreDocs;
  687. for (ScoreDoc scoreDoc : scoreDocs) {
  688. Set<String> fieldsToLoad = new HashSet<>();
  689. fieldsToLoad.add(SearchConstants.COMPONENT_ID_FIELD);
  690. fieldsToLoad.add(SearchConstants.COMPONENT_UUID_FIELD);
  691. fieldsToLoad.add(SearchConstants.COMPONENT_CODE_FIELD);
  692. Document document = indexSearcher.doc(scoreDoc.doc, fieldsToLoad);
  693. Map<String, Object> map = new HashMap<>();
  694. map.put("id", Long.parseLong(document.get(SearchConstants.COMPONENT_ID_FIELD)));
  695. map.put("uuid", document.get(SearchConstants.COMPONENT_UUID_FIELD));
  696. map.put("code", document.get(SearchConstants.COMPONENT_CODE_FIELD));
  697. components.add(map);
  698. }
  699. } catch (IOException e) {
  700. logger.error("", e);
  701. } finally {
  702. releaseIndexSearcher(indexSearcher);
  703. }
  704. return components;
  705. }
  706. @Override
  707. public List<Map<String, Object>> getSimilarBrands(String brandName, Integer size) throws IOException {
  708. size = size == null || size < 1 ? SIMILAR_NUM : size;
  709. if (SearchUtils.isKeywordInvalid(brandName)) {
  710. throw new IllegalArgumentException("输入无效:" + brandName);
  711. }
  712. List<Map<String, Object>> brands = new ArrayList<Map<String, Object>>();
  713. // 品牌名称带有空格,并且中英文名并无一定顺序,因此对nameCn、nameEn均要搜索
  714. BooleanQuery booleanQuery = new BooleanQuery();
  715. // 搜索nameCn
  716. booleanQuery.add(SearchUtils.getBooleanQuery(SearchConstants.BRAND_NAMECN_FIELD, brandName),
  717. BooleanClause.Occur.SHOULD);
  718. // 搜索nameEn
  719. booleanQuery.add(SearchUtils.getBooleanQuery(SearchConstants.BRAND_NAMEEN_FIELD, brandName),
  720. BooleanClause.Occur.SHOULD);
  721. logger.info(booleanQuery.toString());
  722. Sort sort = new Sort(new SortField(SearchConstants.BRAND_NAMEEN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(brandName)),
  723. new SortField(SearchConstants.BRAND_NAMECN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(brandName)));
  724. List<Document> documents = getDocuments(SearchConstants.BRAND_TABLE_NAME, booleanQuery, sort, null, size)
  725. .getContent();
  726. for (Document document : documents) {
  727. Map<String, Object> brand = new HashMap<>();
  728. brand.put("id", Long.parseLong(document.get(SearchConstants.BRAND_ID_FIELD)));
  729. brand.put("uuid", document.get(SearchConstants.BRAND_UUID_FIELD));
  730. brand.put("nameCn", document.get(SearchConstants.BRAND_NAMECN_FIELD));
  731. brand.put("nameEn", document.get(SearchConstants.BRAND_NAMEEN_FIELD));
  732. brands.add(brand);
  733. }
  734. return brands;
  735. }
  736. @Override
  737. public List<Map<String, Object>> getSimilarKinds(String kindName, Integer size) throws IOException {
  738. size = size == null || size < 1 ? SIMILAR_NUM : size;
  739. return getSimilarKinds(kindName, null, null, size);
  740. }
  741. @Override
  742. public List<Map<String, Object>> getSimilarLeafKinds(String kindName, Integer size) throws IOException {
  743. size = size == null || size < 1 ? SIMILAR_NUM : size;
  744. return getSimilarKinds(kindName, (short) 1, null, size);
  745. }
  746. @Override
  747. public List<Map<String, Object>> getSimilarKindsByLevel(String kindName, Short level, Integer size) throws IOException {
  748. size = size == null || size < 1 ? SIMILAR_NUM : size;
  749. return getSimilarKinds(kindName, null, level, size);
  750. }
  751. /**
  752. * 根据输入的类目名获取联想词
  753. *
  754. * @param kindName
  755. * 类目名
  756. * @param isLeaf
  757. * 是否只获取末级类目
  758. * @param level
  759. * 指定的类目级别
  760. * @param size 指定的联想词数目
  761. * @return
  762. */
  763. private List<Map<String, Object>> getSimilarKinds(String kindName, Short isLeaf, Short level, Integer size) throws IOException {
  764. size = size == null || size < 1 ? SIMILAR_NUM : size;
  765. if (SearchUtils.isKeywordInvalid(kindName)) {
  766. throw new IllegalArgumentException("输入无效:" + kindName);
  767. }
  768. List<Map<String, Object>> kinds = new ArrayList<>();
  769. BooleanQuery booleanQuery = new BooleanQuery();
  770. booleanQuery.add(SearchUtils.getBooleanQuery(SearchConstants.KIND_NAMECN_FIELD, kindName),
  771. BooleanClause.Occur.MUST);
  772. if (isLeaf != null && isLeaf == 1) {
  773. booleanQuery.add(new TermQuery(new Term(SearchConstants.KIND_ISLEAF_FIELD, String.valueOf(isLeaf))),
  774. BooleanClause.Occur.MUST);
  775. } else {
  776. if (level != null && level > 0) {
  777. booleanQuery.add(new TermQuery(new Term(SearchConstants.KIND_LEVEL_FIELD, String.valueOf(level))),
  778. BooleanClause.Occur.MUST);
  779. }
  780. }
  781. logger.info(booleanQuery.toString());
  782. Sort sort = new Sort(new SortField(SearchConstants.KIND_NAMECN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(kindName)));
  783. List<Document> documents = getDocuments(SearchConstants.KIND_TABLE_NAME, booleanQuery, sort, null, size).getContent();
  784. for (Document document : documents) {
  785. Map<String, Object> map = new HashMap<>();
  786. map.put("id", Long.parseLong(document.get(SearchConstants.KIND_ID_FIELD)));
  787. map.put("nameCn", document.get(SearchConstants.KIND_NAMECN_FIELD));
  788. map.put("isLeaf", Short.parseShort(document.get(SearchConstants.KIND_ISLEAF_FIELD)));
  789. map.put("level", Short.parseShort(document.get(SearchConstants.KIND_LEVEL_FIELD)));
  790. kinds.add(map);
  791. }
  792. return kinds;
  793. }
  794. @Override
  795. public List<Map<String, String>> getSimilarPropertyValues(Long kindId, Long propertyId, String keyword,
  796. Long topNum) {
  797. if (kindId == null || propertyId == null) {
  798. throw new IllegalArgumentException("类目id和属性id不能为空");
  799. }
  800. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.COMPONENT_TABLE_NAME);
  801. String propertyIdString = String.valueOf(propertyId);
  802. if (!propertyIdString.startsWith(SearchConstants.COMPONENT_PROPERTY_PREFIX)) {
  803. propertyIdString = SearchConstants.COMPONENT_PROPERTY_PREFIX + propertyIdString;
  804. }
  805. propertyIdString = propertyIdString + SearchConstants.COMPONENT_PROPERTY_TOKENIZED_SUFFIX;
  806. if (keyword == null) {
  807. keyword = "";
  808. }
  809. if (topNum == null || topNum < 1) {
  810. topNum = (long) SIMILAR_NUM;
  811. }
  812. List<String> propertyValues = new ArrayList<>();
  813. try {
  814. BooleanQuery booleanQuery = new BooleanQuery();
  815. booleanQuery.add(new TermQuery(new Term(SearchConstants.COMPONENT_KI_ID_FIELD, String.valueOf(kindId))),
  816. BooleanClause.Occur.MUST);
  817. booleanQuery.add(new PrefixQuery(new Term(propertyIdString, keyword.toLowerCase())),
  818. BooleanClause.Occur.MUST);
  819. logger.info(booleanQuery.toString());
  820. // 如果只搜索topNum个结果,去除重复的属性值后,数目很可能是不够的
  821. TopDocs topDocs = indexSearcher.search(booleanQuery, SearchConstants.TOP_NUM);
  822. ScoreDoc[] scoreDocs = topDocs.scoreDocs;
  823. for (ScoreDoc scoreDoc : scoreDocs) {
  824. Set<String> fieldsToLoad = new HashSet<>();
  825. fieldsToLoad.add(propertyIdString);
  826. Document document = indexSearcher.doc(scoreDoc.doc, fieldsToLoad);
  827. String propertyValue = document.get(propertyIdString);
  828. if (!StringUtils.isEmpty(propertyValue) && !propertyValues.contains(propertyValue)) {
  829. propertyValues.add(propertyValue);
  830. }
  831. if (propertyValues.size() >= topNum) {
  832. break;
  833. }
  834. }
  835. } catch (IOException e) {
  836. logger.error("", e);
  837. } finally {
  838. releaseIndexSearcher(indexSearcher);
  839. }
  840. List<Map<String, String>> result = new ArrayList<>();
  841. for (String propertyValue : propertyValues) {
  842. Map<String, String> map = new HashMap<>();
  843. map.put("propertyValue", propertyValue);
  844. result.add(map);
  845. }
  846. return result;
  847. }
  848. /**
  849. * 根据输入获取相似的器件原厂型号
  850. *
  851. * @param componentCode
  852. * @param size 指定的联想词数目
  853. * @return
  854. */
  855. private List<String> getSimilarComponentCodes(String componentCode, Integer size) {
  856. return getSimilarValues(SearchConstants.COMPONENT_TABLE_NAME, SearchConstants.COMPONENT_CODE_FIELD,
  857. SearchConstants.COMPONENT_CODE_FIELD, componentCode.toLowerCase(), size, true);
  858. }
  859. /**
  860. * 根据输入获取相似的品牌名称
  861. *
  862. * @param brandName
  863. * @param size 指定的联想词数目
  864. * @return
  865. */
  866. private List<String> getSimilarBrandNames(String brandName, Integer size) {
  867. // 获取相似的中文品牌
  868. List<String> nameCns = getSimilarValues(SearchConstants.BRAND_TABLE_NAME, SearchConstants.BRAND_NAMECN_FIELD,
  869. SearchConstants.BRAND_NAMECN_UNTOKENIZED_FIELD, brandName, size, false);
  870. // 相似的中文品牌数量足够,直接返回
  871. if (nameCns != null && nameCns.size() == SIMILAR_NUM) {
  872. return nameCns;
  873. }
  874. List<String> names = nameCns;
  875. // 获取相似的英文品牌
  876. List<String> nameEns = getSimilarValues(SearchConstants.BRAND_TABLE_NAME, SearchConstants.BRAND_NAMEEN_FIELD,
  877. SearchConstants.BRAND_NAMEEN_UNTOKENIZED_FIELD, brandName, size, false);
  878. names.addAll(nameEns);
  879. return names;
  880. }
  881. /**
  882. * 根据输入获取相似的类目名称
  883. *
  884. * @param kindName
  885. * @param size 指定的联想词数目
  886. * @return
  887. */
  888. private List<String> getSimilarKindNames(String kindName, Integer size) {
  889. return getSimilarValues(SearchConstants.KIND_TABLE_NAME, SearchConstants.KIND_NAMECN_FIELD,
  890. SearchConstants.KIND_NAMECN_UNTOKENIZED_FIELD, kindName, size, false);
  891. }
  892. /**
  893. * 根据输入值获取该域相似的值
  894. *
  895. * @param tableName 表名
  896. * @param field 联想字段
  897. * @param sortField 排序字段
  898. * @param keyword 关键词
  899. * @param size 联想词数目
  900. * @param usePrefixQuery 是否使用 PrefixQuery
  901. * @return 联想词
  902. */
  903. private List<String> getSimilarValues(String tableName, String field, String sortField, String keyword, Integer size, boolean usePrefixQuery) {
  904. if (SearchUtils.isKeywordInvalid(keyword)) {
  905. throw new IllegalArgumentException("输入无效:" + keyword);
  906. }
  907. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(tableName);
  908. List<String> result = new ArrayList<>();
  909. try {
  910. Query query;
  911. if(usePrefixQuery){
  912. query = new PrefixQuery(new Term(field, keyword.toLowerCase()));
  913. }else{
  914. query = SearchUtils.getBooleanQuery(field, keyword);
  915. }
  916. logger.info(query.toString());
  917. Sort sort = new Sort(new SortField(sortField, new StringFieldComparatorSource(keyword)));
  918. TopDocs hits = indexSearcher.search(query, size, sort);
  919. ScoreDoc[] scoreDocs = hits.scoreDocs;
  920. for (ScoreDoc scoreDoc : scoreDocs) {
  921. Set<String> fieldsToLoad = new HashSet<>();
  922. fieldsToLoad.add(field);
  923. Document document = indexSearcher.doc(scoreDoc.doc, fieldsToLoad);
  924. result.add(document.get(field));
  925. // System.out.println(document.get(field) + "\t" +
  926. // scoreDoc.score);
  927. }
  928. } catch (IOException e) {
  929. logger.error("", e);
  930. } finally {
  931. releaseIndexSearcher(indexSearcher);
  932. }
  933. return result;
  934. }
  935. /**
  936. * 移除集合中重复的元素
  937. *
  938. * @param list
  939. * @return
  940. */
  941. private void removeDuplicate(List<String> list) {
  942. if (list == null) {
  943. return;
  944. }
  945. List<String> result = new ArrayList<>();
  946. for (String str : list) {
  947. if (!result.contains(str)) {
  948. result.add(str);
  949. }
  950. }
  951. list.removeAll(list);
  952. list.addAll(result);
  953. }
  954. /**
  955. * 删除集合内 startIndex(含)后的元素
  956. *
  957. * @param list
  958. * @param startIndex
  959. */
  960. private void removeElements(List<? extends String> list, int startIndex) {
  961. if (CollectionUtils.isEmpty(list)) {
  962. return;
  963. }
  964. int listsSize = list.size();
  965. for (int i = listsSize - 1; i >= startIndex; i--) {
  966. list.remove(i);
  967. }
  968. }
  969. @Override
  970. public Map<String, Object> getGoodsIds(String keyword, PageParams pageParams) throws IOException {
  971. // List<String> keywordFields = new ArrayList<>();
  972. // 先根据品牌搜索,品牌不存在再搜索型号等
  973. // keywordFields.add(SearchConstants.GOODS_BR_NAME_CN_UNTOKENIZED_FIELD);
  974. // keywordFields.add(SearchConstants.GOODS_BR_NAME_EN_UNTOKENIZED_FIELD);
  975. // Map<String, Object> goodsIds = getGoodsIds(keyword, keywordFields, false, pageParams);
  976. // if (CollectionUtils.isEmpty(goodsIds) || goodsIds.get("componentIds") == null
  977. // || JSONObject.parseArray(goodsIds.get("componentIds").toString()).isEmpty()) {
  978. // keyword = recursivelyGetGoodsIds(keyword, null, true);
  979. // goodsIds = getGoodsIds(keyword, null, true, pageParams);
  980. // }
  981. // return goodsIds;
  982. Map<String, Object> map = new HashMap<>();
  983. List<String> goodsFields = new ArrayList<>();
  984. List<String> productsFields = new ArrayList<>();
  985. // 先根据品牌搜索,品牌不存在再搜索型号等
  986. goodsFields.add(SearchConstants.GOODS_PR_PCMPCODE_FIELD);
  987. goodsFields.add(SearchConstants.GOODS_CMP_CODE_FIELD);
  988. productsFields.add(SearchConstants.PRODUCT_PRIVATE_CMPCODE_FIELD);
  989. productsFields.add(SearchConstants.PRODUCT_PRIVATE_PCMPCODE_FIELD);
  990. map.put("stock", getGoodsIds(keyword, goodsFields, true, pageParams));
  991. map.put("futures", getProductIds(keyword, pageParams));
  992. return map;
  993. }
  994. /**
  995. * @param keyword
  996. * @param keywordFields
  997. * 要查询的字段
  998. * @param tokenized
  999. * 是否分词
  1000. * @param pageParams
  1001. * @return
  1002. */
  1003. private Map<String, Object> getGoodsIds(String keyword, List<String> keywordFields, Boolean tokenized,
  1004. PageParams pageParams) {
  1005. // 因为器件、属性值的数据量远比类目、品牌大得多,而且器件搜索可能还需进行分页,
  1006. // 所以涉及器件、属性值的搜索,大都不能像类目和品牌一样直接利用SearchUtils.getDocuments方法
  1007. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.GOODS_TABLE_NAME);
  1008. if (pageParams == null) {
  1009. pageParams = new PageParams();
  1010. }
  1011. if (pageParams.getPage() <= 0)
  1012. pageParams.setPage(1);
  1013. if (pageParams.getSize() <= 0)
  1014. pageParams.setSize(20);
  1015. Map<String, Object> map = new HashMap<String, Object>();
  1016. // List<Long> cmpIds = new ArrayList<>();
  1017. List<Long> goIds = new ArrayList<>();
  1018. try {
  1019. BooleanQuery booleanQuery = queryGoods(keyword, keywordFields, tokenized);
  1020. setGoodsFilter(pageParams.getFilters(), booleanQuery);
  1021. logger.info(booleanQuery.toString());
  1022. Sort sort = sortGoods(keyword, pageParams.getSort());
  1023. TopDocs hits;
  1024. if (pageParams.getPage() > 1) {// 不是第一页
  1025. TopDocs previousHits = indexSearcher.search(booleanQuery,
  1026. (pageParams.getPage() - 1) * pageParams.getSize(), sort, true, false);
  1027. int totalHits = previousHits.totalHits;
  1028. if ((pageParams.getPage() - 1) * pageParams.getSize() >= totalHits) {
  1029. return map;
  1030. }
  1031. ScoreDoc[] previousScoreDocs = previousHits.scoreDocs;
  1032. ScoreDoc after = previousScoreDocs[previousScoreDocs.length - 1];
  1033. hits = indexSearcher.searchAfter(after, booleanQuery, pageParams.getSize(), sort, true, false);
  1034. } else {
  1035. hits = indexSearcher.search(booleanQuery, pageParams.getSize(), sort, true, false);
  1036. }
  1037. // 数据量太大,需要指定将获取的数据(以免载入不必要的数据,降低速度)
  1038. Set<String> fieldsToLoad = new HashSet<>();
  1039. // fieldsToLoad.add(SearchConstants.GOODS_CMP_ID_FIELD);
  1040. fieldsToLoad.add(SearchConstants.GOODS_GO_ID_FIELD);
  1041. ScoreDoc[] scoreDocs = hits.scoreDocs;
  1042. int totalHits = hits.totalHits;
  1043. for (ScoreDoc scoreDoc : scoreDocs) {
  1044. Document document = indexSearcher.doc(scoreDoc.doc, fieldsToLoad);
  1045. // String cmpId = document.get(SearchConstants.GOODS_CMP_ID_FIELD);
  1046. // cmpIds.add(StringUtils.isEmpty(cmpId) || cmpId.equals(ObjectToDocumentUtils.NULL_VALUE) ? null : Long.valueOf(cmpId));
  1047. String goId = document.get(SearchConstants.GOODS_GO_ID_FIELD);
  1048. if (!StringUtils.isEmpty(goId) && !goId.equals(ObjectToDocumentUtils.NULL_VALUE)) {
  1049. goIds.add(Long.valueOf(goId));
  1050. } else {
  1051. totalHits--;
  1052. }
  1053. }
  1054. // map.put("componentIds", cmpIds);
  1055. map.put("content", goIds);
  1056. map.put("page", pageParams.getPage());
  1057. map.put("size", pageParams.getSize());
  1058. map.put("totalElement", totalHits);
  1059. int totalPage = (int) Math.ceil(totalHits / (1.0 * pageParams.getSize()));
  1060. map.put("totalPage", totalPage);
  1061. map.put("last", totalPage == pageParams.getPage() ? true : false);
  1062. map.put("first", pageParams.getPage() == 1 ? true : false);
  1063. } catch (IOException e) {
  1064. logger.error("", e);
  1065. } finally {
  1066. releaseIndexSearcher(indexSearcher);
  1067. }
  1068. return map;
  1069. }
  1070. /**
  1071. * 获取物料信息
  1072. * @param keyword
  1073. * @return
  1074. */
  1075. private SPage<Long> getProductIds(String keyword, PageParams pageParams) throws IOException{
  1076. if (pageParams == null) {
  1077. pageParams = new PageParams();
  1078. }
  1079. if (pageParams.getPage() <= 0)
  1080. pageParams.setPage(1);
  1081. if (pageParams.getSize() <= 0)
  1082. pageParams.setSize(20);
  1083. BooleanQuery q1 = new BooleanQuery();
  1084. q1.add(new TermQuery(new Term(SearchConstants.PRODUCT_PRIVATE_B2CENABLED_FIELD, String.valueOf(1))), BooleanClause.Occur.MUST);
  1085. BooleanQuery booleanQuery = new BooleanQuery();
  1086. booleanQuery.add(q1, BooleanClause.Occur.MUST);
  1087. if (!StringUtils.isEmpty(keyword)) {
  1088. BooleanQuery q2 = new BooleanQuery();
  1089. q2.add(createQuery(SearchConstants.PRODUCT_PRIVATE_PCMPCODE_FIELD, keyword, true,1), BooleanClause.Occur.SHOULD);
  1090. q2.add(createQuery(SearchConstants.PRODUCT_PRIVATE_CMPCODE_FIELD, keyword, true,1), BooleanClause.Occur.SHOULD);
  1091. booleanQuery.add(q2, BooleanClause.Occur.MUST);
  1092. }
  1093. logger.info(booleanQuery.toString());
  1094. SPage<Document> documents = getDocuments(SearchConstants.PRODUCTS_PRIVATE_TABLE_NAME, booleanQuery, new Sort(sortProduct(keyword)), pageParams.getPage(), pageParams.getSize());
  1095. SPage<Long> sPage = new SPage<>(documents.getTotalPage(), documents.getTotalElement(), documents.getPage(), documents.getSize(), documents.isFirst(), documents.isLast());
  1096. List<Long> productIds = new ArrayList<>();
  1097. for (Document document : documents.getContent()) {
  1098. productIds.add(Long.parseLong(document.get(SearchConstants.PRODUCT_PRIVATE_ID_FIELD)));
  1099. }
  1100. sPage.setContent(productIds);
  1101. return sPage;
  1102. }
  1103. /**
  1104. * 递归查询批次(如果没有结果,则降低精度,直至长度为 1)
  1105. *
  1106. * @param keyword 关键词
  1107. * @param keywordFields 要查询的字段
  1108. * @param tokenized 是否分词
  1109. * @return 最后一次搜索的关键词
  1110. */
  1111. private String recursivelyGetGoodsIds(String keyword, List<String> keywordFields, Boolean tokenized) throws IOException {
  1112. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.GOODS_TABLE_NAME);
  1113. try {
  1114. BooleanQuery booleanQuery = queryGoods(keyword, keywordFields, tokenized);
  1115. logger.info(booleanQuery.toString());
  1116. TotalHitCountCollector collector = new TotalHitCountCollector();
  1117. indexSearcher.search(booleanQuery, collector);
  1118. // 如果没有结果,则降低精度,直至 keyword 长度为 1
  1119. if (collector.getTotalHits() < 1 && !SearchUtils.isKeywordInvalid(keyword) && keyword.length() > 1) {
  1120. return recursivelyGetGoodsIds(keyword.substring(0, keyword.length() - 1), keywordFields, tokenized);
  1121. }
  1122. return keyword;
  1123. } finally {
  1124. releaseIndexSearcher(indexSearcher);
  1125. }
  1126. }
  1127. /**
  1128. * 设置批次过滤条件
  1129. *
  1130. * @param filters 指定的过滤条件
  1131. * @param query 原查询
  1132. */
  1133. private void setGoodsFilter(Map<FilterField, Object> filters, BooleanQuery query) {
  1134. Object status;
  1135. // 筛选状态
  1136. if (!CollectionUtils.isEmpty(filters) && !StringUtils.isEmpty(filters.get(FilterField.GOODS_STATUS))) {
  1137. // 如果明确指定了状态,则直接过滤批次(结果中不包括没有批次的器件)
  1138. status = filters.get(FilterField.GOODS_STATUS);
  1139. filter(status, SearchConstants.GOODS_GO_STATUS_FIELD, query);
  1140. } else {
  1141. // 如果未明确指定状态,则使用默认状态分情况进行过滤(结果中包括没有批次的器件)
  1142. status = Arrays.asList(TradeGoods.VALID_STATUS);
  1143. // 批次 id 不为空时,对状态过滤
  1144. Query goNullQuery = SearchUtils.getNullQuery(SearchConstants.GOODS_GO_ID_FIELD);
  1145. BooleanQuery q1 = new BooleanQuery();
  1146. q1.add(goNullQuery, Occur.MUST_NOT);
  1147. filter(status, SearchConstants.GOODS_GO_STATUS_FIELD, q1);
  1148. // 或者批次 id 为空(此时是器件)
  1149. BooleanQuery q2 = new BooleanQuery();
  1150. q2.add(SearchUtils.getNullQuery(SearchConstants.GOODS_CMP_ID_FIELD), Occur.MUST_NOT);
  1151. q2.add(goNullQuery, Occur.MUST);
  1152. BooleanQuery booleanQuery = new BooleanQuery();
  1153. booleanQuery.add(q1, Occur.SHOULD);
  1154. booleanQuery.add(q2, Occur.SHOULD);
  1155. query.add(booleanQuery, Occur.FILTER);
  1156. }
  1157. if (CollectionUtils.isEmpty(filters)) {
  1158. return;
  1159. }
  1160. // 筛选类目
  1161. if (!StringUtils.isEmpty(filters.get(FilterField.GOODS_KINDID))) {
  1162. filter(filters.get(FilterField.GOODS_KINDID), SearchConstants.GOODS_KI_ID_FIELD, query);
  1163. }
  1164. // 筛选品牌
  1165. if (!StringUtils.isEmpty(filters.get(FilterField.GOODS_BRANDID))) {
  1166. filter(filters.get(FilterField.GOODS_BRANDID), SearchConstants.GOODS_BR_ID_FIELD, query);
  1167. }
  1168. // 筛选货源
  1169. if (!StringUtils.isEmpty(filters.get(FilterField.GOODS_STORE_TYPE))) {
  1170. filter(filters.get(FilterField.GOODS_STORE_TYPE), SearchConstants.GOODS_ST_TYPE_FIELD, query);
  1171. }
  1172. // 筛选货币
  1173. if (!StringUtils.isEmpty(filters.get(FilterField.GOODS_CRNAME))) {
  1174. filter(filters.get(FilterField.GOODS_CRNAME), SearchConstants.GOODS_CRNAME_FIELD, query);
  1175. }
  1176. // 价格筛选
  1177. Object minPriceRmb = filters.get(FilterField.GOODS_MINPRICERMB);
  1178. Object maxPriceRmb = filters.get(FilterField.GOODS_MAXPRICERMB);
  1179. Object minPriceUsd = filters.get(FilterField.GOODS_MINPRICEUSD);
  1180. Object maxPriceUsd = filters.get(FilterField.GOODS_MAXPRICEUSD);
  1181. // 筛选人民币价格
  1182. if (!StringUtils.isEmpty(minPriceRmb) || !StringUtils.isEmpty(maxPriceRmb)) {
  1183. Double minPrice = null;
  1184. Double maxPrice = null;
  1185. if (!StringUtils.isEmpty(minPriceRmb)) {
  1186. minPrice = Double.valueOf(minPriceRmb.toString());
  1187. }
  1188. if (!StringUtils.isEmpty(maxPriceRmb)) {
  1189. maxPrice = Double.valueOf(maxPriceRmb.toString());
  1190. }
  1191. query.add(NumericRangeQuery.newDoubleRange(SearchConstants.GOODS_GO_MINPRICERMB_FIELD,
  1192. minPrice, maxPrice, true, true), BooleanClause.Occur.FILTER);
  1193. }
  1194. // 筛选美元价格
  1195. if (!StringUtils.isEmpty(minPriceUsd) || !StringUtils.isEmpty(maxPriceUsd)) {
  1196. Double minPrice = null;
  1197. Double maxPrice = null;
  1198. if (!StringUtils.isEmpty(minPriceUsd)) {
  1199. minPrice = Double.valueOf(minPriceUsd.toString());
  1200. }
  1201. if (!StringUtils.isEmpty(maxPriceUsd)) {
  1202. maxPrice = Double.valueOf(maxPriceUsd.toString());
  1203. }
  1204. query.add(NumericRangeQuery.newDoubleRange(SearchConstants.GOODS_GO_MINPRICEUSD_FIELD,
  1205. minPrice, maxPrice, true, true), BooleanClause.Occur.FILTER);
  1206. }
  1207. }
  1208. /**
  1209. * 设置批次过滤条件
  1210. *
  1211. * @param filters 指定的过滤条件
  1212. * @param query 原查询
  1213. */
  1214. private void setProductsFilter(Map<FilterField, Object> filters, BooleanQuery query) {
  1215. Object status;
  1216. // 筛选状态
  1217. if (!CollectionUtils.isEmpty(filters) && !StringUtils.isEmpty(filters.get(FilterField.PRODUCT_STATUS))) {
  1218. // 如果明确指定了状态,则直接过滤物料
  1219. status = filters.get(FilterField.PRODUCT_STATUS);
  1220. filter(status, SearchConstants.PRODUCT_PRIVATE_B2CENABLED_FIELD, query);
  1221. } else {
  1222. // 如果未明确指定状态,则使用默认状态分情况进行过滤(结果中包括没有批次的器件)
  1223. status = Arrays.asList(V_Products.VALID_STATUS);
  1224. // 或者批次 id 为空(此时是器件)
  1225. BooleanQuery q2 = new BooleanQuery();
  1226. BooleanQuery booleanQuery = new BooleanQuery();
  1227. booleanQuery.add(q2, Occur.SHOULD);
  1228. query.add(booleanQuery, Occur.FILTER);
  1229. }
  1230. }
  1231. /**
  1232. * 批次排序规则
  1233. */
  1234. private Sort sortGoods(String keyword, com.uas.search.constant.model.Sort sort) {
  1235. List<SortField> sortFieldList = new ArrayList<>();
  1236. sortFieldList.add(SortField.FIELD_SCORE);
  1237. if (sort != null) {
  1238. com.uas.search.constant.model.Sort.Field field = sort.getField();
  1239. if (field == null) {
  1240. throw new IllegalArgumentException("排序字段不可为空:" + sort);
  1241. }
  1242. boolean reverse = sort.isReverse();
  1243. if (field == RESERVE) {
  1244. // 库存
  1245. sortFieldList.addAll(Arrays.asList(
  1246. // 降序时,默认值为最小值;升序时,默认值为最大值
  1247. sortField(SearchConstants.GOODS_GO_RESERVE_FIELD, Type.DOUBLE, reverse, reverse ? Double.MIN_VALUE : Double.MAX_VALUE),
  1248. sortField(SearchConstants.GOODS_CMP_WEIGHT_FIELD, Type.DOUBLE, true, Double.MIN_VALUE),
  1249. sortField(SearchConstants.GOODS_BR_WEIGHT_FIELD, Type.DOUBLE, true, Double.MIN_VALUE),
  1250. sortField(SearchConstants.GOODS_GO_MINPRICERMB_FIELD, Type.DOUBLE, false, Double.MAX_VALUE),
  1251. sortField(SearchConstants.GOODS_GO_MINPRICEUSD_FIELD, Type.DOUBLE, false, Double.MAX_VALUE)
  1252. ));
  1253. } else if (field == PRICE) {
  1254. // 价格
  1255. sortFieldList.addAll(Arrays.asList(
  1256. // 降序时,默认值为最小值;升序时,默认值为最大值
  1257. sortField(SearchConstants.GOODS_GO_MINPRICERMB_FIELD, Type.DOUBLE, reverse, reverse ? Double.MIN_VALUE : Double.MAX_VALUE),
  1258. sortField(SearchConstants.GOODS_GO_MINPRICEUSD_FIELD, Type.DOUBLE, reverse, reverse ? Double.MIN_VALUE : Double.MAX_VALUE),
  1259. sortField(SearchConstants.GOODS_CMP_WEIGHT_FIELD, Type.DOUBLE, true, Double.MIN_VALUE),
  1260. sortField(SearchConstants.GOODS_BR_WEIGHT_FIELD, Type.DOUBLE, true, Double.MIN_VALUE),
  1261. sortField(SearchConstants.GOODS_GO_RESERVE_FIELD, Type.DOUBLE, true, Double.MIN_VALUE)
  1262. ));
  1263. } else {
  1264. throw new IllegalArgumentException("不支持该排序方式:" + field);
  1265. }
  1266. } else {
  1267. // 默认(综合排序)
  1268. sortFieldList.addAll(Arrays.asList(
  1269. sortField(SearchConstants.GOODS_CMP_WEIGHT_FIELD, Type.DOUBLE, true, Double.MIN_VALUE),
  1270. sortField(SearchConstants.GOODS_BR_WEIGHT_FIELD, Type.DOUBLE, true, Double.MIN_VALUE),
  1271. sortField(SearchConstants.GOODS_GO_MINPRICERMB_FIELD, Type.DOUBLE, false, Double.MAX_VALUE),
  1272. sortField(SearchConstants.GOODS_GO_MINPRICEUSD_FIELD, Type.DOUBLE, false, Double.MAX_VALUE),
  1273. sortField(SearchConstants.GOODS_GO_RESERVE_FIELD, Type.DOUBLE, true, Double.MIN_VALUE),
  1274. sortField(SearchConstants.GOODS_GO_MINDELIVERY_FIELD, Type.LONG, false, Long.MAX_VALUE)
  1275. ));
  1276. }
  1277. sortFieldList.addAll(Arrays.asList(
  1278. // 如果仍然无法得到正确结果,就根据按照型号等顺序严格排列
  1279. // new SortField(SearchConstants.GOODS_CMP_CODE_FIELD, new StringFieldComparatorSource(keyword, false)),
  1280. new SortField(SearchConstants.GOODS_PR_PCMPCODE_FIELD, new StringFieldComparatorSource(keyword, false)),
  1281. new SortField(SearchConstants.GOODS_BR_NAME_EN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(keyword, false)),
  1282. new SortField(SearchConstants.GOODS_BR_NAME_CN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(keyword, false)),
  1283. new SortField(SearchConstants.GOODS_PR_KIND_FIELD, new StringFieldComparatorSource(keyword, false)),
  1284. new SortField(SearchConstants.GOODS_CMP_CODE_FIELD, new StringFieldComparatorSource(keyword, false)),
  1285. // new SortField(SearchConstants.GOODS_KI_NAME_CN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(keyword, false)),
  1286. sortField(SearchConstants.GOODS_GO_UPDATE_DATE_FIELD, Type.LONG, true, Long.MIN_VALUE)
  1287. ));
  1288. SortField[] sortFields = new SortField[sortFieldList.size()];
  1289. sortFieldList.toArray(sortFields);
  1290. return new Sort(sortFields);
  1291. }
  1292. @Override
  1293. public Map<String, Object> getPCBGoodsIds(String keyword, PageParams pageParams) throws IOException {
  1294. keyword = recursivelyGetPCBGoodsIds(keyword);
  1295. // 因为器件、属性值等的数据量远比类目、品牌大得多,而且器件搜索可能还需进行分页,
  1296. // 所以涉及器件、属性值的搜索,大都不能像类目和品牌一样直接利用SearchUtils.getDocuments方法
  1297. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.PCB_GOODS_TABLE_NAME);
  1298. if (pageParams == null) {
  1299. pageParams = new PageParams();
  1300. }
  1301. if (pageParams.getPage() <= 0)
  1302. pageParams.setPage(1);
  1303. if (pageParams.getSize() <= 0)
  1304. pageParams.setSize(20);
  1305. Map<String, Object> map = new HashMap<String, Object>();
  1306. List<Long> pcbIds = new ArrayList<>();
  1307. List<Long> goIds = new ArrayList<>();
  1308. try {
  1309. BooleanQuery booleanQuery = queryPCBGoods(keyword);
  1310. setPCBGoodsFilter(pageParams.getFilters(), booleanQuery);
  1311. logger.info(booleanQuery.toString());
  1312. Sort sort = sortPCBGoods(keyword, pageParams.getSort());
  1313. TopDocs hits;
  1314. if (pageParams.getPage() > 1) { // 不是第一页
  1315. TopDocs previousHits = indexSearcher.search(booleanQuery,
  1316. (pageParams.getPage() - 1) * pageParams.getSize(), sort, true, false);
  1317. int totalHits = previousHits.totalHits;
  1318. if ((pageParams.getPage() - 1) * pageParams.getSize() >= totalHits) {
  1319. return map;
  1320. }
  1321. ScoreDoc[] previousScoreDocs = previousHits.scoreDocs;
  1322. ScoreDoc after = previousScoreDocs[previousScoreDocs.length - 1];
  1323. hits = indexSearcher.searchAfter(after, booleanQuery, pageParams.getSize(), sort, true, false);
  1324. } else {
  1325. hits = indexSearcher.search(booleanQuery, pageParams.getSize(), sort, true, false);
  1326. }
  1327. // 数据量太大,需要指定将获取的数据(以免载入不必要的数据,降低速度)
  1328. Set<String> fieldsToLoad = new HashSet<>();
  1329. fieldsToLoad.add(SearchConstants.PCB_GOODS_PCB_ID_FIELD);
  1330. fieldsToLoad.add(SearchConstants.PCB_GOODS_GO_ID_FIELD);
  1331. ScoreDoc[] scoreDocs = hits.scoreDocs;
  1332. for (ScoreDoc scoreDoc : scoreDocs) {
  1333. Document document = indexSearcher.doc(scoreDoc.doc, fieldsToLoad);
  1334. String pcbId = document.get(SearchConstants.PCB_GOODS_PCB_ID_FIELD);
  1335. pcbIds.add(StringUtils.isEmpty(pcbId) || pcbId.equals(ObjectToDocumentUtils.NULL_VALUE) ? null : Long.valueOf(pcbId));
  1336. String goId = document.get(SearchConstants.PCB_GOODS_GO_ID_FIELD);
  1337. goIds.add(StringUtils.isEmpty(goId) || goId.equals(ObjectToDocumentUtils.NULL_VALUE) ? null : Long.valueOf(goId));
  1338. }
  1339. map.put("pcbIds", pcbIds);
  1340. map.put("goodsIds", goIds);
  1341. map.put("page", pageParams.getPage());
  1342. map.put("size", pageParams.getSize());
  1343. map.put("total", hits.totalHits);
  1344. } catch (IOException e) {
  1345. logger.error("", e);
  1346. } finally {
  1347. releaseIndexSearcher(indexSearcher);
  1348. }
  1349. return map;
  1350. }
  1351. /**
  1352. * 递归查询 PCB 批次(如果没有结果,则降低精度,直至长度为 1)
  1353. *
  1354. * @param keyword 关键词
  1355. * @return 最后一次搜索的关键词
  1356. */
  1357. private String recursivelyGetPCBGoodsIds(String keyword) throws IOException {
  1358. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.PCB_GOODS_TABLE_NAME);
  1359. try {
  1360. BooleanQuery booleanQuery = queryPCBGoods(keyword);
  1361. logger.info(booleanQuery.toString());
  1362. TotalHitCountCollector collector = new TotalHitCountCollector();
  1363. indexSearcher.search(booleanQuery, collector);
  1364. // 如果没有结果,则降低精度,直至 keyword 长度为 1
  1365. if (collector.getTotalHits() < 1 && !SearchUtils.isKeywordInvalid(keyword) && keyword.length() > 1) {
  1366. return recursivelyGetPCBGoodsIds(keyword.substring(0, keyword.length() - 1));
  1367. }
  1368. return keyword;
  1369. } finally {
  1370. releaseIndexSearcher(indexSearcher);
  1371. }
  1372. }
  1373. /**
  1374. * 设置 PCB 批次过滤条件
  1375. *
  1376. * @param filters 指定的过滤条件
  1377. * @param query 原查询
  1378. */
  1379. private void setPCBGoodsFilter(Map<FilterField, Object> filters, BooleanQuery query) {
  1380. Object status;
  1381. // 筛选状态
  1382. if (!CollectionUtils.isEmpty(filters) && !StringUtils.isEmpty(filters.get(FilterField.GOODS_STATUS))) {
  1383. // 如果明确指定了状态,则直接过滤批次(结果中不包括没有批次的 PCB)
  1384. status = filters.get(FilterField.GOODS_STATUS);
  1385. filter(status, SearchConstants.PCB_GOODS_GO_STATUS_FIELD, query);
  1386. } else {
  1387. // 如果未明确指定状态,则使用默认状态分情况进行过滤(结果中包括没有批次的 PCB)
  1388. status = Arrays.asList(TradeGoods.VALID_STATUS);
  1389. // 批次 id 不为空时,对状态过滤
  1390. Query goNullQuery = SearchUtils.getNullQuery(SearchConstants.PCB_GOODS_GO_ID_FIELD);
  1391. BooleanQuery q1 = new BooleanQuery();
  1392. q1.add(goNullQuery, Occur.MUST_NOT);
  1393. filter(status, SearchConstants.PCB_GOODS_GO_STATUS_FIELD, q1);
  1394. // 或者批次 id 为空(此时是PCB)
  1395. BooleanQuery q2 = new BooleanQuery();
  1396. q2.add(SearchUtils.getNullQuery(SearchConstants.PCB_GOODS_PCB_ID_FIELD), Occur.MUST_NOT);
  1397. q2.add(goNullQuery, Occur.MUST);
  1398. BooleanQuery booleanQuery = new BooleanQuery();
  1399. booleanQuery.add(q1, Occur.SHOULD);
  1400. booleanQuery.add(q2, Occur.SHOULD);
  1401. query.add(booleanQuery, Occur.FILTER);
  1402. }
  1403. if (CollectionUtils.isEmpty(filters)) {
  1404. return;
  1405. }
  1406. // 筛选类目
  1407. if (!StringUtils.isEmpty(filters.get(FilterField.GOODS_KINDID))) {
  1408. filter(filters.get(FilterField.GOODS_KINDID), SearchConstants.PCB_GOODS_KI_ID_FIELD, query);
  1409. }
  1410. // 筛选品牌
  1411. if (!StringUtils.isEmpty(filters.get(FilterField.GOODS_BRANDID))) {
  1412. filter(filters.get(FilterField.GOODS_BRANDID), SearchConstants.PCB_GOODS_BR_ID_FIELD, query);
  1413. }
  1414. // 筛选货币
  1415. if (!StringUtils.isEmpty(filters.get(FilterField.GOODS_CRNAME))) {
  1416. filter(filters.get(FilterField.GOODS_CRNAME), SearchConstants.PCB_GOODS_CRNAME_FIELD, query);
  1417. }
  1418. // 价格筛选
  1419. Object minPriceRmb = filters.get(FilterField.GOODS_MINPRICERMB);
  1420. Object maxPriceRmb = filters.get(FilterField.GOODS_MAXPRICERMB);
  1421. Object minPriceUsd = filters.get(FilterField.GOODS_MINPRICEUSD);
  1422. Object maxPriceUsd = filters.get(FilterField.GOODS_MAXPRICEUSD);
  1423. // 筛选人民币价格
  1424. if (!StringUtils.isEmpty(minPriceRmb) || !StringUtils.isEmpty(maxPriceRmb)) {
  1425. Double minPrice = null;
  1426. Double maxPrice = null;
  1427. if (!StringUtils.isEmpty(minPriceRmb)) {
  1428. minPrice = Double.valueOf(minPriceRmb.toString());
  1429. }
  1430. if (!StringUtils.isEmpty(maxPriceRmb)) {
  1431. maxPrice = Double.valueOf(maxPriceRmb.toString());
  1432. }
  1433. query.add(NumericRangeQuery.newDoubleRange(SearchConstants.PCB_GOODS_GO_MINPRICERMB_FIELD,
  1434. minPrice, maxPrice, true, true), BooleanClause.Occur.FILTER);
  1435. }
  1436. // 筛选美元价格
  1437. if (!StringUtils.isEmpty(minPriceUsd) || !StringUtils.isEmpty(maxPriceUsd)) {
  1438. Double minPrice = null;
  1439. Double maxPrice = null;
  1440. if (!StringUtils.isEmpty(minPriceUsd)) {
  1441. minPrice = Double.valueOf(minPriceUsd.toString());
  1442. }
  1443. if (!StringUtils.isEmpty(maxPriceUsd)) {
  1444. maxPrice = Double.valueOf(maxPriceUsd.toString());
  1445. }
  1446. query.add(NumericRangeQuery.newDoubleRange(SearchConstants.PCB_GOODS_GO_MINPRICEUSD_FIELD,
  1447. minPrice, maxPrice, true, true), BooleanClause.Occur.FILTER);
  1448. }
  1449. }
  1450. /**
  1451. * PCB 批次排序规则
  1452. */
  1453. private Sort sortPCBGoods(String keyword, com.uas.search.constant.model.Sort sort) {
  1454. List<SortField> sortFieldList = new ArrayList<>();
  1455. sortFieldList.add(SortField.FIELD_SCORE);
  1456. if (sort != null) {
  1457. com.uas.search.constant.model.Sort.Field field = sort.getField();
  1458. if (field == null) {
  1459. throw new IllegalArgumentException("排序字段不可为空:" + sort);
  1460. }
  1461. boolean reverse = sort.isReverse();
  1462. if (field == RESERVE) {
  1463. // 库存
  1464. sortFieldList.addAll(Arrays.asList(
  1465. // 降序时,默认值为最小值;升序时,默认值为最大值
  1466. sortField(SearchConstants.PCB_GOODS_GO_RESERVE_FIELD, Type.DOUBLE, reverse, reverse ? Double.MIN_VALUE : Double.MAX_VALUE),
  1467. sortField(SearchConstants.PCB_GOODS_GO_MINPRICERMB_FIELD, Type.DOUBLE, false, Double.MAX_VALUE),
  1468. sortField(SearchConstants.PCB_GOODS_GO_MINPRICEUSD_FIELD, Type.DOUBLE, false, Double.MAX_VALUE)
  1469. ));
  1470. } else if (field == PRICE) {
  1471. // 价格
  1472. sortFieldList.addAll(Arrays.asList(
  1473. // 降序时,默认值为最小值;升序时,默认值为最大值
  1474. sortField(SearchConstants.PCB_GOODS_GO_MINPRICERMB_FIELD, Type.DOUBLE, reverse, reverse ? Double.MIN_VALUE : Double.MAX_VALUE),
  1475. sortField(SearchConstants.PCB_GOODS_GO_MINPRICEUSD_FIELD, Type.DOUBLE, reverse, reverse ? Double.MIN_VALUE : Double.MAX_VALUE),
  1476. sortField(SearchConstants.PCB_GOODS_GO_RESERVE_FIELD, Type.DOUBLE, true, Double.MIN_VALUE)
  1477. ));
  1478. } else {
  1479. throw new IllegalArgumentException("不支持该排序方式:" + field);
  1480. }
  1481. } else {
  1482. // 默认(综合排序)
  1483. sortFieldList.addAll(Arrays.asList(
  1484. sortField(SearchConstants.PCB_GOODS_GO_MINPRICERMB_FIELD, Type.DOUBLE, false, Double.MAX_VALUE),
  1485. sortField(SearchConstants.PCB_GOODS_GO_MINPRICEUSD_FIELD, Type.DOUBLE, false, Double.MAX_VALUE),
  1486. sortField(SearchConstants.PCB_GOODS_GO_RESERVE_FIELD, Type.DOUBLE, true, Double.MIN_VALUE),
  1487. sortField(SearchConstants.PCB_GOODS_GO_MINDELIVERY_FIELD, Type.LONG, false, Long.MAX_VALUE)
  1488. ));
  1489. }
  1490. sortFieldList.addAll(Arrays.asList(
  1491. // 如果仍然无法得到正确结果,就根据按照型号等顺序严格排列
  1492. new SortField(SearchConstants.PCB_GOODS_PR_PCMPCODE_FIELD, new StringFieldComparatorSource(keyword, false)),
  1493. new SortField(SearchConstants.PCB_GOODS_BR_NAME_EN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(keyword, false)),
  1494. new SortField(SearchConstants.PCB_GOODS_BR_NAME_CN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(keyword, false)),
  1495. new SortField(SearchConstants.PCB_GOODS_KI_NAME_CN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(keyword, false)),
  1496. sortField(SearchConstants.PCB_GOODS_GO_UPDATE_DATE_FIELD, Type.LONG, true, Long.MIN_VALUE)
  1497. ));
  1498. SortField[] sortFields = new SortField[sortFieldList.size()];
  1499. sortFieldList.toArray(sortFields);
  1500. return new Sort(sortFields);
  1501. }
  1502. @Override
  1503. public List<Map<String, Object>> collectBySearchGoods(String keyword, CollectField collectedField,
  1504. Map<FilterField, Object> filters) throws IOException {
  1505. List<String> keywordFields = new ArrayList<>();
  1506. // 先根据品牌搜索,品牌不存在再搜索型号等
  1507. keywordFields.add(SearchConstants.GOODS_BR_NAME_CN_UNTOKENIZED_FIELD);
  1508. keywordFields.add(SearchConstants.GOODS_BR_NAME_EN_UNTOKENIZED_FIELD);
  1509. List<Map<String, Object>> result = collectBySearchGoods(keyword, keywordFields, false, collectedField, filters);
  1510. if (CollectionUtils.isEmpty(result)) {
  1511. keyword = recursivelyGetGoodsIds(keyword, null, true);
  1512. result = collectBySearchGoods(keyword, null, true, collectedField, filters);
  1513. }
  1514. return result;
  1515. }
  1516. /**
  1517. * 获取卖家信息
  1518. * @param keyword 关键词
  1519. * @param keywordFields 搜索字段
  1520. * @param page 页码
  1521. * @param size 页数
  1522. * @param tokenized 是否分词
  1523. * @param duplicate 去重类型
  1524. * @return
  1525. */
  1526. private Map<String, SPage<Object>> querySellers(String keyword, Map<String, List<String>> keywordFields, Integer page, Integer size, boolean tokenized, String duplicate) throws IOException {
  1527. if (SearchUtils.isKeywordInvalid(keyword)) {
  1528. throw new IllegalArgumentException("搜索关键词无效:" + keyword);
  1529. }
  1530. Map<String, SPage<Object>> map = new HashMap<>();
  1531. BooleanQuery goodsQuery = new BooleanQuery();
  1532. BooleanQuery productsQuery = new BooleanQuery();
  1533. // TODO 卖家排序
  1534. Sort sort = null;
  1535. // 未指定搜索的字段,则采用默认搜索逻辑
  1536. if (CollectionUtils.isEmpty(keywordFields)) {
  1537. Map<String, Query> boostQuerys = setSellersBoost(keyword);
  1538. } else {
  1539. BooleanQuery goodsBooleanQuery = new BooleanQuery();
  1540. for (String keywordField : keywordFields.get("goods")) {
  1541. if (!tokenized) {
  1542. goodsBooleanQuery.add(new TermQuery(new Term(keywordField, keyword.toLowerCase())), BooleanClause.Occur.SHOULD);
  1543. } else {
  1544. goodsBooleanQuery.add(SearchUtils.getBooleanQuery(keywordField, keyword), BooleanClause.Occur.SHOULD);
  1545. }
  1546. goodsQuery.add(goodsBooleanQuery, Occur.MUST);
  1547. }
  1548. // 现货卖家
  1549. map.put("stock", querySellers(SearchConstants.GOODS_TABLE_NAME, page, size, goodsQuery, sort, duplicate));
  1550. logger.info(goodsQuery.toString());
  1551. BooleanQuery productsBooleanQuery = new BooleanQuery();
  1552. for (String keywordField : keywordFields.get("products")) {
  1553. if (!tokenized) {
  1554. productsBooleanQuery.add(new TermQuery(new Term(keywordField, keyword.toLowerCase())), BooleanClause.Occur.SHOULD);
  1555. } else {
  1556. productsBooleanQuery.add(SearchUtils.getBooleanQuery(keywordField, keyword.toLowerCase()), BooleanClause.Occur.SHOULD);
  1557. }
  1558. productsQuery.add(productsBooleanQuery, Occur.MUST);
  1559. }
  1560. // 期货卖家
  1561. map.put("futures", querySellers(SearchConstants.PRODUCTS_PRIVATE_TABLE_NAME, page, size, productsQuery, sort, duplicate));
  1562. logger.info(productsQuery.toString());
  1563. }
  1564. return map;
  1565. }
  1566. private SPage<Object> querySellers(String indexName, Integer page, Integer size, Query query, Sort sort, String duplicateType) throws IOException {
  1567. SPage<Object> sPage = new SPage<>();
  1568. if (page != null && page > 0) {
  1569. sPage.setPage(page);
  1570. } else {
  1571. sPage.setPage(PAGE_INDEX);
  1572. sPage.setFirst(true);
  1573. }
  1574. size = size != null && size > 0 ? size : PAGE_SIZE;
  1575. sPage.setSize(DUPLICATE_PAGE_SIZE);
  1576. DuplicateFilter duplicateFilter = null;
  1577. if (!StringUtils.isEmpty(duplicateType)) {
  1578. if (duplicateType.equals(SearchConstants.BRAND)) {
  1579. duplicateFilter = new DuplicateFilter(indexName.equals(SearchConstants.GOODS_TABLE_NAME) ? SearchConstants.GOODS_PR_PBRAND_EN_CN_STUUID_UNTOKENIZED_FIELD : SearchConstants.PRODUCT_PRIVATE_PBRAND_ENUU_FIELD);
  1580. } else if (duplicateType.equals(SearchConstants.KIND)) {
  1581. duplicateFilter = new DuplicateFilter(indexName.equals(SearchConstants.GOODS_TABLE_NAME) ? SearchConstants.GOODS_KIND_STUUID_UNTOKENIZED_FIELD : SearchConstants.PRODUCT_PRIVATE_KIND_ENUU_FIELD);
  1582. }
  1583. }
  1584. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(indexName);
  1585. TopDocs topDocs;
  1586. try {
  1587. // 如果页码不为1
  1588. if (sPage.getPage() > 1) {
  1589. TopDocs previousTopDocs;
  1590. if (sort == null) {
  1591. previousTopDocs = duplicateFilter == null ? indexSearcher.search(query, (sPage.getPage() - 1) * sPage.getSize()) : indexSearcher.search(query, duplicateFilter, (sPage.getPage() - 1) * sPage.getSize());
  1592. } else {
  1593. previousTopDocs = indexSearcher.search(query, duplicateFilter, (sPage.getPage() - 1) * sPage.getSize(), sort);
  1594. }
  1595. int totalHits = previousTopDocs.totalHits;
  1596. ScoreDoc[] previousScoreDocs = previousTopDocs.scoreDocs;
  1597. if ((sPage.getPage() - 1) * sPage.getSize() >= totalHits) {
  1598. throw new IllegalArgumentException("页码过大:元素总数量为" + totalHits);
  1599. }
  1600. topDocs = sort == null ? indexSearcher.searchAfter(previousScoreDocs[previousScoreDocs.length - 1], query, sPage.getSize()) :
  1601. indexSearcher.searchAfter(previousScoreDocs[previousScoreDocs.length - 1], query, sPage.getSize(), sort);
  1602. } else {
  1603. sPage.setFirst(true);
  1604. if (sort == null) {
  1605. topDocs = duplicateFilter == null ? indexSearcher.search(query, sPage.getSize()) : indexSearcher.search(query, duplicateFilter, sPage.getSize());
  1606. } else {
  1607. topDocs = duplicateFilter == null ? indexSearcher.search(query, sPage.getSize(), sort) : indexSearcher.search(query, duplicateFilter, sPage.getSize(), sort);
  1608. }
  1609. }
  1610. int totalHits = topDocs.totalHits;
  1611. List<Document> documents = new ArrayList<>();
  1612. for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
  1613. documents.add(indexSearcher.doc(scoreDoc.doc));
  1614. }
  1615. List<Object> content = new ArrayList<>();
  1616. List<Object> results = new ArrayList<>();
  1617. if (indexName.equals(SearchConstants.GOODS_TABLE_NAME)) {
  1618. for (Document document : documents) {
  1619. results.add(document.get(SearchConstants.GOODS_ST_UUID_FIELD));
  1620. }
  1621. } else if (indexName.equals(SearchConstants.PRODUCTS_PRIVATE_TABLE_NAME)) {
  1622. for (Document document : documents) {
  1623. results.add(Long.parseLong(document.get(SearchConstants.PRODUCT_PRIVATE_ENUU_FIELD)));
  1624. }
  1625. }
  1626. Map<String, List<Object>> map = new HashMap<>();
  1627. // 结果去重
  1628. for (Object object : results) {
  1629. if (map.get(String.valueOf(object)) == null) {
  1630. List<Object> storeUuids = new ArrayList<>();
  1631. storeUuids.add(String.valueOf(object));
  1632. map.put(String.valueOf(object), storeUuids);
  1633. } else {
  1634. map.get(String.valueOf(object)).add(object);
  1635. totalHits --;
  1636. }
  1637. }
  1638. // 按批次、物料数量排序
  1639. List<Map.Entry<String, List<Object>>> stuuids = new ArrayList<>(map.entrySet());
  1640. Collections.sort(stuuids, new Comparator<Map.Entry<String, List<Object>>> () {
  1641. @Override
  1642. public int compare(Map.Entry<String, List<Object>> o1, Map.Entry<String, List<Object>> o2) {
  1643. return o1.getValue().size() - o2.getValue().size();
  1644. }
  1645. });
  1646. for (int i = 0; i < (stuuids.size() < size ? stuuids.size() : size) ; i ++) {
  1647. content.add(stuuids.get(i).getKey());
  1648. }
  1649. // 设置总元素个数、页数等信息
  1650. int totalPage = (int) Math.ceil(totalHits / (1.0 * sPage.getSize()));
  1651. sPage.setTotalPage(totalPage);
  1652. if (totalPage == sPage.getPage()) {
  1653. sPage.setLast(true);
  1654. }
  1655. sPage.setSize(size);
  1656. sPage.setTotalElement(totalHits);
  1657. sPage.setContent(content);
  1658. } finally {
  1659. releaseIndexSearcher(indexSearcher);
  1660. }
  1661. return sPage;
  1662. }
  1663. private Map<String, Query> setSellersBoost(String keyword) {
  1664. // TODO
  1665. Map<String, Query> queryMap = new HashMap<>();
  1666. BooleanQuery goodsQuery = new BooleanQuery();
  1667. BooleanQuery productsQuery = new BooleanQuery();
  1668. return null;
  1669. }
  1670. private SortField[] sortSellers(String keyword) {
  1671. // TODO
  1672. return null;
  1673. }
  1674. /**
  1675. * @param keyword
  1676. * @param keywordFields
  1677. * 要查询的字段
  1678. * @param tokenized
  1679. * 是否分词
  1680. * @param collectedField
  1681. * @param filters
  1682. * @return
  1683. */
  1684. private List<Map<String, Object>> collectBySearchGoods(String keyword, List<String> keywordFields,
  1685. Boolean tokenized, CollectField collectedField, Map<FilterField, Object> filters) {
  1686. if (collectedField == null) {
  1687. throw new IllegalArgumentException("参数为空:collectedField");
  1688. }
  1689. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.GOODS_TABLE_NAME);
  1690. List<Map<String, Object>> result = new ArrayList<>();
  1691. try {
  1692. BooleanQuery booleanQuery = queryGoods(keyword, keywordFields, tokenized);
  1693. setGoodsFilter(filters, booleanQuery);
  1694. logger.info(booleanQuery.toString());
  1695. // 统计
  1696. String uniqueField;
  1697. Set<String> fieldsToLoad = new HashSet<>();
  1698. switch (collectedField) {
  1699. case GOODS_KIND:
  1700. uniqueField = SearchConstants.GOODS_KI_ID_FIELD;
  1701. fieldsToLoad.add(SearchConstants.GOODS_KI_ID_FIELD);
  1702. fieldsToLoad.add(SearchConstants.GOODS_KI_NAME_CN_FIELD);
  1703. break;
  1704. case GOODS_BRAND:
  1705. uniqueField = SearchConstants.GOODS_BR_ID_FIELD;
  1706. fieldsToLoad.add(SearchConstants.GOODS_BR_ID_FIELD);
  1707. fieldsToLoad.add(SearchConstants.GOODS_BR_UUID_FIELD);
  1708. fieldsToLoad.add(SearchConstants.GOODS_BR_NAME_CN_FIELD);
  1709. fieldsToLoad.add(SearchConstants.GOODS_BR_NAME_EN_FIELD);
  1710. break;
  1711. case GOODS_STORE_TYPE:
  1712. uniqueField = SearchConstants.GOODS_ST_TYPE_FIELD;
  1713. fieldsToLoad.add(SearchConstants.GOODS_ST_TYPE_FIELD);
  1714. break;
  1715. case GOODS_CRNAME:
  1716. uniqueField = SearchConstants.GOODS_CRNAME_FIELD;
  1717. fieldsToLoad.add(SearchConstants.GOODS_CRNAME_FIELD);
  1718. break;
  1719. default:
  1720. throw new IllegalArgumentException("不支持该统计字段:" + collectedField);
  1721. }
  1722. GoodsGroupCollector collector = new GoodsGroupCollector(uniqueField, fieldsToLoad);
  1723. indexSearcher.search(booleanQuery, collector);
  1724. result = collector.getValues();
  1725. } catch (IOException e) {
  1726. logger.error("", e);
  1727. } finally {
  1728. releaseIndexSearcher(indexSearcher);
  1729. }
  1730. return result;
  1731. }
  1732. /**
  1733. * 获取查询批次的query
  1734. *
  1735. * @param keyword
  1736. * @param keywordFields
  1737. * @param tokenized
  1738. * @return
  1739. */
  1740. private BooleanQuery queryGoods(String keyword, List<String> keywordFields, Boolean tokenized) {
  1741. BooleanQuery booleanQuery = new BooleanQuery();
  1742. if (!SearchUtils.isKeywordInvalid(keyword)) {
  1743. // 未指定搜索的字段,则采用默认搜索逻辑
  1744. if (CollectionUtils.isEmpty(keywordFields)) {
  1745. booleanQuery.add(setGoodsBoost(keyword), BooleanClause.Occur.MUST);
  1746. } else {
  1747. BooleanQuery booleanQuery2 = new BooleanQuery();
  1748. for (String keywordField : keywordFields) {
  1749. // 是否分词
  1750. if (tokenized == null || !tokenized.booleanValue()) {
  1751. booleanQuery2.add(new TermQuery(new Term(keywordField, keyword.toLowerCase())), BooleanClause.Occur.SHOULD);
  1752. } else {
  1753. booleanQuery2.add(SearchUtils.getBooleanQuery(keywordField, keyword), BooleanClause.Occur.SHOULD);
  1754. }
  1755. }
  1756. booleanQuery.add(booleanQuery2, BooleanClause.Occur.MUST);
  1757. }
  1758. }
  1759. return booleanQuery;
  1760. }
  1761. private BooleanQuery queryProducts(String keyword, List<String> keywordFields, Boolean tokenized) {
  1762. BooleanQuery booleanQuery = new BooleanQuery();
  1763. if (!SearchUtils.isKeywordInvalid(keyword)) {
  1764. // 未指定搜索的字段,则采用默认搜索逻辑
  1765. if (CollectionUtils.isEmpty(keywordFields)) {
  1766. // booleanQuery.add(setGoodsBoost(keyword), BooleanClause.Occur.MUST);
  1767. } else {
  1768. BooleanQuery booleanQuery2 = new BooleanQuery();
  1769. for (String keywordField : keywordFields) {
  1770. // 是否分词
  1771. if (tokenized == null || !tokenized.booleanValue()) {
  1772. booleanQuery2.add(new TermQuery(new Term(keywordField, keyword.toLowerCase())), BooleanClause.Occur.SHOULD);
  1773. } else {
  1774. booleanQuery2.add(SearchUtils.getBooleanQuery(keywordField, keyword), BooleanClause.Occur.SHOULD);
  1775. }
  1776. }
  1777. booleanQuery.add(booleanQuery2, BooleanClause.Occur.MUST);
  1778. }
  1779. }
  1780. return booleanQuery;
  1781. }
  1782. /**
  1783. * 同时搜索器件、类目、品牌等,并设置boost
  1784. */
  1785. private Query setGoodsBoost(String keyword) {
  1786. BooleanQuery booleanQuery = new BooleanQuery();
  1787. // 前缀搜索(字段并未分词,进行分词搜索时,会有边界问题,如搜索 'BC807-40,215')
  1788. booleanQuery.add(new PrefixQuery(new Term(SearchConstants.GOODS_CMP_CODE_FIELD, keyword.toLowerCase())), BooleanClause.Occur.SHOULD);
  1789. booleanQuery.add(new PrefixQuery(new Term(SearchConstants.GOODS_PR_PCMPCODE_FIELD, keyword.toLowerCase())), BooleanClause.Occur.SHOULD);
  1790. // 原厂型号
  1791. booleanQuery.add(createQuery(SearchConstants.GOODS_CMP_CODE_FIELD, keyword, 100), BooleanClause.Occur.SHOULD);
  1792. // 非标
  1793. booleanQuery.add(createQuery(SearchConstants.GOODS_PR_PCMPCODE_FIELD, keyword, 100), Occur.SHOULD);
  1794. // 品牌
  1795. booleanQuery.add(createQuery(SearchConstants.GOODS_BR_NAME_CN_FIELD, keyword, 10), BooleanClause.Occur.SHOULD);
  1796. booleanQuery.add(createQuery(SearchConstants.GOODS_BR_NAME_EN_FIELD, keyword, 10), BooleanClause.Occur.SHOULD);
  1797. // 类目
  1798. booleanQuery.add(createQuery(SearchConstants.GOODS_KI_NAME_CN_FIELD, keyword, 1), BooleanClause.Occur.SHOULD);
  1799. // 属性值
  1800. booleanQuery.add(createQuery(SearchConstants.GOODS_CMP_DESCRIPTION_FIELD, keyword, 1), BooleanClause.Occur.SHOULD);
  1801. return booleanQuery;
  1802. }
  1803. @Override
  1804. public List<Map<String, Object>> collectBySearchPCBGoods(String keyword, CollectField collectedField,
  1805. Map<FilterField, Object> filters) throws IOException {
  1806. keyword = recursivelyGetPCBGoodsIds(keyword);
  1807. if (collectedField == null) {
  1808. throw new IllegalArgumentException("参数为空:collectedField");
  1809. }
  1810. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.PCB_GOODS_TABLE_NAME);
  1811. List<Map<String, Object>> result = new ArrayList<>();
  1812. try {
  1813. BooleanQuery booleanQuery = queryPCBGoods(keyword);
  1814. setPCBGoodsFilter(filters, booleanQuery);
  1815. logger.info(booleanQuery.toString());
  1816. // 统计
  1817. String uniqueField;
  1818. Set<String> fieldsToLoad = new HashSet<>();
  1819. switch (collectedField) {
  1820. case GOODS_KIND:
  1821. uniqueField = SearchConstants.PCB_GOODS_KI_ID_FIELD;
  1822. fieldsToLoad.add(SearchConstants.PCB_GOODS_KI_ID_FIELD);
  1823. fieldsToLoad.add(SearchConstants.PCB_GOODS_KI_NAME_CN_FIELD);
  1824. break;
  1825. case GOODS_BRAND:
  1826. uniqueField = SearchConstants.PCB_GOODS_BR_ID_FIELD;
  1827. fieldsToLoad.add(SearchConstants.PCB_GOODS_BR_ID_FIELD);
  1828. fieldsToLoad.add(SearchConstants.PCB_GOODS_BR_UUID_FIELD);
  1829. fieldsToLoad.add(SearchConstants.PCB_GOODS_BR_NAME_CN_FIELD);
  1830. fieldsToLoad.add(SearchConstants.PCB_GOODS_BR_NAME_EN_FIELD);
  1831. break;
  1832. case GOODS_CRNAME:
  1833. uniqueField = SearchConstants.PCB_GOODS_CRNAME_FIELD;
  1834. fieldsToLoad.add(SearchConstants.PCB_GOODS_CRNAME_FIELD);
  1835. break;
  1836. default:
  1837. throw new IllegalArgumentException("不支持该统计字段:" + collectedField);
  1838. }
  1839. GoodsGroupCollector collector = new GoodsGroupCollector(uniqueField, fieldsToLoad);
  1840. indexSearcher.search(booleanQuery, collector);
  1841. result = collector.getValues();
  1842. } catch (IOException e) {
  1843. logger.error("", e);
  1844. } finally {
  1845. releaseIndexSearcher(indexSearcher);
  1846. }
  1847. return result;
  1848. }
  1849. /**
  1850. * 获取查询 PCB 批次的query
  1851. *
  1852. * @param keyword
  1853. * @return
  1854. */
  1855. private BooleanQuery queryPCBGoods(String keyword) {
  1856. BooleanQuery booleanQuery = new BooleanQuery();
  1857. if (!SearchUtils.isKeywordInvalid(keyword)) {
  1858. booleanQuery.add(setPCBGoodsBoost(keyword), BooleanClause.Occur.MUST);
  1859. }
  1860. return booleanQuery;
  1861. }
  1862. /**
  1863. * 搜索 PCB,设置boost
  1864. */
  1865. private BooleanQuery setPCBGoodsBoost(String keyword) {
  1866. BooleanQuery booleanQuery = new BooleanQuery();
  1867. // 前缀搜索(字段并未分词,进行分词搜索时,会有边界问题,如搜索 'BC807-40,215')
  1868. booleanQuery.add(new PrefixQuery(new Term(SearchConstants.PCB_GOODS_PR_PCMPCODE_FIELD, keyword.toLowerCase())), BooleanClause.Occur.SHOULD);
  1869. // PCB
  1870. booleanQuery.add(createQuery(SearchConstants.PCB_GOODS_PR_PCMPCODE_FIELD, keyword, 100), Occur.SHOULD);
  1871. // 品牌
  1872. booleanQuery.add(createQuery(SearchConstants.PCB_GOODS_BR_NAME_CN_FIELD, keyword, 10), BooleanClause.Occur.SHOULD);
  1873. booleanQuery.add(createQuery(SearchConstants.PCB_GOODS_BR_NAME_EN_FIELD, keyword, 10), BooleanClause.Occur.SHOULD);
  1874. // 类目
  1875. booleanQuery.add(createQuery(SearchConstants.PCB_GOODS_KI_NAME_CN_FIELD, keyword, 1), BooleanClause.Occur.SHOULD);
  1876. return booleanQuery;
  1877. }
  1878. private BooleanQuery createQuery(String field, String keyword, float boost){
  1879. return createQuery(field, keyword, false, boost);
  1880. }
  1881. private BooleanQuery createQuery(String field, String keyword, boolean useRegexpQuery, float boost){
  1882. BooleanQuery booleanQuery = new BooleanQuery();
  1883. if (StringUtils.isEmpty(field) || StringUtils.isEmpty(keyword)) {
  1884. return booleanQuery;
  1885. }
  1886. // 根据空格分隔关键词,分隔的词取或的关系
  1887. String[] array = keyword.split(" ");
  1888. for(String str : array){
  1889. if(!StringUtils.isEmpty(str)){
  1890. booleanQuery.add(SearchUtils.getBooleanQuery(field, str, useRegexpQuery), Occur.SHOULD);
  1891. }
  1892. }
  1893. booleanQuery.setBoost(boost);
  1894. return booleanQuery;
  1895. }
  1896. /**
  1897. * 过滤
  1898. *
  1899. * @param list
  1900. * 过滤值列表
  1901. * @param field
  1902. * 过滤的字段
  1903. * @param booleanQuery
  1904. * 查询条件
  1905. */
  1906. @SuppressWarnings("unchecked")
  1907. private void filter(Object list, String field, BooleanQuery booleanQuery) {
  1908. List<Object> values;
  1909. if (list instanceof List) {
  1910. values = (List<Object>) list;
  1911. }else{
  1912. values = new ArrayList<>();
  1913. values.add(list);
  1914. }
  1915. BooleanQuery booleanQuery2 = new BooleanQuery();
  1916. for (Object value : values) {
  1917. TermQuery query = new TermQuery(new Term(field, value.toString().toLowerCase()));
  1918. booleanQuery2.add(query, BooleanClause.Occur.SHOULD);
  1919. }
  1920. booleanQuery.add(booleanQuery2, BooleanClause.Occur.FILTER);
  1921. }
  1922. @Override
  1923. public V_Products getProduct(Long id) throws IOException {
  1924. return DocumentToObjectUtils.toProduct(
  1925. SearchUtils.getDocumentById(SearchConstants.PRODUCTS_PRIVATE_TABLE_NAME, SearchConstants.PRODUCT_PRIVATE_ID_FIELD, id));
  1926. }
  1927. /**
  1928. * 获取标准型号联想词
  1929. *
  1930. * @param keyword 关键词
  1931. * @param size 尺寸
  1932. * @return
  1933. */
  1934. @Override
  1935. public List<Map<String, Object>> getSimilarPCmpCodes(String keyword, Integer size) {
  1936. size = size == null || size < 1 ? SIMILAR_NUM_EIGHT : size;
  1937. if (SearchUtils.isKeywordInvalid(keyword)) {
  1938. throw new IllegalArgumentException("输入无效:" + keyword);
  1939. }
  1940. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.PRODUCTS_PRIVATE_TABLE_NAME);
  1941. List<Map<String, Object>> products = new ArrayList<>();
  1942. try {
  1943. PrefixQuery prefixQuery = new PrefixQuery(
  1944. new Term(SearchConstants.PRODUCT_PRIVATE_PCMPCODE_FIELD, keyword.toLowerCase()));
  1945. logger.info(prefixQuery.toString());
  1946. Sort sort = new Sort(new SortField(SearchConstants.PRODUCT_PRIVATE_PCMPCODE_FIELD, new StringFieldComparatorSource(keyword)));
  1947. TopDocs hits = indexSearcher.search(prefixQuery, size, sort);
  1948. ScoreDoc[] scoreDocs = hits.scoreDocs;
  1949. for (ScoreDoc scoreDoc : scoreDocs) {
  1950. Set<String> fieldsToLoad = new HashSet<>();
  1951. fieldsToLoad.add(SearchConstants.PRODUCT_PRIVATE_PCMPCODE_FIELD);
  1952. Document document = indexSearcher.doc(scoreDoc.doc, fieldsToLoad);
  1953. Map<String, Object> map = new HashMap<>();
  1954. map.put("pCmpCode", document.get(SearchConstants.PRODUCT_PRIVATE_PCMPCODE_FIELD));
  1955. products.add(map);
  1956. }
  1957. } catch (IOException e) {
  1958. logger.error("", e);
  1959. } finally {
  1960. releaseIndexSearcher(indexSearcher);
  1961. }
  1962. return products;
  1963. }
  1964. /**
  1965. * 获取类目联想词
  1966. *
  1967. * @param keyword 关键词
  1968. * @param size 尺寸
  1969. * @return
  1970. */
  1971. @Override
  1972. public List<Map<String, Object>> getSimilarKind(String keyword, Integer size) {
  1973. size = size == null || size < 1 ? SIMILAR_NUM_EIGHT : size;
  1974. if (SearchUtils.isKeywordInvalid(keyword)) {
  1975. throw new IllegalArgumentException("输入无效:" + keyword);
  1976. }
  1977. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.PRODUCTS_PRIVATE_TABLE_NAME);
  1978. List<Map<String, Object>> products = new ArrayList<>();
  1979. try {
  1980. PrefixQuery prefixQuery = new PrefixQuery(
  1981. new Term(SearchConstants.PRODUCT_PRIVATE_KIND_FIELD, keyword.toLowerCase()));
  1982. logger.info(prefixQuery.toString());
  1983. Sort sort = new Sort(new SortField(SearchConstants.PRODUCT_PRIVATE_KIND_FIELD, new StringFieldComparatorSource(keyword)));
  1984. TopDocs hits = indexSearcher.search(prefixQuery, size, sort);
  1985. ScoreDoc[] scoreDocs = hits.scoreDocs;
  1986. for (ScoreDoc scoreDoc : scoreDocs) {
  1987. Set<String> fieldsToLoad = new HashSet<>();
  1988. fieldsToLoad.add(SearchConstants.PRODUCT_PRIVATE_KIND_FIELD);
  1989. Document document = indexSearcher.doc(scoreDoc.doc, fieldsToLoad);
  1990. Map<String, Object> map = new HashMap<>();
  1991. map.put("kind", document.get(SearchConstants.PRODUCT_PRIVATE_KIND_FIELD));
  1992. products.add(map);
  1993. }
  1994. } catch (IOException e) {
  1995. logger.error("", e);
  1996. } finally {
  1997. releaseIndexSearcher(indexSearcher);
  1998. }
  1999. return products;
  2000. }
  2001. @Override
  2002. public Kind getKind(Long id) throws IOException {
  2003. return DocumentToObjectUtils.toKind(
  2004. SearchUtils.getDocumentById(SearchConstants.KIND_TABLE_NAME, SearchConstants.KIND_ID_FIELD, id));
  2005. }
  2006. @Override
  2007. public Brand getBrand(Long id) throws IOException {
  2008. return DocumentToObjectUtils.toBrand(
  2009. SearchUtils.getDocumentById(SearchConstants.BRAND_TABLE_NAME, SearchConstants.BRAND_ID_FIELD, id));
  2010. }
  2011. @Override
  2012. public Component getComponent(Long id) throws IOException {
  2013. return DocumentToObjectUtils.toComponent(SearchUtils.getDocumentById(SearchConstants.COMPONENT_TABLE_NAME,
  2014. SearchConstants.COMPONENT_ID_FIELD, id));
  2015. }
  2016. @Override
  2017. public Goods getGoods(String id) throws IOException {
  2018. return DocumentToObjectUtils.toGoods(
  2019. SearchUtils.getDocumentById(SearchConstants.GOODS_TABLE_NAME, SearchConstants.GOODS_GO_ID_FIELD, id));
  2020. }
  2021. @Override
  2022. public PCBGoods getPCBGoods(String id) throws IOException {
  2023. return DocumentToObjectUtils.toPCBGoods(
  2024. SearchUtils.getDocumentById(SearchConstants.PCB_GOODS_TABLE_NAME, SearchConstants.PCB_GOODS_GO_ID_FIELD, id));
  2025. }
  2026. @Override
  2027. public SPage<Object> getObjects(String tableName, String keyword, String field, Boolean tokenized, @NotEmpty("page") Integer page, @NotEmpty("size") Integer size) throws IOException {
  2028. if (keyword == null) {
  2029. keyword = "";
  2030. }
  2031. if (field == null) {
  2032. field = SearchUtils.getIdField(tableName);
  2033. }
  2034. if (tokenized == null) {
  2035. tokenized = false;
  2036. }
  2037. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(tableName);
  2038. SPage<Object> sPage = new SPage<>();
  2039. try {
  2040. Query query;
  2041. if (tokenized) {
  2042. query = SearchUtils.getBooleanQuery(field, keyword);
  2043. } else {
  2044. query = new TermQuery(new Term(field, keyword));
  2045. }
  2046. // 分页信息
  2047. if (page > 0) {
  2048. sPage.setPage(page);
  2049. } else {
  2050. sPage.setPage(1);
  2051. sPage.setFirst(true);
  2052. }
  2053. if (size > 0) {
  2054. sPage.setSize(size);
  2055. } else {
  2056. sPage.setSize(20);
  2057. }
  2058. TopDocs topDocs;
  2059. // 如果页码不为1
  2060. if (sPage.getPage() > 1) {
  2061. TopDocs previousTopDocs = indexSearcher.search(query, (sPage.getPage() - 1) * sPage.getSize());
  2062. int totalHits = previousTopDocs.totalHits;
  2063. ScoreDoc[] previousScoreDocs = previousTopDocs.scoreDocs;
  2064. if ((sPage.getPage() - 1) * sPage.getSize() >= totalHits) {
  2065. throw new IllegalArgumentException("页码过大:元素总数量为" + totalHits);
  2066. }
  2067. topDocs = indexSearcher.searchAfter(previousScoreDocs[previousScoreDocs.length - 1], query,
  2068. sPage.getSize());
  2069. } else {
  2070. sPage.setFirst(true);
  2071. topDocs = indexSearcher.search(query, sPage.getSize());
  2072. }
  2073. int totalHits = topDocs.totalHits;
  2074. // 设置总元素个数、页数等信息
  2075. sPage.setTotalElement(totalHits);
  2076. int totalPage = (int) Math.ceil(totalHits / (1.0 * sPage.getSize()));
  2077. sPage.setTotalPage(totalPage);
  2078. if (totalPage == sPage.getPage()) {
  2079. sPage.setLast(true);
  2080. }
  2081. List<Object> content = new ArrayList<>();
  2082. for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
  2083. Document document = indexSearcher.doc(scoreDoc.doc);
  2084. content.add(DocumentToObjectUtils.toObject(document, tableName));
  2085. }
  2086. sPage.setContent(content);
  2087. } finally {
  2088. releaseIndexSearcher(indexSearcher);
  2089. }
  2090. return sPage;
  2091. }
  2092. }