SearchServiceImpl.java 136 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120
  1. package com.uas.search.service.impl;
  2. import static com.uas.search.constant.SearchConstants.GOODS_BR_NAME_CN_FIELD;
  3. import static com.uas.search.constant.SearchConstants.GOODS_BR_NAME_CN_UNTOKENIZED_FIELD;
  4. import static com.uas.search.constant.SearchConstants.GOODS_BR_NAME_EN_FIELD;
  5. import static com.uas.search.constant.SearchConstants.GOODS_BR_NAME_EN_UNTOKENIZED_FIELD;
  6. import static com.uas.search.constant.SearchConstants.GOODS_KI_NAME_CN_FIELD;
  7. import static com.uas.search.constant.SearchConstants.GOODS_KI_NAME_CN_UNTOKENIZED_FIELD;
  8. import static com.uas.search.constant.SearchConstants.GOODS_PR_PCMPCODE_FIELD;
  9. import static com.uas.search.constant.model.Sort.Field.PRICE;
  10. import static com.uas.search.constant.model.Sort.Field.RESERVE;
  11. import static com.uas.search.util.SearchUtils.getDocuments;
  12. import static com.uas.search.util.SearchUtils.releaseIndexSearcher;
  13. import com.alibaba.fastjson.JSONArray;
  14. import com.alibaba.fastjson.JSONObject;
  15. import com.uas.search.annotation.NotEmpty;
  16. import com.uas.search.constant.SearchConstants;
  17. import com.uas.search.constant.model.CollectField;
  18. import com.uas.search.constant.model.MultiValue;
  19. import com.uas.search.constant.model.PageParams;
  20. import com.uas.search.constant.model.PageParams.FilterField;
  21. import com.uas.search.constant.model.SPage;
  22. import com.uas.search.constant.model.SortPlus;
  23. import com.uas.search.exception.SearchException;
  24. import com.uas.search.grouping.DistinctGroupCollector;
  25. import com.uas.search.grouping.GoodsGroupCollector;
  26. import com.uas.search.model.Brand;
  27. import com.uas.search.model.Component;
  28. import com.uas.search.model.Goods;
  29. import com.uas.search.model.Kind;
  30. import com.uas.search.model.PCBGoods;
  31. import com.uas.search.model.TradeGoods;
  32. import com.uas.search.model.V_Products;
  33. import com.uas.search.service.SearchService;
  34. import com.uas.search.sort.StringFieldComparatorSource;
  35. import com.uas.search.util.ClassAndTableNameUtils;
  36. import com.uas.search.util.CollectionUtils;
  37. import com.uas.search.util.DocumentToObjectUtils;
  38. import com.uas.search.util.ObjectToDocumentUtils;
  39. import com.uas.search.util.SearchUtils;
  40. import com.uas.search.util.StringUtils;
  41. import java.io.IOException;
  42. import java.net.URLDecoder;
  43. import java.util.ArrayList;
  44. import java.util.Arrays;
  45. import java.util.Calendar;
  46. import java.util.Collections;
  47. import java.util.Comparator;
  48. import java.util.Date;
  49. import java.util.HashMap;
  50. import java.util.HashSet;
  51. import java.util.List;
  52. import java.util.Map;
  53. import java.util.Map.Entry;
  54. import java.util.Set;
  55. import java.util.stream.Collectors;
  56. import org.apache.lucene.document.Document;
  57. import org.apache.lucene.index.Term;
  58. import org.apache.lucene.sandbox.queries.DuplicateFilter;
  59. import org.apache.lucene.search.BooleanClause;
  60. import org.apache.lucene.search.BooleanClause.Occur;
  61. import org.apache.lucene.search.BooleanQuery;
  62. import org.apache.lucene.search.IndexSearcher;
  63. import org.apache.lucene.search.NumericRangeQuery;
  64. import org.apache.lucene.search.PrefixQuery;
  65. import org.apache.lucene.search.Query;
  66. import org.apache.lucene.search.ScoreDoc;
  67. import org.apache.lucene.search.Sort;
  68. import org.apache.lucene.search.SortField;
  69. import org.apache.lucene.search.SortField.Type;
  70. import org.apache.lucene.search.TermQuery;
  71. import org.apache.lucene.search.TopDocs;
  72. import org.apache.lucene.search.TotalHitCountCollector;
  73. import org.slf4j.Logger;
  74. import org.slf4j.LoggerFactory;
  75. import org.springframework.stereotype.Service;
  76. /**
  77. * 搜索索引
  78. *
  79. * @author sunyj
  80. * @since 2016年8月5日 下午2:21:26
  81. */
  82. @Service
  83. public class SearchServiceImpl implements SearchService {
  84. /**
  85. * 获取联想词时返回的最大数目
  86. */
  87. private static final int SIMILAR_NUM = 20;
  88. /**
  89. * 获取联想词时返回的最大数目
  90. */
  91. private static final int SIMILAR_NUM_EIGHT = 8;
  92. /**
  93. * 所有物料
  94. */
  95. private static final String ALL_PRODUCTS = "all";
  96. /**
  97. * 标准物料
  98. */
  99. private static final String STANDARD_PRODUCTS = "standard";
  100. /**
  101. * 非标物料
  102. */
  103. private static final String NONSTANDARD_PRODUCTS = "nStandard";
  104. /**
  105. * 默认的页码
  106. */
  107. private static final int PAGE_INDEX = 1;
  108. /**
  109. * 默认每页的大小
  110. */
  111. private static final int PAGE_SIZE = 20;
  112. /**
  113. * 品牌、类目分词可能重复,取前2000个
  114. */
  115. private static final int DUPLICATE_PAGE_SIZE = 2000;
  116. private static Logger logger = LoggerFactory.getLogger(SearchServiceImpl.class);
  117. /**
  118. * 查询所有物料
  119. *
  120. * @param keyword 关键词
  121. * @param page 页码
  122. * @param size 尺寸
  123. * @param enUU 企业UU
  124. * @param type all 全部 standard 标准 nStandard 非标
  125. * @return idPage
  126. * @throws IOException 输入异常
  127. */
  128. @Override
  129. public SPage<Long> getProductIds(Long enUU, String keyword, Integer page, Integer size, String type, Boolean duplicate) throws IOException {
  130. List<Long> ids = new ArrayList<>();
  131. SPage<Document> documents = getProductDocuments(enUU, keyword, page, size, type, duplicate);
  132. SPage<Long> sPage = new SPage<>(documents.getTotalPage(), documents.getTotalElement(), documents.getPage(),
  133. documents.getSize(), documents.isFirst(), documents.isLast());
  134. for (Document document : documents.getContent()) {
  135. ids.add(Long.parseLong(document.get(SearchConstants.PRODUCT_PRIVATE_ID_FIELD)));
  136. }
  137. sPage.setContent(ids);
  138. return sPage;
  139. }
  140. /**
  141. * 查询所有物料(B2B)
  142. *
  143. * @return idPage
  144. * @throws IOException 输入异常
  145. */
  146. @Override
  147. public SPage<Long> getProductIdsB2B(String keyword, String tableName, Integer page, Integer size, String keywordSearchColumns, String filters, String multiValueField, String sort, String notEqualFilters, String multiValueNotField) throws IOException {
  148. Table_name tbName = null;
  149. if (!StringUtils.isEmpty(tableName)) {
  150. tbName = Table_name.valueOf(tableName.toUpperCase());
  151. }
  152. PageParams pageParams = new PageParams();
  153. if (page != null) {
  154. pageParams.setPage(page);
  155. }
  156. if (size != null) {
  157. pageParams.setSize(size);
  158. }
  159. List<String> keywordSearchColumnsList = null;
  160. if (!StringUtils.isEmpty(keywordSearchColumns)) {
  161. String[] strs = keywordSearchColumns.split(",");
  162. keywordSearchColumnsList = new ArrayList<>();
  163. for (String str : strs) {
  164. keywordSearchColumnsList.add(str);
  165. }
  166. }
  167. pageParams.setKeywordSearchColumns(keywordSearchColumnsList);
  168. Map<String, Object> filtersMap = new HashMap<>();
  169. if (!StringUtils.isEmpty(filters)) {
  170. filtersMap.putAll(JSONObject.parseObject(filters));
  171. }
  172. if (!StringUtils.isEmpty(multiValueField)) {
  173. JSONArray jsonArray = JSONObject.parseArray(multiValueField);
  174. for (int i = 0; i < jsonArray.size(); i++) {
  175. JSONObject jsonObject = jsonArray.getJSONObject(i);
  176. String field = jsonObject.getString("field");
  177. MultiValue multiValue = jsonObject.getObject("multiValue", MultiValue.class);
  178. filtersMap.put(field, multiValue);
  179. }
  180. }
  181. if (!StringUtils.isEmpty(sort)) {
  182. filtersMap.put(SearchConstants.SORT_KEY, JSONObject.parseArray(sort, Sort.class));
  183. }
  184. if (!CollectionUtils.isEmpty(filtersMap)) {
  185. pageParams.setCommonFilters(filtersMap);
  186. }
  187. Map<String, Object> notEqualFiltersMap = new HashMap<>();
  188. if (!StringUtils.isEmpty(notEqualFilters)) {
  189. notEqualFiltersMap.putAll(JSONObject.parseObject(notEqualFilters));
  190. }
  191. if (!StringUtils.isEmpty(multiValueNotField)) {
  192. JSONArray jsonArray = JSONObject.parseArray(multiValueNotField);
  193. for (int i = 0; i < jsonArray.size(); i++) {
  194. JSONObject jsonObject = jsonArray.getJSONObject(i);
  195. String field = jsonObject.getString("field");
  196. MultiValue multiValue = jsonObject.getObject("multiValue", MultiValue.class);
  197. notEqualFiltersMap.put(field, multiValue);
  198. }
  199. }
  200. if (!CollectionUtils.isEmpty(notEqualFiltersMap)) {
  201. pageParams.setNotEqualFilters(notEqualFiltersMap);
  202. }
  203. return searchIds(keyword, tbName, pageParams);
  204. }
  205. @Override
  206. public SPage<Long> searchIds(String keyword, Table_name tableName, PageParams pageParams) throws SearchException {
  207. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(tableName.value());
  208. // 获取单据的id
  209. List<Long> content = new ArrayList<>();
  210. try {
  211. // 获取该表keyword可以搜索的域
  212. List<String> keywordFields = null;
  213. // 若未指定,再获取实体类中所配置的
  214. if (pageParams != null && !CollectionUtils.isEmpty(pageParams.getKeywordSearchColumns())) {
  215. keywordFields = getKeywordFields(pageParams.getKeywordSearchColumns());
  216. } else {
  217. keywordFields = ClassAndTableNameUtils.getKeywordFields(tableName);
  218. }
  219. SPage<ScoreDoc> scoreDocPage = search(indexSearcher, keyword, tableName, keywordFields, true, pageParams);
  220. SPage<Long> sPage = convertSPage(scoreDocPage, Long.class);
  221. for (ScoreDoc scoreDoc : scoreDocPage.getContent()) {
  222. Document document = indexSearcher.doc(scoreDoc.doc);
  223. content.add(Long.valueOf(document.get(ClassAndTableNameUtils.getIdField(tableName))));
  224. }
  225. sPage.setContent(content);
  226. return sPage;
  227. } catch (NumberFormatException | IOException e) {
  228. throw new SearchException(e).setDetailedMessage(e);
  229. } finally {
  230. SearchUtils.releaseIndexSearcher(indexSearcher);
  231. }
  232. }
  233. private SPage<ScoreDoc> search(IndexSearcher indexSearcher, String keyword, Table_name tableName,
  234. List<String> keywordFields, Boolean tokenized, PageParams pageParams) throws IOException {
  235. if (CollectionUtils.isEmpty(keywordFields)) {
  236. throw new IllegalArgumentException("keywordFields不可为空");
  237. }
  238. SPage<ScoreDoc> sPage = new SPage<>();
  239. BooleanQuery booleanQuery = new BooleanQuery();
  240. Sort sort = null;
  241. if (keyword == null) {
  242. keyword = "";
  243. }
  244. // 关键词无效,即搜索所有的数据
  245. // 关键词带空格,进行与操作
  246. String[] strs = keyword.split(" ");
  247. for (String str : strs) {
  248. // keyword可能是哪些域,域之间进行或操作
  249. BooleanQuery booleanQuery2 = new BooleanQuery();
  250. for (String keywordField : keywordFields) {
  251. booleanQuery2.add(SearchUtils.regexpQuery(keywordField, str, tokenized), BooleanClause.Occur.SHOULD);
  252. }
  253. booleanQuery.add(booleanQuery2, BooleanClause.Occur.MUST);
  254. }
  255. // 分页信息
  256. if (pageParams == null) {
  257. sPage.setPage(PAGE_INDEX);
  258. sPage.setSize(PAGE_SIZE);
  259. sPage.setFirst(true);
  260. } else {
  261. int page = pageParams.getPage();
  262. if (page > 0) {
  263. sPage.setPage(page);
  264. } else {
  265. sPage.setPage(PAGE_INDEX);
  266. sPage.setFirst(true);
  267. }
  268. int size = pageParams.getSize();
  269. if (size > 0) {
  270. sPage.setSize(size);
  271. } else {
  272. sPage.setSize(PAGE_SIZE);
  273. }
  274. Map<String, Object> filters = pageParams.getCommonFilters();
  275. if (!CollectionUtils.isEmpty(filters)) {
  276. // 如果filter中开始时间和截止时间至少传了一个参数,说明需要对时间范围进行限定
  277. if (!StringUtils.isEmpty(filters.get(SearchConstants.FROM_DATE_KEY))
  278. || !StringUtils.isEmpty(filters.get(SearchConstants.END_DATE_KEY))) {
  279. // 开始时间初始化为1990.1.1
  280. Calendar calendar = Calendar.getInstance();
  281. calendar.set(1990, 0, 1);
  282. // 截止时间初始化为现在
  283. long fromTime = calendar.getTimeInMillis();
  284. long endTime = new Date().getTime();
  285. if (!StringUtils.isEmpty(filters.get(SearchConstants.FROM_DATE_KEY))) {
  286. fromTime = Long.parseLong(
  287. filters.remove(SearchConstants.FROM_DATE_KEY).toString());
  288. }
  289. if (!StringUtils.isEmpty(filters.get(SearchConstants.END_DATE_KEY))) {
  290. endTime = Long.parseLong(
  291. filters.remove(SearchConstants.END_DATE_KEY).toString());
  292. }
  293. String dateField = ClassAndTableNameUtils.getDateField(tableName);
  294. if (!StringUtils.isEmpty(dateField)) {
  295. booleanQuery.add(NumericRangeQuery.newLongRange(dateField, fromTime, endTime, true, true), BooleanClause.Occur.MUST);
  296. }
  297. }
  298. // 如果需要排序
  299. if (!StringUtils.isEmpty(filters.get(SearchConstants.SORT_KEY))) {
  300. Object sortObject = filters.remove(SearchConstants.SORT_KEY);
  301. List<SortPlus> sortList = null;
  302. if (sortObject instanceof ArrayList) {
  303. sortList = (List<SortPlus>) sortObject;
  304. }
  305. boolean sortFieldsIsNull = true;
  306. if (!CollectionUtils.isEmpty(sortList)) {
  307. SortField[] sortFields = new SortField[sortList.size()];
  308. for (int i = 0; i < sortList.size(); i++) {
  309. try {
  310. SortPlus s = sortList.get(i);
  311. SortField sortField = new SortField(s.getField(), getType(s), !s.isReverse());
  312. if (s.getMissingValue() != null) {
  313. sortField.setMissingValue(s.getMissingValue());
  314. }
  315. sortFields[i] = sortField;
  316. sortFieldsIsNull = true;
  317. } catch (ClassCastException e) {
  318. continue;
  319. }
  320. }
  321. sort = sortFieldsIsNull ? null : new Sort(sortFields);
  322. }
  323. }
  324. // 其他过滤条件,键即为相应的数据库字段名(field名)
  325. Set<Entry<String, Object>> entrySet = filters.entrySet();
  326. for (Entry<String, Object> entry : entrySet) {
  327. String key = entry.getKey();
  328. Object value = entry.getValue();
  329. // 拼接索引域名
  330. String field = key;
  331. /*
  332. * 对于明细、企业等所关联的表的数据,是以json格式存储的,搜索时可能会对明细等表中多个字段进行过滤,
  333. * 但是键都是明细等表在主表中所对应的字段名称,为了实现这样的过滤功能,需要将多个值放在com.uas.search.
  334. * b2b.model.MultiValue中, 作为filters中key-value的value传递过来
  335. */
  336. if (value instanceof MultiValue) {
  337. MultiValue multiValue = (MultiValue) value;
  338. List<Object> values = multiValue.getValues();
  339. Occur occur = multiValue.isOr() ? Occur.SHOULD : Occur.MUST;
  340. BooleanQuery booleanQuery2 = new BooleanQuery();
  341. for (Object object : values) {
  342. booleanQuery2.add(filterIgnoreCase(field, String.valueOf(object)), occur);
  343. }
  344. booleanQuery.add(booleanQuery2, Occur.FILTER);
  345. } else {
  346. if (value != null) {
  347. booleanQuery.add(filterIgnoreCase(field, String.valueOf(value)), Occur.FILTER);
  348. }
  349. }
  350. }
  351. }
  352. // 排除过滤
  353. Map<String, Object> notEqualFilters = pageParams.getNotEqualFilters();
  354. if (!CollectionUtils.isEmpty(notEqualFilters)) {
  355. Set<Entry<String, Object>> entrySet = notEqualFilters.entrySet();
  356. for (Entry<String, Object> entry : entrySet) {
  357. String key = entry.getKey();
  358. Object value = entry.getValue();
  359. String field = key;
  360. // 排除多个值
  361. if (value instanceof MultiValue) {
  362. MultiValue multiValue = (MultiValue) value;
  363. List<Object> values = multiValue.getValues();
  364. Occur occur = multiValue.isOr() ? Occur.SHOULD : Occur.MUST;
  365. BooleanQuery booleanQuery2 = new BooleanQuery();
  366. for (Object object : values) {
  367. booleanQuery2.add(SearchUtils.regexpQuery(field, String.valueOf(object), false), occur);
  368. }
  369. booleanQuery.add(booleanQuery2, Occur.MUST_NOT);
  370. } else {
  371. if (value != null) {
  372. booleanQuery.add(SearchUtils.regexpQuery(field, String.valueOf(value), false),
  373. Occur.MUST_NOT);
  374. }
  375. }
  376. }
  377. }
  378. }
  379. logger.info(booleanQuery.toString());
  380. TopDocs topDocs;
  381. // 如果页码不为1
  382. if (sPage.getPage() > 1) {
  383. TopDocs previousTopDocs = null;
  384. if (sort != null) {
  385. previousTopDocs = indexSearcher.search(booleanQuery, (sPage.getPage() - 1) * sPage.getSize(), sort);
  386. } else {
  387. previousTopDocs = indexSearcher.search(booleanQuery, (sPage.getPage() - 1) * sPage.getSize());
  388. }
  389. int totalHits = previousTopDocs.totalHits;
  390. ScoreDoc[] previousScoreDocs = previousTopDocs.scoreDocs;
  391. // 搜索结果为空时,返回空集
  392. if (totalHits == 0) {
  393. sPage.setTotalElement(totalHits);
  394. int totalPage = (int) Math.ceil(totalHits / (1.0 * sPage.getSize()));
  395. sPage.setTotalPage(totalPage);
  396. if (totalPage == sPage.getPage()) {
  397. sPage.setLast(true);
  398. }
  399. sPage.setContent(new ArrayList<ScoreDoc>());
  400. return sPage;
  401. }
  402. // 超出页码数时,返回第1页数据
  403. if ((sPage.getPage() - 1) * sPage.getSize() >= totalHits) {
  404. sPage.setPage(1);
  405. sPage.setFirst(true);
  406. if (sort != null) {
  407. topDocs = indexSearcher.search(booleanQuery, sPage.getSize(), sort);
  408. } else {
  409. topDocs = indexSearcher.search(booleanQuery, sPage.getSize());
  410. }
  411. }
  412. if (sort != null && sPage.getPage() > 1) {
  413. topDocs = indexSearcher.searchAfter(previousScoreDocs[previousScoreDocs.length - 1], booleanQuery,
  414. sPage.getSize(), sort);
  415. } else {
  416. topDocs = indexSearcher.searchAfter(previousScoreDocs[previousScoreDocs.length - 1], booleanQuery,
  417. sPage.getSize());
  418. }
  419. } else {
  420. sPage.setFirst(true);
  421. if (sort != null) {
  422. topDocs = indexSearcher.search(booleanQuery, sPage.getSize(), sort);
  423. } else {
  424. topDocs = indexSearcher.search(booleanQuery, sPage.getSize());
  425. }
  426. }
  427. int totalHits = topDocs.totalHits;
  428. // 设置总元素个数、页数等信息
  429. sPage.setTotalElement(totalHits);
  430. int totalPage = (int) Math.ceil(totalHits / (1.0 * sPage.getSize()));
  431. sPage.setTotalPage(totalPage);
  432. if (totalPage == sPage.getPage()) {
  433. sPage.setLast(true);
  434. }
  435. sPage.setContent(Arrays.asList(topDocs.scoreDocs));
  436. return sPage;
  437. }
  438. /**
  439. * 获取排序字段的类型
  440. *
  441. * @param s
  442. * @return
  443. */
  444. private SortField.Type getType(SortPlus s) {
  445. if (s == null) {
  446. return null;
  447. }
  448. SortPlus.Type type = s.getType();
  449. switch (type) {
  450. case STRING:
  451. return SortField.Type.STRING;
  452. case INT:
  453. return SortField.Type.INT;
  454. case FLOAT:
  455. return SortField.Type.FLOAT;
  456. case LONG:
  457. return SortField.Type.LONG;
  458. case DOUBLE:
  459. return SortField.Type.DOUBLE;
  460. default:
  461. throw new SearchException("排序参数错误:" + type);
  462. }
  463. }
  464. /**
  465. * @return 过滤时不区分大小写
  466. */
  467. private Query filterIgnoreCase(String field, String keyword){
  468. BooleanQuery booleanQuery = new BooleanQuery();
  469. booleanQuery.add(SearchUtils.regexpQuery(field, keyword, false), Occur.SHOULD);
  470. booleanQuery.add(SearchUtils.regexpQuery(field, keyword.toLowerCase(), false), Occur.SHOULD);
  471. return booleanQuery;
  472. }
  473. /**
  474. * 转换SPage
  475. *
  476. * @param scoreDocPage
  477. * @param clazz
  478. * @return
  479. */
  480. private <T> SPage<T> convertSPage(SPage<ScoreDoc> scoreDocPage, Class<T> clazz) {
  481. return new SPage<>(scoreDocPage.getTotalPage(), scoreDocPage.getTotalElement(), scoreDocPage.getPage(),
  482. scoreDocPage.getSize(), scoreDocPage.isFirst(), scoreDocPage.isLast());
  483. }
  484. /**
  485. * 获取指定的可以搜索的字段列名
  486. * @param keywordSearchColumns
  487. * keyword模糊搜索的字段名称
  488. * @return 可以搜索的字段列名
  489. */
  490. private List<String> getKeywordFields(List<String> keywordSearchColumns) {
  491. List<String> result = new ArrayList<>();
  492. for (String keywordSearchColumn : keywordSearchColumns) {
  493. // 返回之前先拼接索引域名
  494. result.add(keywordSearchColumn);
  495. }
  496. return result;
  497. }
  498. private SPage<Document> getProductDocuments(Long enUU, String keyword, Integer page, Integer size, String type, Boolean duplicate) throws IOException {
  499. // if (SearchUtils.isKeywordInvalid(keyword)) {
  500. // throw new IllegalArgumentException("搜索关键词无效:" + keyword);
  501. // }
  502. BooleanQuery q1 = new BooleanQuery();
  503. if (null != enUU) {
  504. q1.add(new TermQuery(new Term(SearchConstants.PRODUCT_PRIVATE_ENUU_FIELD, String.valueOf(enUU))), BooleanClause.Occur.MUST);
  505. }
  506. if (STANDARD_PRODUCTS.equals(type)) {
  507. q1.add(new TermQuery(new Term(SearchConstants.PRODUCT_PRIVATE_STANDARD_FIELD, String.valueOf(1))), BooleanClause.Occur.MUST);
  508. } else if (NONSTANDARD_PRODUCTS.equals(type)) {
  509. q1.add(new TermQuery(new Term(SearchConstants.PRODUCT_PRIVATE_STANDARD_FIELD, String.valueOf(0))), BooleanClause.Occur.MUST);
  510. }
  511. q1.add(new TermQuery(new Term(SearchConstants.PRODUCT_PRIVATE_B2CENABLED_FIELD, String.valueOf(1))), BooleanClause.Occur.MUST);
  512. BooleanQuery booleanQuery = new BooleanQuery();
  513. booleanQuery.add(q1, BooleanClause.Occur.MUST);
  514. if (!StringUtils.isEmpty(keyword)) {
  515. BooleanQuery q2 = new BooleanQuery();
  516. if (!duplicate) {
  517. q2.add(createQuery(SearchConstants.PRODUCT_PRIVATE_KIND_FIELD, keyword.toLowerCase(), true,1), BooleanClause.Occur.SHOULD);
  518. q2.add(createQuery(SearchConstants.PRODUCT_PRIVATE_PBRANDEN_FIELD, keyword.toLowerCase(), true,1), BooleanClause.Occur.SHOULD);
  519. }
  520. q2.add(createQuery(SearchConstants.PRODUCT_PRIVATE_PCMPCODE_FIELD, keyword.toLowerCase(), true,1), BooleanClause.Occur.SHOULD);
  521. booleanQuery.add(q2, BooleanClause.Occur.MUST);
  522. }
  523. if (Boolean.valueOf(duplicate)) {
  524. DuplicateFilter duplicateFilter = new DuplicateFilter(SearchConstants.PRODUCT_PRIVATE_PCMPCODE_FIELD);
  525. logger.info(booleanQuery.toString());
  526. return getDocuments(SearchConstants.PRODUCTS_PRIVATE_TABLE_NAME, booleanQuery, new Sort(sortProduct()), page, size, duplicateFilter);
  527. } else {
  528. logger.info(booleanQuery.toString());
  529. return getDocuments(SearchConstants.PRODUCTS_PRIVATE_TABLE_NAME, booleanQuery, new Sort(sortProduct()), page, size);
  530. }
  531. }
  532. /**
  533. * @return 物料排序规则
  534. */
  535. private SortField[] sortProduct() {
  536. List<SortField> sortFieldList = new ArrayList<>();
  537. // id
  538. sortFieldList.add(new SortField(SearchConstants.PRODUCT_PRIVATE_ID_FIELD, Type.LONG, true));
  539. sortFieldList.add(new SortField(SearchConstants.PRODUCT_PRIVATE_ATTACH_FIELD, Type.DOUBLE, true));
  540. SortField[] sortFields = new SortField[sortFieldList.size()];
  541. sortFieldList.toArray(sortFields);
  542. return sortFields;
  543. }
  544. @Override
  545. public SPage<Long> getKindIds(String keyword, Integer page, Integer size) throws IOException {
  546. List<Long> ids = new ArrayList<>();
  547. SPage<Document> documents = getKindDocuments(keyword, page, size);;
  548. SPage<Long> sPage = new SPage<>(documents.getTotalPage(), documents.getTotalElement(), documents.getPage(),
  549. documents.getSize(), documents.isFirst(), documents.isLast());
  550. for (Document document : documents.getContent()) {
  551. ids.add(Long.parseLong(document.get(SearchConstants.KIND_ID_FIELD)));
  552. }
  553. sPage.setContent(ids);
  554. return sPage;
  555. }
  556. @Override
  557. public SPage<Map<String, Object>> getKinds(String keyword, Integer page, Integer size) throws IOException {
  558. List<Map<String, Object>> kinds = new ArrayList<>();
  559. SPage<Document> documents = getKindDocuments(keyword, page, size);
  560. SPage<Map<String, Object>> sPage = new SPage<>(documents.getTotalPage(),
  561. documents.getTotalElement(), documents.getPage(), documents.getSize(), documents.isFirst(),
  562. documents.isLast());
  563. for (Document document : documents.getContent()) {
  564. Map<String, Object> kind = new HashMap<>();
  565. kind.put("id", Long.parseLong(document.get(SearchConstants.KIND_ID_FIELD)));
  566. kind.put("nameCn", document.get(SearchConstants.KIND_NAMECN_FIELD));
  567. kinds.add(kind);
  568. }
  569. sPage.setContent(kinds);
  570. return sPage;
  571. }
  572. private SPage<Document> getKindDocuments(String keyword, Integer page, Integer size) throws IOException {
  573. if (SearchUtils.isKeywordInvalid(keyword)) {
  574. throw new IllegalArgumentException("搜索关键词无效:" + keyword);
  575. }
  576. BooleanQuery booleanQuery = SearchUtils.getBooleanQuery(SearchConstants.KIND_NAMECN_FIELD, keyword);
  577. logger.info(booleanQuery.toString());
  578. return getDocuments(SearchConstants.KIND_TABLE_NAME, booleanQuery, new Sort(sortKind(keyword)), page, size);
  579. }
  580. /**
  581. * @return 类目排序规则
  582. */
  583. private SortField[] sortKind(String keyword) {
  584. // 分数 > 访问量 > 搜索次数
  585. return new SortField[]{
  586. sortField(SearchConstants.KIND_VISIT_COUNT_FIELD, Type.LONG, true, Long.MIN_VALUE),
  587. sortField(SearchConstants.KIND_SEARCH_COUNT_FIELD, Type.LONG, true, Long.MIN_VALUE),
  588. new SortField(SearchConstants.KIND_NAMECN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(keyword))
  589. };
  590. }
  591. /**
  592. * 构造 SortField
  593. *
  594. * @param field Name of field to sort by. Can be <code>null</code> if
  595. * <code>type</code> is SCORE or DOC.
  596. * @param type Type of values in the terms.
  597. * @param reverse True if natural order should be reversed.
  598. * @param missingValue Used for 'sortMissingFirst/Last'
  599. * @return SortField
  600. */
  601. private SortField sortField(String field, Type type, boolean reverse, Object missingValue) {
  602. SortField sortField = new SortField(field, type, reverse);
  603. sortField.setMissingValue(missingValue);
  604. return sortField;
  605. }
  606. @Override
  607. public SPage<Long> getBrandIds(String keyword, Integer page, Integer size) throws IOException {
  608. List<Long> ids = new ArrayList<>();
  609. SPage<Document> documents = getBrandDocuments(keyword, page, size);
  610. SPage<Long> sPage = new SPage<>(documents.getTotalPage(), documents.getTotalElement(), documents.getPage(),
  611. documents.getSize(), documents.isFirst(), documents.isLast());
  612. for (Document document : documents.getContent()) {
  613. ids.add(Long.parseLong(document.get(SearchConstants.BRAND_ID_FIELD)));
  614. }
  615. sPage.setContent(ids);
  616. return sPage;
  617. }
  618. @Override
  619. public SPage<Map<String, Object>> getBrands(String keyword, Integer page, Integer size) throws IOException {
  620. List<Map<String, Object>> brands = new ArrayList<>();
  621. SPage<Document> documents = getBrandDocuments(keyword, page, size);
  622. SPage<Map<String, Object>> sPage = new SPage<Map<String, Object>>(documents.getTotalPage(),
  623. documents.getTotalElement(), documents.getPage(), documents.getSize(), documents.isFirst(),
  624. documents.isLast());
  625. for (Document document : documents.getContent()) {
  626. Map<String, Object> brand = new HashMap<String, Object>();
  627. brand.put("id", Long.parseLong(document.get(SearchConstants.BRAND_ID_FIELD)));
  628. brand.put("uuid", document.get(SearchConstants.BRAND_UUID_FIELD));
  629. brand.put("nameCn", document.get(SearchConstants.BRAND_NAMECN_FIELD));
  630. brand.put("nameEn", document.get(SearchConstants.BRAND_NAMEEN_FIELD));
  631. brands.add(brand);
  632. }
  633. sPage.setContent(brands);
  634. return sPage;
  635. }
  636. @Override
  637. public SPage<Map<String, Object>> getBrandsAndSellers(String keyword, Integer page, Integer size)
  638. throws IOException {
  639. List<Map<String, Object>> content = new ArrayList<>();
  640. Map<String, Object> map = new HashMap<>();
  641. List<Long> brandIds = new ArrayList<>();
  642. SPage<Object> stock = null;
  643. SPage<Object> futures = null;
  644. // 精准匹配
  645. SPage<Long> documents = getBrandIds(keyword, page, size, false);
  646. SPage<Map<String, Object>> sPage = new SPage<>(1, 1, 1, documents.getSize(), true, true);
  647. // 精准匹配成功
  648. if (!CollectionUtils.isEmpty(documents.getContent())) {
  649. Map<String, List<String>> keywordFeildsMap = new HashMap<>();
  650. List<String> goodsFeilds = new ArrayList<>();
  651. goodsFeilds.add(SearchConstants.GOODS_PR_PBRAND_EN_UNTOKENIZED_FIELD);
  652. goodsFeilds.add(SearchConstants.GOODS_PR_PBRAND_CN_UNTOKENIZED_FIELD);
  653. List<String> productsFeilds = new ArrayList<>();
  654. productsFeilds.add(SearchConstants.PRODUCT_PRIVATE_PBRAND_CN_FIELD);
  655. productsFeilds.add(SearchConstants.PRODUCT_PRIVATE_PBRAND_EN_FIELD);
  656. keywordFeildsMap.put("goods", goodsFeilds);
  657. keywordFeildsMap.put("products", productsFeilds);
  658. brandIds.add(documents.getContent().get(0));
  659. // 获取卖家信息
  660. Map<String, SPage<Object>> sellers = querySellers(keyword, keywordFeildsMap, page, size, false, SearchConstants.BRAND);
  661. stock = sellers.get("stock");
  662. futures = sellers.get("futures");
  663. map.put("stock", stock);
  664. map.put("futures", futures);
  665. // 模糊匹配
  666. } else {
  667. SPage<Long> brandPage = getBrandIds(keyword, page, size, true);
  668. brandIds = brandPage.getContent();
  669. if (CollectionUtils.isEmpty(brandIds)) {
  670. List<String> keywordFields = new ArrayList<>();
  671. keywordFields.add(SearchConstants.BRAND_NAMECN_FIELD);
  672. keywordFields.add(SearchConstants.BRAND_NAMEEN_FIELD);
  673. keyword = recursivelyBrands(keyword, keywordFields, true);
  674. brandPage = getBrandIds(keyword, page, size, true);
  675. brandIds = brandPage.getContent();
  676. }
  677. sPage = new SPage<>(brandPage.getTotalPage(), brandPage.getTotalElement(), brandPage.getPage(), brandPage.getSize(), brandPage.isFirst(), brandPage.isLast());
  678. }
  679. map.put("brandIds", brandIds);
  680. content.add(map);
  681. sPage.setContent(content);
  682. return sPage;
  683. }
  684. @Override
  685. public Map<String, Object> getSellersWithKind(String keyword, Integer page, Integer size)
  686. throws IOException {
  687. Map<String, Object> map = new HashMap<>();
  688. SPage<Object> stock = null;
  689. SPage<Object> futures = null;
  690. Map<String, List<String>> keywordFeildsMap = new HashMap<>();
  691. List<String> goodsFeilds = new ArrayList<>();
  692. goodsFeilds.add(SearchConstants.GOODS_PR_KIND_FIELD);
  693. List<String> productsFeilds = new ArrayList<>();
  694. productsFeilds.add(SearchConstants.PRODUCT_PRIVATE_KIND_FIELD);
  695. keywordFeildsMap.put("goods", goodsFeilds);
  696. keywordFeildsMap.put("products", productsFeilds);
  697. Map<String, SPage<Object>> sellers = querySellers(keyword, keywordFeildsMap, page, size, true, SearchConstants.KIND);
  698. stock = sellers.get("stock");
  699. futures = sellers.get("futures");
  700. map.put("stock", stock);
  701. map.put("futures", futures);
  702. return map;
  703. }
  704. /**
  705. * 获取品牌id
  706. * @param keyword
  707. * @param page
  708. * @param size
  709. * @return
  710. */
  711. private SPage<Long> getBrandIds(String keyword, Integer page, Integer size, Boolean tokenized) throws IOException{
  712. List<Long> brandIds = new ArrayList<>();
  713. if (SearchUtils.isKeywordInvalid(keyword)) {
  714. throw new IllegalArgumentException("搜索关键词无效:" + keyword);
  715. }
  716. List<String> keywordFields = new ArrayList<>();
  717. if (tokenized) {
  718. keywordFields.add(SearchConstants.BRAND_NAMECN_FIELD);
  719. keywordFields.add(SearchConstants.BRAND_NAMEEN_FIELD);
  720. } else {
  721. keywordFields.add(SearchConstants.BRAND_NAMECN_UNTOKENIZED_FIELD);
  722. keywordFields.add(SearchConstants.BRAND_NAMEEN_UNTOKENIZED_FIELD);
  723. }
  724. BooleanQuery booleanQuery = queryBrands(keyword, keywordFields, tokenized);
  725. logger.info(booleanQuery.toString());
  726. SPage<Document> documents = getDocuments(SearchConstants.BRAND_TABLE_NAME, booleanQuery, new Sort(sortBrand(keyword)), page,
  727. size);
  728. SPage<Long> sPage = new SPage<Long>(documents.getTotalPage(),
  729. documents.getTotalElement(), documents.getPage(), documents.getSize(), documents.isFirst(),
  730. documents.isLast());
  731. for (Document document : documents.getContent()) {
  732. brandIds.add(Long.parseLong(document.get(SearchConstants.BRAND_ID_FIELD)));
  733. }
  734. sPage.setContent(brandIds);
  735. return sPage;
  736. }
  737. private BooleanQuery queryBrands(String keyword, List<String> keywordFields, Boolean tokenized) {
  738. BooleanQuery booleanQuery = new BooleanQuery();
  739. if (!SearchUtils.isKeywordInvalid(keyword) && !CollectionUtils.isEmpty(keywordFields)) {
  740. for (String keywordField : keywordFields) {
  741. if (tokenized) {
  742. booleanQuery.add(SearchUtils.getBooleanQuery(keywordField, keyword.toLowerCase()),
  743. BooleanClause.Occur.SHOULD);
  744. } else {
  745. booleanQuery.add(new TermQuery(new Term(keywordField, keyword.toLowerCase())), Occur.SHOULD);
  746. }
  747. }
  748. }
  749. return booleanQuery;
  750. }
  751. /**
  752. * 获取品牌Document
  753. * @param keyword 关键词
  754. * @param page 页码
  755. * @param size 页数
  756. * @return
  757. * @throws IOException
  758. */
  759. private SPage<Document> getBrandDocuments(String keyword, Integer page, Integer size) throws IOException {
  760. if (SearchUtils.isKeywordInvalid(keyword)) {
  761. throw new IllegalArgumentException("搜索关键词无效:" + keyword);
  762. }
  763. BooleanQuery booleanQuery = new BooleanQuery();
  764. booleanQuery.add(SearchUtils.getBooleanQuery(SearchConstants.BRAND_NAMECN_FIELD, keyword),
  765. BooleanClause.Occur.SHOULD);
  766. booleanQuery.add(SearchUtils.getBooleanQuery(SearchConstants.BRAND_NAMEEN_FIELD, keyword),
  767. BooleanClause.Occur.SHOULD);
  768. logger.info(booleanQuery.toString());
  769. return getDocuments(SearchConstants.BRAND_TABLE_NAME, booleanQuery, new Sort(sortBrand(keyword)), page,
  770. size);
  771. }
  772. /**
  773. * @return 品牌排序规则
  774. */
  775. private SortField[] sortBrand(String keyword) {
  776. // 自定义排序 > 权重 > 访问量 > 搜索次数 > 分数
  777. // 分数排序放在最后,是因为有的中英文名称相同,分数翻倍,但实际匹配度并不高
  778. return new SortField[]{
  779. sortField(SearchConstants.BRAND_WEIGHT_FIELD, Type.DOUBLE, true, Double.MIN_VALUE),
  780. sortField(SearchConstants.BRAND_VISIT_COUNT_FIELD, Type.LONG, true, Long.MIN_VALUE),
  781. sortField(SearchConstants.BRAND_SEARCH_COUNT_FIELD, Type.LONG, true, Long.MIN_VALUE),
  782. new SortField(SearchConstants.BRAND_NAMEEN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(keyword, true)),
  783. new SortField(SearchConstants.BRAND_NAMECN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(keyword))
  784. };
  785. }
  786. @Override
  787. public Map<String, Object> getComponentIds(String keyword, PageParams pageParams) {
  788. Map<String, Object> searchComponentIds = getComponentIds(keyword, pageParams, null, null);
  789. return searchComponentIds;
  790. // TODO 对品牌、类目甚至拼音混合搜索(待完善)
  791. // int total = (int) searchComponentIds.get("total");
  792. // if (total != 0) {
  793. // return searchComponentIds;
  794. // }
  795. // List<Long> kindIds = getKindIds(keyword, Occur.SHOULD);
  796. // List<Long> brandIds = getBrandIds(keyword, Occur.SHOULD);
  797. // return getComponentIds(null, pageParams, kindIds, brandIds);
  798. }
  799. /**
  800. * 根据关键词搜索产品
  801. *
  802. * @param keyword
  803. * @param pageParams
  804. * @param kindIds
  805. * @param brandIds
  806. * @return
  807. */
  808. private Map<String, Object> getComponentIds(String keyword, PageParams pageParams, List<Long> kindIds,
  809. List<Long> brandIds) {
  810. // 因为器件、属性值的数据量远比类目、品牌大得多,而且器件搜索可能还需进行分页,
  811. // 所以涉及器件、属性值的搜索,大都不能像类目和品牌一样直接利用SearchUtils.getDocuments方法
  812. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.COMPONENT_TABLE_NAME);
  813. if (pageParams == null) {
  814. pageParams = new PageParams();
  815. }
  816. if (pageParams.getPage() <= 0)
  817. pageParams.setPage(1);
  818. if (pageParams.getSize() <= 0)
  819. pageParams.setSize(20);
  820. Map<String, Object> map = new HashMap<>();
  821. List<Long> ids = new ArrayList<>();
  822. try {
  823. BooleanQuery booleanQuery = new BooleanQuery();
  824. if (!SearchUtils.isKeywordInvalid(keyword)) {
  825. booleanQuery.add(setBoost(keyword), BooleanClause.Occur.MUST);
  826. }
  827. Map<FilterField, Object> filters = pageParams.getFilters();
  828. if (!CollectionUtils.isEmpty(filters)) {
  829. // 筛选类目
  830. if (!StringUtils.isEmpty(filters.get(FilterField.COMPONENT_KINDID))) {
  831. String kindId = filters.get(FilterField.COMPONENT_KINDID).toString();
  832. TermQuery kindQuery = new TermQuery(new Term(SearchConstants.COMPONENT_KI_ID_FIELD, kindId));
  833. booleanQuery.add(kindQuery, BooleanClause.Occur.MUST);
  834. }
  835. // 筛选品牌
  836. if (!StringUtils.isEmpty(filters.get(FilterField.COMPONENT_BRANDID))) {
  837. String brandId = filters.get(FilterField.COMPONENT_BRANDID).toString();
  838. TermQuery brandQuery = new TermQuery(new Term(SearchConstants.COMPONENT_BR_ID_FIELD, brandId));
  839. booleanQuery.add(brandQuery, BooleanClause.Occur.MUST);
  840. }
  841. // 库存不为0
  842. if (!StringUtils.isEmpty(filters.get(FilterField.COMPONENT_HAS_RESERVE))) {
  843. Boolean isReserveNotEmpty = (Boolean) filters.get(FilterField.COMPONENT_HAS_RESERVE);
  844. if (isReserveNotEmpty) {
  845. booleanQuery.add(NumericRangeQuery.newDoubleRange(SearchConstants.COMPONENT_RESERVE_FIELD, 0.0,
  846. Double.MAX_VALUE, false, true), BooleanClause.Occur.MUST);
  847. }
  848. }
  849. // 现货、呆滞库存、样品数量不为0,取或的关系
  850. if (!StringUtils.isEmpty(filters.get(FilterField.COMPONENT_HAS_SAMPLE))
  851. || !StringUtils.isEmpty(filters.get(FilterField.COMPONENT_HAS_ORIGINAL))
  852. || !StringUtils.isEmpty(filters.get(FilterField.COMPONENT_HAS_INACTION_STOCK))) {
  853. BooleanQuery booleanQuery2 = new BooleanQuery();
  854. if (!StringUtils.isEmpty(filters.get(FilterField.COMPONENT_HAS_SAMPLE))) {
  855. booleanQuery2.add(NumericRangeQuery.newDoubleRange(SearchConstants.COMPONENT_SAMPLE_QTY_FIELD,
  856. 0.0, Double.MAX_VALUE, false, true), BooleanClause.Occur.SHOULD);
  857. }
  858. if (!StringUtils.isEmpty(filters.get(FilterField.COMPONENT_HAS_ORIGINAL))) {
  859. booleanQuery2.add(NumericRangeQuery.newDoubleRange(SearchConstants.COMPONENT_ORIGINAL_QTY_FIELD,
  860. 0.0, Double.MAX_VALUE, false, true), BooleanClause.Occur.SHOULD);
  861. }
  862. if (!StringUtils.isEmpty(filters.get(FilterField.COMPONENT_HAS_INACTION_STOCK))) {
  863. booleanQuery2.add(
  864. NumericRangeQuery.newDoubleRange(SearchConstants.COMPONENT_INACTION_STOCK_QTY_FIELD,
  865. 0.0, Double.MAX_VALUE, false, true),
  866. BooleanClause.Occur.SHOULD);
  867. }
  868. booleanQuery.add(booleanQuery2, Occur.MUST);
  869. }
  870. // 属性过滤
  871. if (!StringUtils.isEmpty(filters.get(FilterField.COMPONENT_PROPERTIES))) {
  872. JSONObject proJSON = JSONObject
  873. .parseObject(String.valueOf(filters.get(FilterField.COMPONENT_PROPERTIES)));
  874. for (String key : proJSON.keySet()) {
  875. String value = String.valueOf(proJSON.get(key));
  876. if (!StringUtils.isEmpty(value)) {
  877. if (!key.startsWith(SearchConstants.COMPONENT_PROPERTY_PREFIX)) {
  878. key = SearchConstants.COMPONENT_PROPERTY_PREFIX + key;
  879. }
  880. TermQuery propertyQuery = new TermQuery(new Term(key, value));
  881. booleanQuery.add(propertyQuery, BooleanClause.Occur.MUST);
  882. }
  883. }
  884. }
  885. }
  886. if (!CollectionUtils.isEmpty(kindIds)) {
  887. BooleanQuery booleanQuery2 = new BooleanQuery();
  888. for (Long id : kindIds) {
  889. booleanQuery2.add(new TermQuery(new Term(SearchConstants.COMPONENT_KI_ID_FIELD, id.toString())),
  890. Occur.SHOULD);
  891. }
  892. booleanQuery.add(booleanQuery2, Occur.MUST);
  893. }
  894. if (!CollectionUtils.isEmpty(brandIds)) {
  895. BooleanQuery booleanQuery2 = new BooleanQuery();
  896. for (Long id : brandIds) {
  897. booleanQuery2.add(new TermQuery(new Term(SearchConstants.COMPONENT_BR_ID_FIELD, id.toString())),
  898. Occur.SHOULD);
  899. }
  900. booleanQuery.add(booleanQuery2, Occur.MUST);
  901. }
  902. logger.info(booleanQuery.toString());
  903. Sort sort = new Sort(sortComponent(keyword));
  904. TopDocs hits;
  905. if (pageParams.getPage() > 1) {// 不是第一页
  906. TopDocs previousHits = indexSearcher.search(booleanQuery,
  907. (pageParams.getPage() - 1) * pageParams.getSize(), sort, true, false);
  908. ScoreDoc[] previousScoreDocs = previousHits.scoreDocs;
  909. ScoreDoc after = previousScoreDocs[previousScoreDocs.length - 1];
  910. hits = indexSearcher.searchAfter(after, booleanQuery, pageParams.getSize(), sort, true, false);
  911. } else {
  912. hits = indexSearcher.search(booleanQuery, pageParams.getSize(), sort, true, false);
  913. }
  914. ScoreDoc[] scoreDocs = hits.scoreDocs;
  915. for (ScoreDoc scoreDoc : scoreDocs) {
  916. // 数据量太大,需要指定将获取的数据(以免载入不必要的数据,降低速度)
  917. Set<String> fieldsToLoad = new HashSet<>();
  918. fieldsToLoad.add(SearchConstants.COMPONENT_ID_FIELD);
  919. Document document = indexSearcher.doc(scoreDoc.doc, fieldsToLoad);
  920. String componentId = document.get(SearchConstants.COMPONENT_ID_FIELD);
  921. ids.add(Long.parseLong(componentId));
  922. // System.out.println(componentId + "\t" + scoreDoc.score);
  923. }
  924. map.put("componentIds", ids);
  925. map.put("page", pageParams.getPage());
  926. map.put("size", pageParams.getSize());
  927. map.put("total", hits.totalHits);
  928. } catch (IOException e) {
  929. logger.error("", e);
  930. } finally {
  931. releaseIndexSearcher(indexSearcher);
  932. }
  933. return map;
  934. }
  935. /**
  936. * 同时搜索器件、类目、品牌,并设置boost
  937. *
  938. * @param keyword
  939. * @return
  940. */
  941. private Query setBoost(String keyword) {
  942. BooleanQuery booleanQuery = new BooleanQuery();
  943. PrefixQuery prefixQuery = new PrefixQuery(
  944. new Term(SearchConstants.COMPONENT_CODE_FIELD, keyword.toLowerCase()));
  945. prefixQuery.setBoost(100);
  946. booleanQuery.add(prefixQuery, BooleanClause.Occur.SHOULD);
  947. booleanQuery.add(createQuery(SearchConstants.COMPONENT_BR_NAMECN_FIELD, keyword, 10), BooleanClause.Occur.SHOULD);
  948. booleanQuery.add(createQuery(SearchConstants.COMPONENT_BR_NAMEEN_FIELD, keyword, 10), BooleanClause.Occur.SHOULD);
  949. booleanQuery.add(createQuery(SearchConstants.COMPONENT_KI_NAME_FIELD, keyword, 1), BooleanClause.Occur.SHOULD);
  950. return booleanQuery;
  951. }
  952. /**
  953. * @return 器件排序规则
  954. */
  955. private SortField[] sortComponent(String keyword) {
  956. // 分数 > 器件(访问量 > 搜索次数) > 品牌(权重 > 访问量 > 搜索次数) > 类目(访问量 > 搜索次数)
  957. return new SortField[]{
  958. sortField(SearchConstants.COMPONENT_WEIGHT_FIELD, Type.DOUBLE, true, Double.MIN_VALUE),
  959. sortField(SearchConstants.COMPONENT_VISIT_COUNT_FIELD, Type.LONG, true, Long.MIN_VALUE),
  960. sortField(SearchConstants.COMPONENT_SEARCH_COUNT_FIELD, Type.LONG, true, Long.MIN_VALUE),
  961. sortField(SearchConstants.COMPONENT_BR_WEIGHT_FIELD, Type.DOUBLE, true, Double.MIN_VALUE),
  962. sortField(SearchConstants.COMPONENT_BR_VISIT_COUNT_FIELD, Type.LONG, true, Long.MIN_VALUE),
  963. sortField(SearchConstants.COMPONENT_BR_SEARCH_COUNT_FIELD, Type.LONG, true, Long.MIN_VALUE),
  964. sortField(SearchConstants.COMPONENT_KI_VISIT_COUNT_FIELD, Type.LONG, true, Long.MIN_VALUE),
  965. sortField(SearchConstants.COMPONENT_KI_SEARCH_COUNT_FIELD, Type.LONG, true, Long.MIN_VALUE),
  966. new SortField(SearchConstants.COMPONENT_CODE_FIELD, new StringFieldComparatorSource(keyword, true)),
  967. new SortField(SearchConstants.COMPONENT_BR_NAMEEN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(keyword, true)),
  968. new SortField(SearchConstants.COMPONENT_BR_NAMECN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(keyword, true)),
  969. new SortField(SearchConstants.COMPONENT_KI_NAME_UNTOKENIZED_FIELD, new StringFieldComparatorSource(keyword))
  970. };
  971. }
  972. @Override
  973. public Set<Long> getKindIdsBySearchComponent(String keyword, String brandId) {
  974. Query filter = null;
  975. // 筛选品牌
  976. if (!StringUtils.isEmpty(brandId)) {
  977. filter = new TermQuery(new Term(SearchConstants.COMPONENT_BR_ID_FIELD, brandId));
  978. }
  979. return collectBySearchComponent(keyword, filter, SearchConstants.COMPONENT_KI_ID_FIELD).getValues();
  980. }
  981. @Override
  982. public Set<Map<String, Object>> getKindsBySearchComponent(String keyword, String brandId) {
  983. Query filter = null;
  984. // 筛选品牌
  985. if (!StringUtils.isEmpty(brandId)) {
  986. filter = new TermQuery(new Term(SearchConstants.COMPONENT_BR_ID_FIELD, brandId));
  987. }
  988. return collectBySearchComponent(keyword, filter, SearchConstants.KIND_ID_FIELD,
  989. new DistinctGroupCollector.CollectField(SearchConstants.KIND_ID_FIELD, "id"),
  990. new DistinctGroupCollector.CollectField(SearchConstants.KIND_NAMECN_FIELD, "nameCn"))
  991. .getCollectValues();
  992. }
  993. @Override
  994. public Set<Long> getBrandIdsBySearchComponent(String keyword, String kindId) {
  995. Query filter = null;
  996. // 筛选类目
  997. if (!StringUtils.isEmpty(kindId)) {
  998. filter = new TermQuery(new Term(SearchConstants.COMPONENT_KI_ID_FIELD, kindId));
  999. }
  1000. return collectBySearchComponent(keyword, filter, SearchConstants.COMPONENT_BR_ID_FIELD).getValues();
  1001. }
  1002. @Override
  1003. public Set<Map<String, Object>> getBrandsBySearchComponent(String keyword, String kindId) {
  1004. Query filter = null;
  1005. // 筛选类目
  1006. if (!StringUtils.isEmpty(kindId)) {
  1007. filter = new TermQuery(new Term(SearchConstants.COMPONENT_KI_ID_FIELD, kindId));
  1008. }
  1009. return collectBySearchComponent(keyword, filter, SearchConstants.COMPONENT_BR_ID_FIELD,
  1010. new DistinctGroupCollector.CollectField(SearchConstants.COMPONENT_BR_ID_FIELD, "id"),
  1011. new DistinctGroupCollector.CollectField(SearchConstants.COMPONENT_BR_UUID_FIELD, "uuid"),
  1012. new DistinctGroupCollector.CollectField(SearchConstants.COMPONENT_BR_NAMECN_FIELD, "nameCn"))
  1013. .getCollectValues();
  1014. }
  1015. /**
  1016. * 搜索器件时统计指定信息
  1017. *
  1018. * @param keyword 关键词
  1019. * @param filter 过滤条件
  1020. * @param groupField 统计的单个字段(多为 id)
  1021. * @param collectFields 统计的多个字段(详细信息),可为空
  1022. * @return 统计信息
  1023. */
  1024. private DistinctGroupCollector collectBySearchComponent(String keyword, Query filter, String groupField, DistinctGroupCollector.CollectField... collectFields){
  1025. if (SearchUtils.isKeywordInvalid(keyword)) {
  1026. throw new IllegalArgumentException("搜索关键词无效:" + keyword);
  1027. }
  1028. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.COMPONENT_TABLE_NAME);
  1029. try {
  1030. BooleanQuery booleanQuery = new BooleanQuery();
  1031. keyword = URLDecoder.decode(keyword, "UTF-8");
  1032. booleanQuery.add(setBoost(keyword), BooleanClause.Occur.MUST);
  1033. if(filter != null){
  1034. booleanQuery.add(filter, Occur.FILTER);
  1035. }
  1036. logger.info(booleanQuery.toString());
  1037. DistinctGroupCollector collector = new DistinctGroupCollector(groupField, collectFields);
  1038. indexSearcher.search(booleanQuery, collector);
  1039. return collector;
  1040. } catch (IOException e) {
  1041. throw new IllegalStateException("统计失败", e);
  1042. } finally {
  1043. releaseIndexSearcher(indexSearcher);
  1044. }
  1045. }
  1046. @Override
  1047. public List<String> getSimilarKeywords(String keyword, Integer size) {
  1048. size = size == null || size < 1 ? SIMILAR_NUM : size;
  1049. List<String> result = new ArrayList<>();
  1050. // 相似的器件原厂型号数量足够,直接返回
  1051. List<String> componentCodes = getSimilarComponentCodes(keyword, size);
  1052. result.addAll(componentCodes);
  1053. removeDuplicate(result);
  1054. if (result.size() == size) {
  1055. return result;
  1056. }
  1057. // 获取相似类目
  1058. List<String> kindNames = getSimilarKindNames(keyword, size);
  1059. if (!CollectionUtils.isEmpty(kindNames)) {
  1060. result.addAll(kindNames);
  1061. removeDuplicate(result);
  1062. // 如果总的数量超出SIMILAR_NUM,去除多余的元素
  1063. if (result.size() > size) {
  1064. removeElements(result, size);
  1065. return result;
  1066. }
  1067. }
  1068. // 获取相似品牌
  1069. List<String> brandNames = getSimilarBrandNames(keyword, size);
  1070. if (!CollectionUtils.isEmpty(brandNames)) {
  1071. result.addAll(brandNames);
  1072. removeDuplicate(result);
  1073. if (result.size() > size) {
  1074. removeElements(result, size);
  1075. return result;
  1076. }
  1077. }
  1078. return result;
  1079. }
  1080. @Override
  1081. public List<Map<String, Object>> getSimilarComponents(String componentCode, Integer size) {
  1082. size = size == null || size < 1 ? SIMILAR_NUM : size;
  1083. if (SearchUtils.isKeywordInvalid(componentCode)) {
  1084. throw new IllegalArgumentException("输入无效:" + componentCode);
  1085. }
  1086. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.COMPONENT_TABLE_NAME);
  1087. List<Map<String, Object>> components = new ArrayList<>();
  1088. try {
  1089. PrefixQuery prefixQuery = new PrefixQuery(
  1090. new Term(SearchConstants.COMPONENT_CODE_FIELD, componentCode.toLowerCase()));
  1091. logger.info(prefixQuery.toString());
  1092. Sort sort = new Sort(new SortField(SearchConstants.COMPONENT_CODE_FIELD, new StringFieldComparatorSource(componentCode)));
  1093. TopDocs hits = indexSearcher.search(prefixQuery, size, sort);
  1094. ScoreDoc[] scoreDocs = hits.scoreDocs;
  1095. for (ScoreDoc scoreDoc : scoreDocs) {
  1096. Set<String> fieldsToLoad = new HashSet<>();
  1097. fieldsToLoad.add(SearchConstants.COMPONENT_ID_FIELD);
  1098. fieldsToLoad.add(SearchConstants.COMPONENT_UUID_FIELD);
  1099. fieldsToLoad.add(SearchConstants.COMPONENT_CODE_FIELD);
  1100. Document document = indexSearcher.doc(scoreDoc.doc, fieldsToLoad);
  1101. Map<String, Object> map = new HashMap<>();
  1102. map.put("id", Long.parseLong(document.get(SearchConstants.COMPONENT_ID_FIELD)));
  1103. map.put("uuid", document.get(SearchConstants.COMPONENT_UUID_FIELD));
  1104. map.put("code", document.get(SearchConstants.COMPONENT_CODE_FIELD));
  1105. components.add(map);
  1106. }
  1107. } catch (IOException e) {
  1108. logger.error("", e);
  1109. } finally {
  1110. releaseIndexSearcher(indexSearcher);
  1111. }
  1112. return components;
  1113. }
  1114. @Override
  1115. public List<Map<String, Object>> getSimilarProducts(String code, Integer size) {
  1116. size = size == null || size < 1 ? SIMILAR_NUM : size;
  1117. if (SearchUtils.isKeywordInvalid(code)) {
  1118. throw new IllegalArgumentException("输入无效:" + code);
  1119. }
  1120. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.PRODUCTS_PRIVATE_TABLE_NAME);
  1121. List<Map<String, Object>> products = new ArrayList<>();
  1122. try {
  1123. String termCode = "";
  1124. TermQuery termQuery = new TermQuery(new Term(SearchConstants.PRODUCT_PRIVATE_PCMPCODE_FIELD, code.toLowerCase()));
  1125. TopDocs termHits = indexSearcher.search(termQuery, 1);
  1126. if (termHits.totalHits > 0) {
  1127. for (ScoreDoc scoreDoc : termHits.scoreDocs) {
  1128. Set<String> fieldsToLoad = new HashSet<>();
  1129. fieldsToLoad.add(SearchConstants.PRODUCT_PRIVATE_ID_FIELD);
  1130. fieldsToLoad.add(SearchConstants.PRODUCT_PRIVATE_PCMPCODE_FIELD);
  1131. Document document = indexSearcher.doc(scoreDoc.doc, fieldsToLoad);
  1132. Map<String, Object> map = new HashMap<>();
  1133. map.put("id", Long.parseLong(document.get(SearchConstants.PRODUCT_PRIVATE_ID_FIELD)));
  1134. map.put("pcmpcode", document.get(SearchConstants.PRODUCT_PRIVATE_PCMPCODE_FIELD));
  1135. termCode = String.valueOf(map.get("pcmpcode"));
  1136. products.add(map);
  1137. }
  1138. }
  1139. PrefixQuery prefixQuery = new PrefixQuery(
  1140. new Term(SearchConstants.PRODUCT_PRIVATE_PCMPCODE_FIELD, code.toLowerCase()));
  1141. logger.info(prefixQuery.toString());
  1142. // 物料型号重复率过高,故先去重
  1143. DuplicateFilter codeFilter = new DuplicateFilter(SearchConstants.PRODUCT_PRIVATE_PCMPCODE_FIELD);
  1144. Sort sort = new Sort(new SortField(SearchConstants.PRODUCT_PRIVATE_PCMPCODE_FIELD, new StringFieldComparatorSource(code, true)));
  1145. TopDocs hits = indexSearcher.search(prefixQuery, codeFilter, size * 2, sort, true, false);
  1146. ScoreDoc[] scoreDocs = hits.scoreDocs;
  1147. String pcmpcode = "";
  1148. for (ScoreDoc scoreDoc : scoreDocs) {
  1149. Set<String> fieldsToLoad = new HashSet<>();
  1150. fieldsToLoad.add(SearchConstants.PRODUCT_PRIVATE_ID_FIELD);
  1151. fieldsToLoad.add(SearchConstants.PRODUCT_PRIVATE_PCMPCODE_FIELD);
  1152. Document document = indexSearcher.doc(scoreDoc.doc, fieldsToLoad);
  1153. Map<String, Object> map = new HashMap<>();
  1154. map.put("id", Long.parseLong(document.get(SearchConstants.PRODUCT_PRIVATE_ID_FIELD)));
  1155. map.put("pcmpcode", document.get(SearchConstants.PRODUCT_PRIVATE_PCMPCODE_FIELD));
  1156. if (!pcmpcode.equals(map.get("pcmpcode")) && !termCode.equals(map.get("pcmpcode"))) {
  1157. products.add(map);
  1158. pcmpcode = String.valueOf(map.get("pcmpcode"));
  1159. }
  1160. }
  1161. } catch (IOException e) {
  1162. logger.error("", e);
  1163. } finally {
  1164. releaseIndexSearcher(indexSearcher);
  1165. }
  1166. return products;
  1167. }
  1168. @Override
  1169. public List<Map<String, Object>> getSimilarBrands(String brandName, Integer size) throws IOException {
  1170. size = size == null || size < 1 ? SIMILAR_NUM : size;
  1171. if (SearchUtils.isKeywordInvalid(brandName)) {
  1172. throw new IllegalArgumentException("输入无效:" + brandName);
  1173. }
  1174. List<Map<String, Object>> brands = new ArrayList<Map<String, Object>>();
  1175. // 品牌名称带有空格,并且中英文名并无一定顺序,因此对nameCn、nameEn均要搜索
  1176. BooleanQuery booleanQuery = new BooleanQuery();
  1177. // 搜索nameCn
  1178. booleanQuery.add(SearchUtils.getBooleanQuery(SearchConstants.BRAND_NAMECN_FIELD, brandName),
  1179. BooleanClause.Occur.SHOULD);
  1180. // 搜索nameEn
  1181. booleanQuery.add(SearchUtils.getBooleanQuery(SearchConstants.BRAND_NAMEEN_FIELD, brandName),
  1182. BooleanClause.Occur.SHOULD);
  1183. logger.info(booleanQuery.toString());
  1184. Sort sort = new Sort(new SortField(SearchConstants.BRAND_NAMEEN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(brandName)),
  1185. new SortField(SearchConstants.BRAND_NAMECN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(brandName)));
  1186. List<Document> documents = getDocuments(SearchConstants.BRAND_TABLE_NAME, booleanQuery, sort, null, size)
  1187. .getContent();
  1188. for (Document document : documents) {
  1189. Map<String, Object> brand = new HashMap<>();
  1190. brand.put("id", Long.parseLong(document.get(SearchConstants.BRAND_ID_FIELD)));
  1191. brand.put("uuid", document.get(SearchConstants.BRAND_UUID_FIELD));
  1192. brand.put("nameCn", document.get(SearchConstants.BRAND_NAMECN_FIELD));
  1193. brand.put("nameEn", document.get(SearchConstants.BRAND_NAMEEN_FIELD));
  1194. brands.add(brand);
  1195. }
  1196. return brands;
  1197. }
  1198. @Override
  1199. public List<Map<String, Object>> getSimilarKinds(String kindName, Integer size) throws IOException {
  1200. size = size == null || size < 1 ? SIMILAR_NUM : size;
  1201. return getSimilarKinds(kindName, null, null, size);
  1202. }
  1203. @Override
  1204. public List<Map<String, Object>> getSimilarLeafKinds(String kindName, Integer size) throws IOException {
  1205. size = size == null || size < 1 ? SIMILAR_NUM : size;
  1206. return getSimilarKinds(kindName, (short) 1, null, size);
  1207. }
  1208. @Override
  1209. public List<Map<String, Object>> getSimilarKindsByLevel(String kindName, Short level, Integer size) throws IOException {
  1210. size = size == null || size < 1 ? SIMILAR_NUM : size;
  1211. return getSimilarKinds(kindName, null, level, size);
  1212. }
  1213. /**
  1214. * 根据输入的类目名获取联想词
  1215. *
  1216. * @param kindName
  1217. * 类目名
  1218. * @param isLeaf
  1219. * 是否只获取末级类目
  1220. * @param level
  1221. * 指定的类目级别
  1222. * @param size 指定的联想词数目
  1223. * @return
  1224. */
  1225. private List<Map<String, Object>> getSimilarKinds(String kindName, Short isLeaf, Short level, Integer size) throws IOException {
  1226. size = size == null || size < 1 ? SIMILAR_NUM : size;
  1227. if (SearchUtils.isKeywordInvalid(kindName)) {
  1228. throw new IllegalArgumentException("输入无效:" + kindName);
  1229. }
  1230. List<Map<String, Object>> kinds = new ArrayList<>();
  1231. BooleanQuery booleanQuery = new BooleanQuery();
  1232. booleanQuery.add(SearchUtils.getBooleanQuery(SearchConstants.KIND_NAMECN_FIELD, kindName),
  1233. BooleanClause.Occur.MUST);
  1234. if (isLeaf != null && isLeaf == 1) {
  1235. booleanQuery.add(new TermQuery(new Term(SearchConstants.KIND_ISLEAF_FIELD, String.valueOf(isLeaf))),
  1236. BooleanClause.Occur.MUST);
  1237. } else {
  1238. if (level != null && level > 0) {
  1239. booleanQuery.add(new TermQuery(new Term(SearchConstants.KIND_LEVEL_FIELD, String.valueOf(level))),
  1240. BooleanClause.Occur.MUST);
  1241. }
  1242. }
  1243. logger.info(booleanQuery.toString());
  1244. Sort sort = new Sort(new SortField(SearchConstants.KIND_NAMECN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(kindName)));
  1245. List<Document> documents = getDocuments(SearchConstants.KIND_TABLE_NAME, booleanQuery, sort, null, size).getContent();
  1246. for (Document document : documents) {
  1247. Map<String, Object> map = new HashMap<>();
  1248. map.put("id", Long.parseLong(document.get(SearchConstants.KIND_ID_FIELD)));
  1249. map.put("nameCn", document.get(SearchConstants.KIND_NAMECN_FIELD));
  1250. map.put("isLeaf", Short.parseShort(document.get(SearchConstants.KIND_ISLEAF_FIELD)));
  1251. map.put("level", Short.parseShort(document.get(SearchConstants.KIND_LEVEL_FIELD)));
  1252. kinds.add(map);
  1253. }
  1254. return kinds;
  1255. }
  1256. @Override
  1257. public List<Map<String, String>> getSimilarPropertyValues(Long kindId, Long propertyId, String keyword,
  1258. Long topNum) {
  1259. if (kindId == null || propertyId == null) {
  1260. throw new IllegalArgumentException("类目id和属性id不能为空");
  1261. }
  1262. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.COMPONENT_TABLE_NAME);
  1263. String propertyIdString = String.valueOf(propertyId);
  1264. if (!propertyIdString.startsWith(SearchConstants.COMPONENT_PROPERTY_PREFIX)) {
  1265. propertyIdString = SearchConstants.COMPONENT_PROPERTY_PREFIX + propertyIdString;
  1266. }
  1267. propertyIdString = propertyIdString + SearchConstants.COMPONENT_PROPERTY_TOKENIZED_SUFFIX;
  1268. if (keyword == null) {
  1269. keyword = "";
  1270. }
  1271. if (topNum == null || topNum < 1) {
  1272. topNum = (long) SIMILAR_NUM;
  1273. }
  1274. List<String> propertyValues = new ArrayList<>();
  1275. try {
  1276. BooleanQuery booleanQuery = new BooleanQuery();
  1277. booleanQuery.add(new TermQuery(new Term(SearchConstants.COMPONENT_KI_ID_FIELD, String.valueOf(kindId))),
  1278. BooleanClause.Occur.MUST);
  1279. booleanQuery.add(new PrefixQuery(new Term(propertyIdString, keyword.toLowerCase())),
  1280. BooleanClause.Occur.MUST);
  1281. logger.info(booleanQuery.toString());
  1282. // 如果只搜索topNum个结果,去除重复的属性值后,数目很可能是不够的
  1283. TopDocs topDocs = indexSearcher.search(booleanQuery, SearchConstants.TOP_NUM);
  1284. ScoreDoc[] scoreDocs = topDocs.scoreDocs;
  1285. for (ScoreDoc scoreDoc : scoreDocs) {
  1286. Set<String> fieldsToLoad = new HashSet<>();
  1287. fieldsToLoad.add(propertyIdString);
  1288. Document document = indexSearcher.doc(scoreDoc.doc, fieldsToLoad);
  1289. String propertyValue = document.get(propertyIdString);
  1290. if (!StringUtils.isEmpty(propertyValue) && !propertyValues.contains(propertyValue)) {
  1291. propertyValues.add(propertyValue);
  1292. }
  1293. if (propertyValues.size() >= topNum) {
  1294. break;
  1295. }
  1296. }
  1297. } catch (IOException e) {
  1298. logger.error("", e);
  1299. } finally {
  1300. releaseIndexSearcher(indexSearcher);
  1301. }
  1302. List<Map<String, String>> result = new ArrayList<>();
  1303. for (String propertyValue : propertyValues) {
  1304. Map<String, String> map = new HashMap<>();
  1305. map.put("propertyValue", propertyValue);
  1306. result.add(map);
  1307. }
  1308. return result;
  1309. }
  1310. @Override
  1311. public Set<Map<String, Object>> getSimilarGoodsCode(String keyword, String storeType, Integer size) throws IOException {
  1312. size = size == null || size < 1 ? SIMILAR_NUM : size;
  1313. if (SearchUtils.isKeywordInvalid(keyword)) {
  1314. throw new IllegalArgumentException("输入无效:" + keyword);
  1315. }
  1316. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.GOODS_TABLE_NAME);
  1317. Set<Map<String, Object>> goods = new HashSet<>();
  1318. try {
  1319. List<String> storeTypes = new ArrayList<>();
  1320. BooleanQuery booleanQuery = new BooleanQuery();
  1321. List<Long> status = Arrays.asList(TradeGoods.VALID_STATUS);
  1322. filter(status, SearchConstants.GOODS_GO_STATUS_FIELD, booleanQuery);
  1323. if (!StringUtils.isEmpty(storeType)) {
  1324. storeTypes = Arrays.asList(storeType.split(","));
  1325. }
  1326. if (!CollectionUtils.isEmpty(storeTypes)) {
  1327. filter(storeTypes, SearchConstants.GOODS_ST_TYPE_FIELD, booleanQuery);
  1328. }
  1329. DistinctGroupCollector.CollectField[] collectFields = new DistinctGroupCollector.CollectField[] {
  1330. new DistinctGroupCollector.CollectField(SearchConstants.GOODS_PR_PCMPCODE_FIELD, SearchConstants.GOODS_PR_PCMPCODE_FIELD)
  1331. };
  1332. DistinctGroupCollector collector = new DistinctGroupCollector(SearchConstants.GOODS_PR_PCMPCODE_FIELD, collectFields);
  1333. booleanQuery.add(new TermQuery(new Term(GOODS_PR_PCMPCODE_FIELD, keyword.toLowerCase())), BooleanClause.Occur.MUST);
  1334. indexSearcher.search(booleanQuery, collector);
  1335. goods.addAll(collector.getCollectValues());
  1336. BooleanQuery booleanQueryPre = new BooleanQuery();
  1337. filter(status, SearchConstants.GOODS_GO_STATUS_FIELD, booleanQueryPre);
  1338. if (!CollectionUtils.isEmpty(storeTypes)) {
  1339. filter(storeTypes, SearchConstants.GOODS_ST_TYPE_FIELD, booleanQueryPre);
  1340. }
  1341. booleanQueryPre.add(new PrefixQuery(new Term(GOODS_PR_PCMPCODE_FIELD, keyword.toLowerCase())), Occur.MUST);
  1342. logger.info(booleanQueryPre.toString());
  1343. indexSearcher.search(booleanQueryPre, collector);
  1344. goods.addAll(collector.getCollectValues());
  1345. } catch (IOException e) {
  1346. logger.error("", e);
  1347. } finally {
  1348. releaseIndexSearcher(indexSearcher);
  1349. }
  1350. if (goods.size() > size) {
  1351. goods = goods.stream().limit(size).collect(Collectors.toSet());
  1352. }
  1353. return goods;
  1354. }
  1355. @Override
  1356. public Set<Map<String, Object>> getSimilarGoodsBrand(String keyword, String storeType, Integer size)
  1357. throws IOException {
  1358. size = size == null || size < 1 ? SIMILAR_NUM : size;
  1359. if (SearchUtils.isKeywordInvalid(keyword)) {
  1360. throw new IllegalArgumentException("输入无效:" + keyword);
  1361. }
  1362. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.GOODS_TABLE_NAME);
  1363. Set<Map<String, Object>> goods = new HashSet<>();
  1364. try {
  1365. List<String> storeTypes = new ArrayList<>();
  1366. BooleanQuery booleanQuery = new BooleanQuery();
  1367. List<Long> status = Arrays.asList(TradeGoods.VALID_STATUS);
  1368. filter(status, SearchConstants.GOODS_GO_STATUS_FIELD, booleanQuery);
  1369. if (!StringUtils.isEmpty(storeType)) {
  1370. storeTypes = Arrays.asList(storeType.split(","));
  1371. }
  1372. if (!CollectionUtils.isEmpty(storeTypes)) {
  1373. filter(storeTypes, SearchConstants.GOODS_ST_TYPE_FIELD, booleanQuery);
  1374. }
  1375. DistinctGroupCollector.CollectField[] collectFields = new DistinctGroupCollector.CollectField[] {
  1376. new DistinctGroupCollector.CollectField(GOODS_BR_NAME_EN_UNTOKENIZED_FIELD, GOODS_BR_NAME_EN_UNTOKENIZED_FIELD)
  1377. };
  1378. DistinctGroupCollector collector = new DistinctGroupCollector(GOODS_BR_NAME_EN_UNTOKENIZED_FIELD, collectFields);
  1379. BooleanQuery nameQuery = new BooleanQuery();
  1380. nameQuery.add(new TermQuery(new Term(GOODS_BR_NAME_EN_UNTOKENIZED_FIELD, keyword.toLowerCase())), Occur.SHOULD);
  1381. nameQuery.add(new TermQuery(new Term(GOODS_BR_NAME_CN_UNTOKENIZED_FIELD, keyword.toLowerCase())), Occur.SHOULD);
  1382. booleanQuery.add(nameQuery, Occur.MUST);
  1383. indexSearcher.search(booleanQuery, collector);
  1384. goods.addAll(collector.getCollectValues());
  1385. BooleanQuery booleanQueryPre = new BooleanQuery();
  1386. filter(status, SearchConstants.GOODS_GO_STATUS_FIELD, booleanQueryPre);
  1387. if (!CollectionUtils.isEmpty(storeTypes)) {
  1388. filter(storeTypes, SearchConstants.GOODS_ST_TYPE_FIELD, booleanQueryPre);
  1389. }
  1390. BooleanQuery namePreQuery = new BooleanQuery();
  1391. namePreQuery.add(new PrefixQuery(new Term(GOODS_BR_NAME_EN_UNTOKENIZED_FIELD, keyword.toLowerCase())), Occur.SHOULD);
  1392. namePreQuery.add(new PrefixQuery(new Term(GOODS_BR_NAME_CN_UNTOKENIZED_FIELD, keyword.toLowerCase())), Occur.SHOULD);
  1393. booleanQueryPre.add(namePreQuery, Occur.MUST);
  1394. logger.info(booleanQueryPre.toString());
  1395. indexSearcher.search(booleanQueryPre, collector);
  1396. goods.addAll(collector.getCollectValues());
  1397. } catch (IOException e) {
  1398. logger.error("", e);
  1399. } finally {
  1400. releaseIndexSearcher(indexSearcher);
  1401. }
  1402. if (goods.size() > size) {
  1403. goods = goods.stream().limit(size).collect(Collectors.toSet());
  1404. }
  1405. return goods;
  1406. }
  1407. @Override
  1408. public Set<Map<String, Object>> getSimilarGoodsKind(String keyword, String storeType, Integer size)
  1409. throws IOException {
  1410. size = size == null || size < 1 ? SIMILAR_NUM : size;
  1411. if (SearchUtils.isKeywordInvalid(keyword)) {
  1412. throw new IllegalArgumentException("输入无效:" + keyword);
  1413. }
  1414. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.GOODS_TABLE_NAME);
  1415. Set<Map<String, Object>> goods = new HashSet<>();
  1416. try {
  1417. List<String> storeTypes = new ArrayList<>();
  1418. BooleanQuery booleanQuery = new BooleanQuery();
  1419. List<Long> status = Arrays.asList(TradeGoods.VALID_STATUS);
  1420. filter(status, SearchConstants.GOODS_GO_STATUS_FIELD, booleanQuery);
  1421. if (!StringUtils.isEmpty(storeType)) {
  1422. storeTypes = Arrays.asList(storeType.split(","));
  1423. }
  1424. if (!CollectionUtils.isEmpty(storeTypes)) {
  1425. filter(storeTypes, SearchConstants.GOODS_ST_TYPE_FIELD, booleanQuery);
  1426. }
  1427. DistinctGroupCollector.CollectField[] collectFields = new DistinctGroupCollector.CollectField[] {
  1428. new DistinctGroupCollector.CollectField(GOODS_KI_NAME_CN_FIELD, GOODS_KI_NAME_CN_FIELD),
  1429. };
  1430. DistinctGroupCollector collector = new DistinctGroupCollector(GOODS_KI_NAME_CN_FIELD, collectFields);
  1431. booleanQuery.add(new TermQuery(new Term(GOODS_KI_NAME_CN_UNTOKENIZED_FIELD, keyword.toLowerCase())), BooleanClause.Occur.MUST);
  1432. indexSearcher.search(booleanQuery, collector);
  1433. goods.addAll(collector.getCollectValues());
  1434. BooleanQuery booleanQueryPre = new BooleanQuery();
  1435. filter(status, SearchConstants.GOODS_GO_STATUS_FIELD, booleanQueryPre);
  1436. if (!CollectionUtils.isEmpty(storeTypes)) {
  1437. filter(storeTypes, SearchConstants.GOODS_ST_TYPE_FIELD, booleanQueryPre);
  1438. }
  1439. booleanQueryPre.add(new PrefixQuery(new Term(GOODS_KI_NAME_CN_FIELD, keyword.toLowerCase())), Occur.MUST);
  1440. logger.info(booleanQueryPre.toString());
  1441. indexSearcher.search(booleanQueryPre, collector);
  1442. goods.addAll(collector.getCollectValues());
  1443. } catch (IOException e) {
  1444. logger.error("", e);
  1445. } finally {
  1446. releaseIndexSearcher(indexSearcher);
  1447. }
  1448. if (goods.size() > size) {
  1449. goods = goods.stream().limit(size).collect(Collectors.toSet());
  1450. }
  1451. return goods;
  1452. }
  1453. /**
  1454. * 根据输入获取相似的器件原厂型号
  1455. *
  1456. * @param componentCode
  1457. * @param size 指定的联想词数目
  1458. * @return
  1459. */
  1460. private List<String> getSimilarComponentCodes(String componentCode, Integer size) {
  1461. return getSimilarValues(SearchConstants.COMPONENT_TABLE_NAME, SearchConstants.COMPONENT_CODE_FIELD,
  1462. SearchConstants.COMPONENT_CODE_FIELD, componentCode.toLowerCase(), size, true);
  1463. }
  1464. /**
  1465. * 根据输入获取相似的品牌名称
  1466. *
  1467. * @param brandName
  1468. * @param size 指定的联想词数目
  1469. * @return
  1470. */
  1471. private List<String> getSimilarBrandNames(String brandName, Integer size) {
  1472. // 获取相似的中文品牌
  1473. List<String> nameCns = getSimilarValues(SearchConstants.BRAND_TABLE_NAME, SearchConstants.BRAND_NAMECN_FIELD,
  1474. SearchConstants.BRAND_NAMECN_UNTOKENIZED_FIELD, brandName, size, false);
  1475. // 相似的中文品牌数量足够,直接返回
  1476. if (nameCns != null && nameCns.size() == SIMILAR_NUM) {
  1477. return nameCns;
  1478. }
  1479. List<String> names = nameCns;
  1480. // 获取相似的英文品牌
  1481. List<String> nameEns = getSimilarValues(SearchConstants.BRAND_TABLE_NAME, SearchConstants.BRAND_NAMEEN_FIELD,
  1482. SearchConstants.BRAND_NAMEEN_UNTOKENIZED_FIELD, brandName, size, false);
  1483. names.addAll(nameEns);
  1484. return names;
  1485. }
  1486. /**
  1487. * 根据输入获取相似的类目名称
  1488. *
  1489. * @param kindName
  1490. * @param size 指定的联想词数目
  1491. * @return
  1492. */
  1493. private List<String> getSimilarKindNames(String kindName, Integer size) {
  1494. return getSimilarValues(SearchConstants.KIND_TABLE_NAME, SearchConstants.KIND_NAMECN_FIELD,
  1495. SearchConstants.KIND_NAMECN_UNTOKENIZED_FIELD, kindName, size, false);
  1496. }
  1497. /**
  1498. * 根据输入值获取该域相似的值
  1499. *
  1500. * @param tableName 表名
  1501. * @param field 联想字段
  1502. * @param sortField 排序字段
  1503. * @param keyword 关键词
  1504. * @param size 联想词数目
  1505. * @param usePrefixQuery 是否使用 PrefixQuery
  1506. * @return 联想词
  1507. */
  1508. private List<String> getSimilarValues(String tableName, String field, String sortField, String keyword, Integer size, boolean usePrefixQuery) {
  1509. if (SearchUtils.isKeywordInvalid(keyword)) {
  1510. throw new IllegalArgumentException("输入无效:" + keyword);
  1511. }
  1512. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(tableName);
  1513. List<String> result = new ArrayList<>();
  1514. try {
  1515. Query query;
  1516. if(usePrefixQuery){
  1517. query = new PrefixQuery(new Term(field, keyword.toLowerCase()));
  1518. }else{
  1519. query = SearchUtils.getBooleanQuery(field, keyword);
  1520. }
  1521. logger.info(query.toString());
  1522. Sort sort = new Sort(new SortField(sortField, new StringFieldComparatorSource(keyword)));
  1523. TopDocs hits = indexSearcher.search(query, size, sort);
  1524. ScoreDoc[] scoreDocs = hits.scoreDocs;
  1525. for (ScoreDoc scoreDoc : scoreDocs) {
  1526. Set<String> fieldsToLoad = new HashSet<>();
  1527. fieldsToLoad.add(field);
  1528. Document document = indexSearcher.doc(scoreDoc.doc, fieldsToLoad);
  1529. result.add(document.get(field));
  1530. // System.out.println(document.get(field) + "\t" +
  1531. // scoreDoc.score);
  1532. }
  1533. } catch (IOException e) {
  1534. logger.error("", e);
  1535. } finally {
  1536. releaseIndexSearcher(indexSearcher);
  1537. }
  1538. return result;
  1539. }
  1540. /**
  1541. * 移除集合中重复的元素
  1542. *
  1543. * @param list
  1544. * @return
  1545. */
  1546. private void removeDuplicate(List<String> list) {
  1547. if (list == null) {
  1548. return;
  1549. }
  1550. List<String> result = new ArrayList<>();
  1551. for (String str : list) {
  1552. if (!result.contains(str)) {
  1553. result.add(str);
  1554. }
  1555. }
  1556. list.removeAll(list);
  1557. list.addAll(result);
  1558. }
  1559. /**
  1560. * 删除集合内 startIndex(含)后的元素
  1561. *
  1562. * @param list
  1563. * @param startIndex
  1564. */
  1565. private void removeElements(List<? extends String> list, int startIndex) {
  1566. if (CollectionUtils.isEmpty(list)) {
  1567. return;
  1568. }
  1569. int listsSize = list.size();
  1570. for (int i = listsSize - 1; i >= startIndex; i--) {
  1571. list.remove(i);
  1572. }
  1573. }
  1574. @Override
  1575. public Map<String, Object> getGoodsIds(String keyword, PageParams pageParams) throws IOException {
  1576. // List<String> keywordFields = new ArrayList<>();
  1577. // 先根据品牌搜索,品牌不存在再搜索型号等
  1578. // keywordFields.add(SearchConstants.GOODS_BR_NAME_CN_UNTOKENIZED_FIELD);
  1579. // keywordFields.add(SearchConstants.GOODS_BR_NAME_EN_UNTOKENIZED_FIELD);
  1580. // Map<String, Object> goodsIds = getGoodsIds(keyword, keywordFields, false, pageParams);
  1581. // if (CollectionUtils.isEmpty(goodsIds) || goodsIds.get("componentIds") == null
  1582. // || JSONObject.parseArray(goodsIds.get("componentIds").toString()).isEmpty()) {
  1583. // keyword = recursivelyGetGoodsIds(keyword, null, true);
  1584. // goodsIds = getGoodsIds(keyword, null, true, pageParams);
  1585. // }
  1586. // return goodsIds;
  1587. Map<String, Object> map = new HashMap<>();
  1588. List<String> goodsFields = new ArrayList<>();
  1589. List<String> productsFields = new ArrayList<>();
  1590. // 先根据品牌搜索,品牌不存在再搜索型号等
  1591. goodsFields.add(GOODS_PR_PCMPCODE_FIELD);
  1592. goodsFields.add(SearchConstants.GOODS_CMP_CODE_FIELD);
  1593. productsFields.add(SearchConstants.PRODUCT_PRIVATE_CMPCODE_FIELD);
  1594. productsFields.add(SearchConstants.PRODUCT_PRIVATE_PCMPCODE_FIELD);
  1595. PageParams stockPageParams = new PageParams(pageParams);
  1596. PageParams futuresPageParams = new PageParams(pageParams);
  1597. String productkeyword = new String(keyword);
  1598. // 现货
  1599. Map<String, Object> stock = getGoodsIds(keyword, goodsFields, false, pageParams);
  1600. // 如搜索无结果则分词
  1601. if (CollectionUtils.isEmpty(stock) || stock.get("content") == null || JSONObject.parseArray(stock.get("content").toString()).isEmpty()) {
  1602. keyword = recursivelyGetGoodsIds(keyword, goodsFields, true);
  1603. stock = getGoodsIds(keyword, goodsFields, true, stockPageParams);
  1604. }
  1605. // 期货
  1606. PageParams recursivelyPageParams = new PageParams(pageParams);
  1607. SPage<Long> futures = getProductIds(productkeyword, productsFields, false, futuresPageParams);
  1608. // 如搜索无结果则分词
  1609. if (futures == null || CollectionUtils.isEmpty(futures.getContent())) {
  1610. productkeyword = recursivelyProductIds(productkeyword, productsFields, true);
  1611. futures = getProductIds(productkeyword, productsFields, true, recursivelyPageParams);
  1612. }
  1613. map.put("stock", stock);
  1614. map.put("futures", futures);
  1615. return map;
  1616. }
  1617. @Override
  1618. public Map<String, Object> getOldGoodsIds(String keyword, PageParams pageParams) throws IOException {
  1619. List<String> keywordFields = new ArrayList<>();
  1620. // 先根据品牌搜索,品牌不存在再搜索型号等
  1621. keywordFields.add(GOODS_BR_NAME_CN_UNTOKENIZED_FIELD);
  1622. keywordFields.add(GOODS_BR_NAME_EN_UNTOKENIZED_FIELD);
  1623. Map<String, Object> goodsIds = getOldGoodsIds(keyword, keywordFields, false, pageParams);
  1624. if (CollectionUtils.isEmpty(goodsIds) || goodsIds.get("componentIds") == null
  1625. || JSONObject.parseArray(goodsIds.get("componentIds").toString()).isEmpty()) {
  1626. keyword = recursivelyGetGoodsIds(keyword, null, true);
  1627. goodsIds = getOldGoodsIds(keyword, null, true, pageParams);
  1628. }
  1629. return goodsIds;
  1630. }
  1631. /**
  1632. * 递归查询物料(如果没有结果,则降低精度,直至长度为 1)
  1633. *
  1634. * @param keyword 关键词
  1635. * @param keywordFields 要查询的字段
  1636. * @param tokenized 是否分词
  1637. * @return 最后一次搜索的关键词
  1638. */
  1639. private String recursivelyProductIds(String keyword, List<String> keywordFields, Boolean tokenized) throws IOException {
  1640. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.PRODUCTS_PRIVATE_TABLE_NAME);
  1641. try {
  1642. BooleanQuery booleanQuery = queryProducts(keyword, keywordFields, tokenized);
  1643. logger.info(booleanQuery.toString());
  1644. TotalHitCountCollector collector = new TotalHitCountCollector();
  1645. indexSearcher.search(booleanQuery, collector);
  1646. // 如果没有结果,则降低精度,直至 keyword 长度为 1
  1647. if (collector.getTotalHits() < 1 && !SearchUtils.isKeywordInvalid(keyword) && keyword.length() > 1) {
  1648. return recursivelyProductIds(keyword.substring(0, keyword.length() - 1), keywordFields, tokenized);
  1649. }
  1650. return keyword;
  1651. } finally {
  1652. releaseIndexSearcher(indexSearcher);
  1653. }
  1654. }
  1655. /**
  1656. * 递归查询品牌(如果没有结果,则降低精度,直至长度为 1)
  1657. *
  1658. * @param keyword 关键词
  1659. * @param keywordFields 要查询的字段
  1660. * @param tokenized 是否分词
  1661. * @return 最后一次搜索的关键词
  1662. */
  1663. private String recursivelyBrands(String keyword, List<String> keywordFields, Boolean tokenized) throws IOException {
  1664. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.BRAND_TABLE_NAME);
  1665. try {
  1666. BooleanQuery booleanQuery = queryBrands(keyword, keywordFields, tokenized);
  1667. logger.info(booleanQuery.toString());
  1668. TotalHitCountCollector collector = new TotalHitCountCollector();
  1669. indexSearcher.search(booleanQuery, collector);
  1670. // 如果没有结果,则降低精度,直至 keyword 长度为 1
  1671. if (collector.getTotalHits() < 1 && !SearchUtils.isKeywordInvalid(keyword) && keyword.length() > 1) {
  1672. return recursivelyBrands(keyword.substring(0, keyword.length() - 1), keywordFields, tokenized);
  1673. }
  1674. return keyword;
  1675. } finally {
  1676. releaseIndexSearcher(indexSearcher);
  1677. }
  1678. }
  1679. /**
  1680. * 通过递归类目查询物料(如果没有结果,则降低精度,直至长度为 1)
  1681. *
  1682. * @param keyword 关键词
  1683. * @return 最后一次搜索的关键词
  1684. */
  1685. private String recursivelyKindsForSellers(String indexName, String keyword, Query query, String duplicateType) throws IOException {
  1686. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(indexName);
  1687. try {
  1688. DuplicateFilter duplicateFilter = null;
  1689. if (!StringUtils.isEmpty(duplicateType)) {
  1690. if (duplicateType.equals(SearchConstants.BRAND)) {
  1691. duplicateFilter = new DuplicateFilter(indexName.equals(SearchConstants.GOODS_TABLE_NAME) ? SearchConstants.GOODS_PR_PBRAND_EN_CN_STUUID_UNTOKENIZED_FIELD : SearchConstants.PRODUCT_PRIVATE_PBRAND_ENUU_FIELD);
  1692. } else if (duplicateType.equals(SearchConstants.KIND)) {
  1693. duplicateFilter = new DuplicateFilter(indexName.equals(SearchConstants.GOODS_TABLE_NAME) ? SearchConstants.GOODS_KIND_STUUID_UNTOKENIZED_FIELD : SearchConstants.PRODUCT_PRIVATE_KIND_ENUU_FIELD);
  1694. }
  1695. }
  1696. TotalHitCountCollector collector = new TotalHitCountCollector();
  1697. indexSearcher.search(query, duplicateFilter, collector);
  1698. // 如果没有结果,则降低精度,直至 keyword 长度为 1
  1699. if (collector.getTotalHits() < 1 && !SearchUtils.isKeywordInvalid(keyword) && keyword.length() > 1) {
  1700. return recursivelyKindsForSellers(indexName, keyword.substring(0, keyword.length() - 1), query, duplicateType);
  1701. }
  1702. return keyword;
  1703. } finally {
  1704. releaseIndexSearcher(indexSearcher);
  1705. }
  1706. }
  1707. /**
  1708. * @param keyword
  1709. * @param keywordFields
  1710. * 要查询的字段
  1711. * @param tokenized
  1712. * 是否分词
  1713. * @param pageParams
  1714. * @return
  1715. */
  1716. private Map<String, Object> getGoodsIds(String keyword, List<String> keywordFields, Boolean tokenized,
  1717. PageParams pageParams) {
  1718. // 因为器件、属性值的数据量远比类目、品牌大得多,而且器件搜索可能还需进行分页,
  1719. // 所以涉及器件、属性值的搜索,大都不能像类目和品牌一样直接利用SearchUtils.getDocuments方法
  1720. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.GOODS_TABLE_NAME);
  1721. if (pageParams == null) {
  1722. pageParams = new PageParams();
  1723. }
  1724. if (pageParams.getPage() <= 0)
  1725. pageParams.setPage(1);
  1726. if (pageParams.getSize() <= 0)
  1727. pageParams.setSize(20);
  1728. Map<String, Object> map = new HashMap<String, Object>();
  1729. // List<Long> cmpIds = new ArrayList<>();
  1730. List<Long> goIds = new ArrayList<>();
  1731. try {
  1732. BooleanQuery booleanQuery = queryGoods(keyword, keywordFields, tokenized);
  1733. setGoodsFilter(pageParams.getFilters(), booleanQuery);
  1734. logger.info(booleanQuery.toString());
  1735. Sort sort = sortGoods(keyword, pageParams.getSort());
  1736. TopDocs hits;
  1737. if (pageParams.getPage() > 1) {// 不是第一页
  1738. TopDocs previousHits = indexSearcher.search(booleanQuery, (pageParams.getPage() - 1) * pageParams.getSize(), sort, true, false);
  1739. int totalHits = previousHits.totalHits;
  1740. if (totalHits == 0) {
  1741. return map;
  1742. }
  1743. int totalPage = (int) Math.ceil(totalHits / (1.0 * pageParams.getSize()));
  1744. if (pageParams.getPage() > totalPage) {
  1745. pageParams.setPage(totalPage);
  1746. if (totalPage == 1) {
  1747. hits = indexSearcher.search(booleanQuery, pageParams.getSize(), sort, true, false);
  1748. } else {
  1749. previousHits = indexSearcher.search(booleanQuery, (pageParams.getPage() - 1) * pageParams.getSize(), sort, true, false);
  1750. ScoreDoc[] previousScoreDocs = previousHits.scoreDocs;
  1751. ScoreDoc after = previousScoreDocs[previousScoreDocs.length - 1];
  1752. hits = indexSearcher.searchAfter(after, booleanQuery, pageParams.getSize(), sort, true, false);
  1753. }
  1754. } else {
  1755. ScoreDoc[] previousScoreDocs = previousHits.scoreDocs;
  1756. ScoreDoc after = previousScoreDocs[previousScoreDocs.length - 1];
  1757. hits = indexSearcher.searchAfter(after, booleanQuery, pageParams.getSize(), sort, true, false);
  1758. }
  1759. } else {
  1760. hits = indexSearcher.search(booleanQuery, pageParams.getSize(), sort, true, false);
  1761. }
  1762. // 数据量太大,需要指定将获取的数据(以免载入不必要的数据,降低速度)
  1763. Set<String> fieldsToLoad = new HashSet<>();
  1764. // fieldsToLoad.add(SearchConstants.GOODS_CMP_ID_FIELD);
  1765. fieldsToLoad.add(SearchConstants.GOODS_GO_ID_FIELD);
  1766. ScoreDoc[] scoreDocs = hits.scoreDocs;
  1767. int totalHits = hits.totalHits;
  1768. for (ScoreDoc scoreDoc : scoreDocs) {
  1769. Document document = indexSearcher.doc(scoreDoc.doc, fieldsToLoad);
  1770. // String cmpId = document.get(SearchConstants.GOODS_CMP_ID_FIELD);
  1771. // cmpIds.add(StringUtils.isEmpty(cmpId) || cmpId.equals(ObjectToDocumentUtils.NULL_VALUE) ? null : Long.valueOf(cmpId));
  1772. String goId = document.get(SearchConstants.GOODS_GO_ID_FIELD);
  1773. if (!StringUtils.isEmpty(goId) && !goId.equals(ObjectToDocumentUtils.NULL_VALUE)) {
  1774. goIds.add(Long.valueOf(goId));
  1775. } else {
  1776. totalHits--;
  1777. }
  1778. }
  1779. // map.put("componentIds", cmpIds);
  1780. map.put("content", goIds);
  1781. map.put("page", pageParams.getPage());
  1782. map.put("size", pageParams.getSize());
  1783. map.put("totalElement", totalHits);
  1784. int totalPage = (int) Math.ceil(totalHits / (1.0 * pageParams.getSize()));
  1785. map.put("totalPage", totalPage);
  1786. map.put("last", totalPage == pageParams.getPage() ? true : false);
  1787. map.put("first", pageParams.getPage() == 1 ? true : false);
  1788. } catch (IOException e) {
  1789. logger.error("", e);
  1790. } finally {
  1791. releaseIndexSearcher(indexSearcher);
  1792. }
  1793. return map;
  1794. }
  1795. /**
  1796. * @param keyword
  1797. * @param keywordFields
  1798. * 要查询的字段
  1799. * @param tokenized
  1800. * 是否分词
  1801. * @param pageParams
  1802. * @return
  1803. */
  1804. private Map<String, Object> getOldGoodsIds(String keyword, List<String> keywordFields, Boolean tokenized,
  1805. PageParams pageParams) {
  1806. // 因为器件、属性值的数据量远比类目、品牌大得多,而且器件搜索可能还需进行分页,
  1807. // 所以涉及器件、属性值的搜索,大都不能像类目和品牌一样直接利用SearchUtils.getDocuments方法
  1808. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.GOODS_TABLE_NAME);
  1809. if (pageParams == null) {
  1810. pageParams = new PageParams();
  1811. }
  1812. if (pageParams.getPage() <= 0)
  1813. pageParams.setPage(1);
  1814. if (pageParams.getSize() <= 0)
  1815. pageParams.setSize(20);
  1816. Map<String, Object> map = new HashMap<String, Object>();
  1817. List<Long> cmpIds = new ArrayList<>();
  1818. List<Long> goIds = new ArrayList<>();
  1819. try {
  1820. BooleanQuery booleanQuery = queryGoods(keyword, keywordFields, tokenized);
  1821. setGoodsFilter(pageParams.getFilters(), booleanQuery);
  1822. logger.info(booleanQuery.toString());
  1823. Sort sort = sortGoods(keyword, pageParams.getSort());
  1824. TopDocs hits;
  1825. if (pageParams.getPage() > 1) {// 不是第一页
  1826. TopDocs previousHits = indexSearcher.search(booleanQuery,
  1827. (pageParams.getPage() - 1) * pageParams.getSize(), sort, true, false);
  1828. int totalHits = previousHits.totalHits;
  1829. if ((pageParams.getPage() - 1) * pageParams.getSize() >= totalHits) {
  1830. return map;
  1831. }
  1832. ScoreDoc[] previousScoreDocs = previousHits.scoreDocs;
  1833. ScoreDoc after = previousScoreDocs[previousScoreDocs.length - 1];
  1834. hits = indexSearcher.searchAfter(after, booleanQuery, pageParams.getSize(), sort, true, false);
  1835. } else {
  1836. hits = indexSearcher.search(booleanQuery, pageParams.getSize(), sort, true, false);
  1837. }
  1838. // 数据量太大,需要指定将获取的数据(以免载入不必要的数据,降低速度)
  1839. Set<String> fieldsToLoad = new HashSet<>();
  1840. fieldsToLoad.add(SearchConstants.GOODS_CMP_ID_FIELD);
  1841. fieldsToLoad.add(SearchConstants.GOODS_GO_ID_FIELD);
  1842. ScoreDoc[] scoreDocs = hits.scoreDocs;
  1843. for (ScoreDoc scoreDoc : scoreDocs) {
  1844. Document document = indexSearcher.doc(scoreDoc.doc, fieldsToLoad);
  1845. String cmpId = document.get(SearchConstants.GOODS_CMP_ID_FIELD);
  1846. cmpIds.add(StringUtils.isEmpty(cmpId) || cmpId.equals(ObjectToDocumentUtils.NULL_VALUE) ? null : Long.valueOf(cmpId));
  1847. String goId = document.get(SearchConstants.GOODS_GO_ID_FIELD);
  1848. goIds.add(StringUtils.isEmpty(goId) || goId.equals(ObjectToDocumentUtils.NULL_VALUE) ? null : Long.valueOf(goId));
  1849. }
  1850. map.put("componentIds", cmpIds);
  1851. map.put("goodsIds", goIds);
  1852. map.put("page", pageParams.getPage());
  1853. map.put("size", pageParams.getSize());
  1854. map.put("total", hits.totalHits);
  1855. } catch (IOException e) {
  1856. logger.error("", e);
  1857. } finally {
  1858. SearchUtils.releaseIndexSearcher(indexSearcher);
  1859. }
  1860. return map;
  1861. }
  1862. /**
  1863. * 获取物料信息
  1864. * @param keyword
  1865. * @return
  1866. */
  1867. private SPage<Long> getProductIds(String keyword, List<String> keywordFields, Boolean tokenized, PageParams pageParams) throws IOException{
  1868. if (pageParams == null) {
  1869. pageParams = new PageParams();
  1870. }
  1871. if (pageParams.getPage() <= 0)
  1872. pageParams.setPage(1);
  1873. if (pageParams.getSize() <= 0)
  1874. pageParams.setSize(20);
  1875. BooleanQuery booleanQuery = queryProducts(keyword, keywordFields, tokenized);
  1876. logger.info(booleanQuery.toString());
  1877. SPage<Document> documents = getDocuments(SearchConstants.PRODUCTS_PRIVATE_TABLE_NAME, booleanQuery, new Sort(sortProduct()), pageParams.getPage(), pageParams.getSize());
  1878. SPage<Long> sPage = new SPage<>(documents.getTotalPage(), documents.getTotalElement(), documents.getPage(), documents.getSize(), documents.isFirst(), documents.isLast());
  1879. List<Long> productIds = new ArrayList<>();
  1880. for (Document document : documents.getContent()) {
  1881. productIds.add(Long.parseLong(document.get(SearchConstants.PRODUCT_PRIVATE_ID_FIELD)));
  1882. }
  1883. sPage.setContent(productIds);
  1884. return sPage;
  1885. }
  1886. /**
  1887. * 递归查询批次(如果没有结果,则降低精度,直至长度为 1)
  1888. *
  1889. * @param keyword 关键词
  1890. * @param keywordFields 要查询的字段
  1891. * @param tokenized 是否分词
  1892. * @return 最后一次搜索的关键词
  1893. */
  1894. private String recursivelyGetGoodsIds(String keyword, List<String> keywordFields, Boolean tokenized) throws IOException {
  1895. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.GOODS_TABLE_NAME);
  1896. try {
  1897. BooleanQuery booleanQuery = queryGoods(keyword, keywordFields, tokenized);
  1898. logger.info(booleanQuery.toString());
  1899. TotalHitCountCollector collector = new TotalHitCountCollector();
  1900. indexSearcher.search(booleanQuery, collector);
  1901. // 如果没有结果,则降低精度,直至 keyword 长度为 1
  1902. if (collector.getTotalHits() < 1 && !SearchUtils.isKeywordInvalid(keyword) && keyword.length() > 1) {
  1903. return recursivelyGetGoodsIds(keyword.substring(0, keyword.length() - 1), keywordFields, tokenized);
  1904. }
  1905. return keyword;
  1906. } finally {
  1907. releaseIndexSearcher(indexSearcher);
  1908. }
  1909. }
  1910. /**
  1911. * 设置批次过滤条件
  1912. *
  1913. * @param filters 指定的过滤条件
  1914. * @param query 原查询
  1915. */
  1916. private void setGoodsFilter(Map<FilterField, Object> filters, BooleanQuery query) {
  1917. Object status;
  1918. // 筛选状态
  1919. if (!CollectionUtils.isEmpty(filters) && !StringUtils.isEmpty(filters.get(FilterField.GOODS_STATUS))) {
  1920. // 如果明确指定了状态,则直接过滤批次(结果中不包括没有批次的器件)
  1921. status = filters.get(FilterField.GOODS_STATUS);
  1922. filter(status, SearchConstants.GOODS_GO_STATUS_FIELD, query);
  1923. } else {
  1924. // 如果未明确指定状态,则使用默认状态分情况进行过滤(结果中包括没有批次的器件)
  1925. status = Arrays.asList(TradeGoods.VALID_STATUS);
  1926. // 批次 id 不为空时,对状态过滤
  1927. Query goNullQuery = SearchUtils.getNullQuery(SearchConstants.GOODS_GO_ID_FIELD);
  1928. BooleanQuery q1 = new BooleanQuery();
  1929. q1.add(goNullQuery, Occur.MUST_NOT);
  1930. filter(status, SearchConstants.GOODS_GO_STATUS_FIELD, q1);
  1931. // 或者批次 id 为空(此时是器件)
  1932. BooleanQuery q2 = new BooleanQuery();
  1933. q2.add(SearchUtils.getNullQuery(SearchConstants.GOODS_CMP_ID_FIELD), Occur.MUST_NOT);
  1934. q2.add(goNullQuery, Occur.MUST);
  1935. BooleanQuery booleanQuery = new BooleanQuery();
  1936. booleanQuery.add(q1, Occur.SHOULD);
  1937. booleanQuery.add(q2, Occur.SHOULD);
  1938. query.add(booleanQuery, Occur.FILTER);
  1939. }
  1940. if (CollectionUtils.isEmpty(filters)) {
  1941. return;
  1942. }
  1943. // 筛选类目
  1944. if (!StringUtils.isEmpty(filters.get(FilterField.GOODS_KINDID))) {
  1945. filter(filters.get(FilterField.GOODS_KINDID), SearchConstants.GOODS_KI_ID_FIELD, query);
  1946. }
  1947. // 筛选品牌
  1948. if (!StringUtils.isEmpty(filters.get(FilterField.GOODS_BRANDID))) {
  1949. filter(filters.get(FilterField.GOODS_BRANDID), SearchConstants.GOODS_BR_ID_FIELD, query);
  1950. }
  1951. // 筛选货源
  1952. if (!StringUtils.isEmpty(filters.get(FilterField.GOODS_STORE_TYPE))) {
  1953. filter(filters.get(FilterField.GOODS_STORE_TYPE), SearchConstants.GOODS_ST_TYPE_FIELD, query);
  1954. }
  1955. // 筛选货币
  1956. if (!StringUtils.isEmpty(filters.get(FilterField.GOODS_CRNAME))) {
  1957. filter(filters.get(FilterField.GOODS_CRNAME), SearchConstants.GOODS_CRNAME_FIELD, query);
  1958. }
  1959. // 价格筛选
  1960. Object minPriceRmb = filters.get(FilterField.GOODS_MINPRICERMB);
  1961. Object maxPriceRmb = filters.get(FilterField.GOODS_MAXPRICERMB);
  1962. Object minPriceUsd = filters.get(FilterField.GOODS_MINPRICEUSD);
  1963. Object maxPriceUsd = filters.get(FilterField.GOODS_MAXPRICEUSD);
  1964. // 筛选人民币价格
  1965. if (!StringUtils.isEmpty(minPriceRmb) || !StringUtils.isEmpty(maxPriceRmb)) {
  1966. Double minPrice = null;
  1967. Double maxPrice = null;
  1968. if (!StringUtils.isEmpty(minPriceRmb)) {
  1969. minPrice = Double.valueOf(minPriceRmb.toString());
  1970. }
  1971. if (!StringUtils.isEmpty(maxPriceRmb)) {
  1972. maxPrice = Double.valueOf(maxPriceRmb.toString());
  1973. }
  1974. query.add(NumericRangeQuery.newDoubleRange(SearchConstants.GOODS_GO_MINPRICERMB_FIELD,
  1975. minPrice, maxPrice, true, true), BooleanClause.Occur.FILTER);
  1976. }
  1977. // 筛选美元价格
  1978. if (!StringUtils.isEmpty(minPriceUsd) || !StringUtils.isEmpty(maxPriceUsd)) {
  1979. Double minPrice = null;
  1980. Double maxPrice = null;
  1981. if (!StringUtils.isEmpty(minPriceUsd)) {
  1982. minPrice = Double.valueOf(minPriceUsd.toString());
  1983. }
  1984. if (!StringUtils.isEmpty(maxPriceUsd)) {
  1985. maxPrice = Double.valueOf(maxPriceUsd.toString());
  1986. }
  1987. query.add(NumericRangeQuery.newDoubleRange(SearchConstants.GOODS_GO_MINPRICEUSD_FIELD,
  1988. minPrice, maxPrice, true, true), BooleanClause.Occur.FILTER);
  1989. }
  1990. }
  1991. /**
  1992. * 设置批次过滤条件
  1993. *
  1994. * @param filters 指定的过滤条件
  1995. * @param query 原查询
  1996. */
  1997. private void setProductsFilter(Map<FilterField, Object> filters, BooleanQuery query) {
  1998. Object status;
  1999. // 筛选状态
  2000. if (!CollectionUtils.isEmpty(filters) && !StringUtils.isEmpty(filters.get(FilterField.PRODUCT_STATUS))) {
  2001. // 如果明确指定了状态,则直接过滤物料
  2002. status = filters.get(FilterField.PRODUCT_STATUS);
  2003. filter(status, SearchConstants.PRODUCT_PRIVATE_B2CENABLED_FIELD, query);
  2004. } else {
  2005. // 如果未明确指定状态,则使用默认状态分情况进行过滤(结果中包括没有批次的器件)
  2006. status = Arrays.asList(V_Products.VALID_STATUS);
  2007. // 或者批次 id 为空(此时是器件)
  2008. BooleanQuery q2 = new BooleanQuery();
  2009. BooleanQuery booleanQuery = new BooleanQuery();
  2010. booleanQuery.add(q2, Occur.SHOULD);
  2011. query.add(booleanQuery, Occur.FILTER);
  2012. }
  2013. }
  2014. /**
  2015. * 批次排序规则
  2016. */
  2017. private Sort sortGoods(String keyword, com.uas.search.constant.model.Sort sort) {
  2018. List<SortField> sortFieldList = new ArrayList<>();
  2019. sortFieldList.add(SortField.FIELD_SCORE);
  2020. if (sort != null) {
  2021. com.uas.search.constant.model.Sort.Field field = sort.getField();
  2022. if (field == null) {
  2023. throw new IllegalArgumentException("排序字段不可为空:" + sort);
  2024. }
  2025. boolean reverse = sort.isReverse();
  2026. if (field == RESERVE) {
  2027. // 库存
  2028. sortFieldList.addAll(Arrays.asList(
  2029. // 降序时,默认值为最小值;升序时,默认值为最大值
  2030. sortField(SearchConstants.GOODS_GO_RESERVE_FIELD, Type.DOUBLE, reverse, reverse ? Double.MIN_VALUE : Double.MAX_VALUE),
  2031. sortField(SearchConstants.GOODS_CMP_WEIGHT_FIELD, Type.DOUBLE, true, Double.MIN_VALUE),
  2032. sortField(SearchConstants.GOODS_BR_WEIGHT_FIELD, Type.DOUBLE, true, Double.MIN_VALUE),
  2033. sortField(SearchConstants.GOODS_GO_MINPRICERMB_FIELD, Type.DOUBLE, false, Double.MAX_VALUE),
  2034. sortField(SearchConstants.GOODS_GO_MINPRICEUSD_FIELD, Type.DOUBLE, false, Double.MAX_VALUE)
  2035. ));
  2036. } else if (field == PRICE) {
  2037. // 价格
  2038. sortFieldList.addAll(Arrays.asList(
  2039. // 降序时,默认值为最小值;升序时,默认值为最大值
  2040. sortField(SearchConstants.GOODS_GO_MINPRICERMB_FIELD, Type.DOUBLE, reverse, reverse ? Double.MIN_VALUE : Double.MAX_VALUE),
  2041. sortField(SearchConstants.GOODS_GO_MINPRICEUSD_FIELD, Type.DOUBLE, reverse, reverse ? Double.MIN_VALUE : Double.MAX_VALUE),
  2042. sortField(SearchConstants.GOODS_CMP_WEIGHT_FIELD, Type.DOUBLE, true, Double.MIN_VALUE),
  2043. sortField(SearchConstants.GOODS_BR_WEIGHT_FIELD, Type.DOUBLE, true, Double.MIN_VALUE),
  2044. sortField(SearchConstants.GOODS_GO_RESERVE_FIELD, Type.DOUBLE, true, Double.MIN_VALUE)
  2045. ));
  2046. } else {
  2047. throw new IllegalArgumentException("不支持该排序方式:" + field);
  2048. }
  2049. } else {
  2050. // 默认(综合排序)
  2051. sortFieldList.addAll(Arrays.asList(
  2052. sortField(SearchConstants.GOODS_CMP_WEIGHT_FIELD, Type.DOUBLE, true, Double.MIN_VALUE),
  2053. sortField(SearchConstants.GOODS_BR_WEIGHT_FIELD, Type.DOUBLE, true, Double.MIN_VALUE),
  2054. sortField(SearchConstants.GOODS_GO_MINPRICERMB_FIELD, Type.DOUBLE, false, Double.MAX_VALUE),
  2055. sortField(SearchConstants.GOODS_GO_MINPRICEUSD_FIELD, Type.DOUBLE, false, Double.MAX_VALUE),
  2056. sortField(SearchConstants.GOODS_GO_RESERVE_FIELD, Type.DOUBLE, true, Double.MIN_VALUE),
  2057. sortField(SearchConstants.GOODS_GO_MINDELIVERY_FIELD, Type.LONG, false, Long.MAX_VALUE)
  2058. ));
  2059. }
  2060. sortFieldList.addAll(Arrays.asList(
  2061. // 如果仍然无法得到正确结果,就根据按照型号等顺序严格排列
  2062. // new SortField(SearchConstants.GOODS_CMP_CODE_FIELD, new StringFieldComparatorSource(keyword, false)),
  2063. new SortField(GOODS_PR_PCMPCODE_FIELD, new StringFieldComparatorSource(keyword, false)),
  2064. new SortField(GOODS_BR_NAME_EN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(keyword, false)),
  2065. new SortField(GOODS_BR_NAME_CN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(keyword, false)),
  2066. new SortField(SearchConstants.GOODS_PR_KIND_FIELD, new StringFieldComparatorSource(keyword, false)),
  2067. new SortField(SearchConstants.GOODS_CMP_CODE_FIELD, new StringFieldComparatorSource(keyword, false)),
  2068. // new SortField(SearchConstants.GOODS_KI_NAME_CN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(keyword, false)),
  2069. new SortField(SearchConstants.GOODS_GO_ATTACH_FIELD, Type.DOUBLE, true),
  2070. sortField(SearchConstants.GOODS_GO_UPDATE_DATE_FIELD, Type.LONG, true, Long.MIN_VALUE)
  2071. ));
  2072. SortField[] sortFields = new SortField[sortFieldList.size()];
  2073. sortFieldList.toArray(sortFields);
  2074. return new Sort(sortFields);
  2075. }
  2076. @Override
  2077. public Map<String, Object> getPCBGoodsIds(String keyword, PageParams pageParams) throws IOException {
  2078. keyword = recursivelyGetPCBGoodsIds(keyword);
  2079. // 因为器件、属性值等的数据量远比类目、品牌大得多,而且器件搜索可能还需进行分页,
  2080. // 所以涉及器件、属性值的搜索,大都不能像类目和品牌一样直接利用SearchUtils.getDocuments方法
  2081. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.PCB_GOODS_TABLE_NAME);
  2082. if (pageParams == null) {
  2083. pageParams = new PageParams();
  2084. }
  2085. if (pageParams.getPage() <= 0)
  2086. pageParams.setPage(1);
  2087. if (pageParams.getSize() <= 0)
  2088. pageParams.setSize(20);
  2089. Map<String, Object> map = new HashMap<String, Object>();
  2090. List<Long> pcbIds = new ArrayList<>();
  2091. List<Long> goIds = new ArrayList<>();
  2092. try {
  2093. BooleanQuery booleanQuery = queryPCBGoods(keyword);
  2094. setPCBGoodsFilter(pageParams.getFilters(), booleanQuery);
  2095. logger.info(booleanQuery.toString());
  2096. Sort sort = sortPCBGoods(keyword, pageParams.getSort());
  2097. TopDocs hits;
  2098. if (pageParams.getPage() > 1) { // 不是第一页
  2099. TopDocs previousHits = indexSearcher.search(booleanQuery,
  2100. (pageParams.getPage() - 1) * pageParams.getSize(), sort, true, false);
  2101. int totalHits = previousHits.totalHits;
  2102. if ((pageParams.getPage() - 1) * pageParams.getSize() >= totalHits) {
  2103. return map;
  2104. }
  2105. ScoreDoc[] previousScoreDocs = previousHits.scoreDocs;
  2106. ScoreDoc after = previousScoreDocs[previousScoreDocs.length - 1];
  2107. hits = indexSearcher.searchAfter(after, booleanQuery, pageParams.getSize(), sort, true, false);
  2108. } else {
  2109. hits = indexSearcher.search(booleanQuery, pageParams.getSize(), sort, true, false);
  2110. }
  2111. // 数据量太大,需要指定将获取的数据(以免载入不必要的数据,降低速度)
  2112. Set<String> fieldsToLoad = new HashSet<>();
  2113. fieldsToLoad.add(SearchConstants.PCB_GOODS_PCB_ID_FIELD);
  2114. fieldsToLoad.add(SearchConstants.PCB_GOODS_GO_ID_FIELD);
  2115. ScoreDoc[] scoreDocs = hits.scoreDocs;
  2116. for (ScoreDoc scoreDoc : scoreDocs) {
  2117. Document document = indexSearcher.doc(scoreDoc.doc, fieldsToLoad);
  2118. String pcbId = document.get(SearchConstants.PCB_GOODS_PCB_ID_FIELD);
  2119. pcbIds.add(StringUtils.isEmpty(pcbId) || pcbId.equals(ObjectToDocumentUtils.NULL_VALUE) ? null : Long.valueOf(pcbId));
  2120. String goId = document.get(SearchConstants.PCB_GOODS_GO_ID_FIELD);
  2121. goIds.add(StringUtils.isEmpty(goId) || goId.equals(ObjectToDocumentUtils.NULL_VALUE) ? null : Long.valueOf(goId));
  2122. }
  2123. map.put("pcbIds", pcbIds);
  2124. map.put("goodsIds", goIds);
  2125. map.put("page", pageParams.getPage());
  2126. map.put("size", pageParams.getSize());
  2127. map.put("total", hits.totalHits);
  2128. } catch (IOException e) {
  2129. logger.error("", e);
  2130. } finally {
  2131. releaseIndexSearcher(indexSearcher);
  2132. }
  2133. return map;
  2134. }
  2135. /**
  2136. * 递归查询 PCB 批次(如果没有结果,则降低精度,直至长度为 1)
  2137. *
  2138. * @param keyword 关键词
  2139. * @return 最后一次搜索的关键词
  2140. */
  2141. private String recursivelyGetPCBGoodsIds(String keyword) throws IOException {
  2142. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.PCB_GOODS_TABLE_NAME);
  2143. try {
  2144. BooleanQuery booleanQuery = queryPCBGoods(keyword);
  2145. logger.info(booleanQuery.toString());
  2146. TotalHitCountCollector collector = new TotalHitCountCollector();
  2147. indexSearcher.search(booleanQuery, collector);
  2148. // 如果没有结果,则降低精度,直至 keyword 长度为 1
  2149. if (collector.getTotalHits() < 1 && !SearchUtils.isKeywordInvalid(keyword) && keyword.length() > 1) {
  2150. return recursivelyGetPCBGoodsIds(keyword.substring(0, keyword.length() - 1));
  2151. }
  2152. return keyword;
  2153. } finally {
  2154. releaseIndexSearcher(indexSearcher);
  2155. }
  2156. }
  2157. /**
  2158. * 设置 PCB 批次过滤条件
  2159. *
  2160. * @param filters 指定的过滤条件
  2161. * @param query 原查询
  2162. */
  2163. private void setPCBGoodsFilter(Map<FilterField, Object> filters, BooleanQuery query) {
  2164. Object status;
  2165. // 筛选状态
  2166. if (!CollectionUtils.isEmpty(filters) && !StringUtils.isEmpty(filters.get(FilterField.GOODS_STATUS))) {
  2167. // 如果明确指定了状态,则直接过滤批次(结果中不包括没有批次的 PCB)
  2168. status = filters.get(FilterField.GOODS_STATUS);
  2169. filter(status, SearchConstants.PCB_GOODS_GO_STATUS_FIELD, query);
  2170. } else {
  2171. // 如果未明确指定状态,则使用默认状态分情况进行过滤(结果中包括没有批次的 PCB)
  2172. status = Arrays.asList(TradeGoods.VALID_STATUS);
  2173. // 批次 id 不为空时,对状态过滤
  2174. Query goNullQuery = SearchUtils.getNullQuery(SearchConstants.PCB_GOODS_GO_ID_FIELD);
  2175. BooleanQuery q1 = new BooleanQuery();
  2176. q1.add(goNullQuery, Occur.MUST_NOT);
  2177. filter(status, SearchConstants.PCB_GOODS_GO_STATUS_FIELD, q1);
  2178. // 或者批次 id 为空(此时是PCB)
  2179. BooleanQuery q2 = new BooleanQuery();
  2180. q2.add(SearchUtils.getNullQuery(SearchConstants.PCB_GOODS_PCB_ID_FIELD), Occur.MUST_NOT);
  2181. q2.add(goNullQuery, Occur.MUST);
  2182. BooleanQuery booleanQuery = new BooleanQuery();
  2183. booleanQuery.add(q1, Occur.SHOULD);
  2184. booleanQuery.add(q2, Occur.SHOULD);
  2185. query.add(booleanQuery, Occur.FILTER);
  2186. }
  2187. if (CollectionUtils.isEmpty(filters)) {
  2188. return;
  2189. }
  2190. // 筛选类目
  2191. if (!StringUtils.isEmpty(filters.get(FilterField.GOODS_KINDID))) {
  2192. filter(filters.get(FilterField.GOODS_KINDID), SearchConstants.PCB_GOODS_KI_ID_FIELD, query);
  2193. }
  2194. // 筛选品牌
  2195. if (!StringUtils.isEmpty(filters.get(FilterField.GOODS_BRANDID))) {
  2196. filter(filters.get(FilterField.GOODS_BRANDID), SearchConstants.PCB_GOODS_BR_ID_FIELD, query);
  2197. }
  2198. // 筛选货币
  2199. if (!StringUtils.isEmpty(filters.get(FilterField.GOODS_CRNAME))) {
  2200. filter(filters.get(FilterField.GOODS_CRNAME), SearchConstants.PCB_GOODS_CRNAME_FIELD, query);
  2201. }
  2202. // 价格筛选
  2203. Object minPriceRmb = filters.get(FilterField.GOODS_MINPRICERMB);
  2204. Object maxPriceRmb = filters.get(FilterField.GOODS_MAXPRICERMB);
  2205. Object minPriceUsd = filters.get(FilterField.GOODS_MINPRICEUSD);
  2206. Object maxPriceUsd = filters.get(FilterField.GOODS_MAXPRICEUSD);
  2207. // 筛选人民币价格
  2208. if (!StringUtils.isEmpty(minPriceRmb) || !StringUtils.isEmpty(maxPriceRmb)) {
  2209. Double minPrice = null;
  2210. Double maxPrice = null;
  2211. if (!StringUtils.isEmpty(minPriceRmb)) {
  2212. minPrice = Double.valueOf(minPriceRmb.toString());
  2213. }
  2214. if (!StringUtils.isEmpty(maxPriceRmb)) {
  2215. maxPrice = Double.valueOf(maxPriceRmb.toString());
  2216. }
  2217. query.add(NumericRangeQuery.newDoubleRange(SearchConstants.PCB_GOODS_GO_MINPRICERMB_FIELD,
  2218. minPrice, maxPrice, true, true), BooleanClause.Occur.FILTER);
  2219. }
  2220. // 筛选美元价格
  2221. if (!StringUtils.isEmpty(minPriceUsd) || !StringUtils.isEmpty(maxPriceUsd)) {
  2222. Double minPrice = null;
  2223. Double maxPrice = null;
  2224. if (!StringUtils.isEmpty(minPriceUsd)) {
  2225. minPrice = Double.valueOf(minPriceUsd.toString());
  2226. }
  2227. if (!StringUtils.isEmpty(maxPriceUsd)) {
  2228. maxPrice = Double.valueOf(maxPriceUsd.toString());
  2229. }
  2230. query.add(NumericRangeQuery.newDoubleRange(SearchConstants.PCB_GOODS_GO_MINPRICEUSD_FIELD,
  2231. minPrice, maxPrice, true, true), BooleanClause.Occur.FILTER);
  2232. }
  2233. }
  2234. /**
  2235. * PCB 批次排序规则
  2236. */
  2237. private Sort sortPCBGoods(String keyword, com.uas.search.constant.model.Sort sort) {
  2238. List<SortField> sortFieldList = new ArrayList<>();
  2239. sortFieldList.add(SortField.FIELD_SCORE);
  2240. if (sort != null) {
  2241. com.uas.search.constant.model.Sort.Field field = sort.getField();
  2242. if (field == null) {
  2243. throw new IllegalArgumentException("排序字段不可为空:" + sort);
  2244. }
  2245. boolean reverse = sort.isReverse();
  2246. if (field == RESERVE) {
  2247. // 库存
  2248. sortFieldList.addAll(Arrays.asList(
  2249. // 降序时,默认值为最小值;升序时,默认值为最大值
  2250. sortField(SearchConstants.PCB_GOODS_GO_RESERVE_FIELD, Type.DOUBLE, reverse, reverse ? Double.MIN_VALUE : Double.MAX_VALUE),
  2251. sortField(SearchConstants.PCB_GOODS_GO_MINPRICERMB_FIELD, Type.DOUBLE, false, Double.MAX_VALUE),
  2252. sortField(SearchConstants.PCB_GOODS_GO_MINPRICEUSD_FIELD, Type.DOUBLE, false, Double.MAX_VALUE)
  2253. ));
  2254. } else if (field == PRICE) {
  2255. // 价格
  2256. sortFieldList.addAll(Arrays.asList(
  2257. // 降序时,默认值为最小值;升序时,默认值为最大值
  2258. sortField(SearchConstants.PCB_GOODS_GO_MINPRICERMB_FIELD, Type.DOUBLE, reverse, reverse ? Double.MIN_VALUE : Double.MAX_VALUE),
  2259. sortField(SearchConstants.PCB_GOODS_GO_MINPRICEUSD_FIELD, Type.DOUBLE, reverse, reverse ? Double.MIN_VALUE : Double.MAX_VALUE),
  2260. sortField(SearchConstants.PCB_GOODS_GO_RESERVE_FIELD, Type.DOUBLE, true, Double.MIN_VALUE)
  2261. ));
  2262. } else {
  2263. throw new IllegalArgumentException("不支持该排序方式:" + field);
  2264. }
  2265. } else {
  2266. // 默认(综合排序)
  2267. sortFieldList.addAll(Arrays.asList(
  2268. sortField(SearchConstants.PCB_GOODS_GO_MINPRICERMB_FIELD, Type.DOUBLE, false, Double.MAX_VALUE),
  2269. sortField(SearchConstants.PCB_GOODS_GO_MINPRICEUSD_FIELD, Type.DOUBLE, false, Double.MAX_VALUE),
  2270. sortField(SearchConstants.PCB_GOODS_GO_RESERVE_FIELD, Type.DOUBLE, true, Double.MIN_VALUE),
  2271. sortField(SearchConstants.PCB_GOODS_GO_MINDELIVERY_FIELD, Type.LONG, false, Long.MAX_VALUE)
  2272. ));
  2273. }
  2274. sortFieldList.addAll(Arrays.asList(
  2275. // 如果仍然无法得到正确结果,就根据按照型号等顺序严格排列
  2276. new SortField(SearchConstants.PCB_GOODS_PR_PCMPCODE_FIELD, new StringFieldComparatorSource(keyword, false)),
  2277. new SortField(SearchConstants.PCB_GOODS_BR_NAME_EN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(keyword, false)),
  2278. new SortField(SearchConstants.PCB_GOODS_BR_NAME_CN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(keyword, false)),
  2279. new SortField(SearchConstants.PCB_GOODS_KI_NAME_CN_UNTOKENIZED_FIELD, new StringFieldComparatorSource(keyword, false)),
  2280. sortField(SearchConstants.PCB_GOODS_GO_UPDATE_DATE_FIELD, Type.LONG, true, Long.MIN_VALUE)
  2281. ));
  2282. SortField[] sortFields = new SortField[sortFieldList.size()];
  2283. sortFieldList.toArray(sortFields);
  2284. return new Sort(sortFields);
  2285. }
  2286. @Override
  2287. public List<Map<String, Object>> collectBySearchGoods(String keyword, CollectField collectedField,
  2288. Map<FilterField, Object> filters) throws IOException {
  2289. List<String> keywordFields = new ArrayList<>();
  2290. // 先根据品牌搜索,品牌不存在再搜索型号等
  2291. keywordFields.add(GOODS_BR_NAME_CN_UNTOKENIZED_FIELD);
  2292. keywordFields.add(GOODS_BR_NAME_EN_UNTOKENIZED_FIELD);
  2293. List<Map<String, Object>> result = collectBySearchGoods(keyword, keywordFields, false, collectedField, filters);
  2294. if (CollectionUtils.isEmpty(result)) {
  2295. keyword = recursivelyGetGoodsIds(keyword, null, true);
  2296. result = collectBySearchGoods(keyword, null, true, collectedField, filters);
  2297. }
  2298. return result;
  2299. }
  2300. /**
  2301. * 获取卖家信息
  2302. * @param keyword 关键词
  2303. * @param keywordFields 搜索字段
  2304. * @param page 页码
  2305. * @param size 页数
  2306. * @param tokenized 是否分词
  2307. * @param duplicate 去重类型
  2308. * @return
  2309. */
  2310. private Map<String, SPage<Object>> querySellers(String keyword, Map<String, List<String>> keywordFields, Integer page, Integer size, boolean tokenized, String duplicate) throws IOException {
  2311. if (SearchUtils.isKeywordInvalid(keyword)) {
  2312. throw new IllegalArgumentException("搜索关键词无效:" + keyword);
  2313. }
  2314. Map<String, SPage<Object>> map = new HashMap<>();
  2315. // TODO 卖家排序
  2316. Sort sort = null;
  2317. // 未指定搜索的字段,则采用默认搜索逻辑
  2318. if (CollectionUtils.isEmpty(keywordFields)) {
  2319. Map<String, Query> boostQuerys = setSellersBoost(keyword);
  2320. } else {
  2321. Map<String, BooleanQuery> queryMap = queryKindForSellers(keyword, keywordFields, tokenized);
  2322. BooleanQuery goodsQuery = queryMap.get("goodsQuery");
  2323. BooleanQuery productsQuery = queryMap.get("productsQuery");
  2324. // 现货卖家
  2325. SPage<Object> stock = querySellers(SearchConstants.GOODS_TABLE_NAME, page, size, goodsQuery, sort, duplicate);
  2326. if (duplicate.equals(SearchConstants.KIND) && (stock == null || CollectionUtils.isEmpty(stock.getContent()))) {
  2327. keyword = recursivelyKindsForSellers(SearchConstants.GOODS_TABLE_NAME, keyword, goodsQuery, duplicate);
  2328. goodsQuery = queryKindForSellers(keyword, keywordFields, tokenized).get("goodsQuery");
  2329. stock = querySellers(SearchConstants.GOODS_TABLE_NAME, page, size, goodsQuery, sort, duplicate);
  2330. }
  2331. map.put("stock", stock);
  2332. logger.info(goodsQuery.toString());
  2333. // 期货卖家
  2334. SPage<Object> futures = querySellers(SearchConstants.PRODUCTS_PRIVATE_TABLE_NAME, page, size, productsQuery, sort, duplicate);
  2335. if (duplicate.equals(SearchConstants.KIND) && (futures == null || CollectionUtils.isEmpty(futures.getContent()))) {
  2336. keyword = recursivelyKindsForSellers(SearchConstants.PRODUCTS_PRIVATE_TABLE_NAME, keyword, productsQuery, duplicate);
  2337. productsQuery = queryKindForSellers(keyword, keywordFields, tokenized).get("productsQuery");
  2338. futures = querySellers(SearchConstants.PRODUCTS_PRIVATE_TABLE_NAME, page, size, productsQuery, sort, duplicate);
  2339. }
  2340. map.put("futures", futures);
  2341. logger.info(productsQuery.toString());
  2342. }
  2343. return map;
  2344. }
  2345. private Map<String, BooleanQuery> queryKindForSellers(String keyword, Map<String, List<String>> keywordFields, boolean tokenized) {
  2346. Map<String, BooleanQuery> booleanQueryMap = new HashMap<>();
  2347. BooleanQuery goodsQuery = new BooleanQuery();
  2348. BooleanQuery productsQuery = new BooleanQuery();
  2349. BooleanQuery goodsBooleanQuery = new BooleanQuery();
  2350. BooleanQuery productsBooleanQuery = new BooleanQuery();
  2351. for (String keywordField : keywordFields.get("goods")) {
  2352. if (!tokenized) {
  2353. goodsBooleanQuery.add(new TermQuery(new Term(keywordField, keyword.toLowerCase())), BooleanClause.Occur.SHOULD);
  2354. } else {
  2355. goodsBooleanQuery.add(SearchUtils.getBooleanQuery(keywordField, keyword), BooleanClause.Occur.SHOULD);
  2356. }
  2357. goodsQuery.add(goodsBooleanQuery, Occur.MUST);
  2358. }
  2359. for (String keywordField : keywordFields.get("products")) {
  2360. if (!tokenized) {
  2361. productsBooleanQuery.add(new TermQuery(new Term(keywordField, keyword.toLowerCase())), BooleanClause.Occur.SHOULD);
  2362. } else {
  2363. productsBooleanQuery.add(SearchUtils.getBooleanQuery(keywordField, keyword.toLowerCase()), BooleanClause.Occur.SHOULD);
  2364. }
  2365. productsQuery.add(productsBooleanQuery, Occur.MUST);
  2366. }
  2367. booleanQueryMap.put("goodsQuery", goodsQuery);
  2368. booleanQueryMap.put("productsQuery", productsQuery);
  2369. return booleanQueryMap;
  2370. }
  2371. private SPage<Object> querySellers(String indexName, Integer page, Integer size, BooleanQuery query, Sort sort, String duplicateType) throws IOException {
  2372. SPage<Object> sPage = new SPage<>();
  2373. if (page != null && page > 0) {
  2374. sPage.setPage(page);
  2375. } else {
  2376. sPage.setPage(PAGE_INDEX);
  2377. sPage.setFirst(true);
  2378. }
  2379. size = size != null && size > 0 ? size : PAGE_SIZE;
  2380. sPage.setSize(size);
  2381. DuplicateFilter duplicateFilter = null;
  2382. if (!StringUtils.isEmpty(duplicateType)) {
  2383. if (duplicateType.equals(SearchConstants.BRAND)) {
  2384. duplicateFilter = new DuplicateFilter(indexName.equals(SearchConstants.GOODS_TABLE_NAME) ? SearchConstants.GOODS_PR_PBRAND_EN_CN_STUUID_UNTOKENIZED_FIELD : SearchConstants.PRODUCT_PRIVATE_PBRAND_ENUU_FIELD);
  2385. } else if (duplicateType.equals(SearchConstants.KIND)) {
  2386. duplicateFilter = new DuplicateFilter(indexName.equals(SearchConstants.GOODS_TABLE_NAME) ? SearchConstants.GOODS_KIND_STUUID_UNTOKENIZED_FIELD : SearchConstants.PRODUCT_PRIVATE_KIND_ENUU_FIELD);
  2387. }
  2388. }
  2389. if (indexName.equals(SearchConstants.GOODS_TABLE_NAME)) {
  2390. Object status = Arrays.asList(TradeGoods.VALID_STATUS);
  2391. // 批次 id 不为空时,对状态过滤
  2392. filter(status, SearchConstants.GOODS_GO_STATUS_FIELD, query);
  2393. } else if (indexName.equals(SearchConstants.PRODUCTS_PRIVATE_TABLE_NAME)) {
  2394. Object status = Arrays.asList(V_Products.VALID_STATUS);
  2395. // 批次 id 不为空时,对状态过滤
  2396. filter(status, SearchConstants.PRODUCT_PRIVATE_B2CENABLED_FIELD, query);
  2397. }
  2398. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(indexName);
  2399. TopDocs topDocs;
  2400. try {
  2401. if (sort == null) {
  2402. topDocs = duplicateFilter == null ? indexSearcher.search(query, DUPLICATE_PAGE_SIZE) : indexSearcher.search(query, duplicateFilter, DUPLICATE_PAGE_SIZE);
  2403. } else {
  2404. topDocs = duplicateFilter == null ? indexSearcher.search(query, DUPLICATE_PAGE_SIZE, sort) : indexSearcher.search(query, duplicateFilter, DUPLICATE_PAGE_SIZE, sort);
  2405. }
  2406. int totalHits = topDocs.totalHits;
  2407. List<Document> documents = new ArrayList<>();
  2408. for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
  2409. documents.add(indexSearcher.doc(scoreDoc.doc));
  2410. }
  2411. List<Object> content = new ArrayList<>();
  2412. List<Object> results = new ArrayList<>();
  2413. if (indexName.equals(SearchConstants.GOODS_TABLE_NAME)) {
  2414. for (Document document : documents) {
  2415. results.add(document.get(SearchConstants.GOODS_ST_UUID_FIELD));
  2416. }
  2417. } else if (indexName.equals(SearchConstants.PRODUCTS_PRIVATE_TABLE_NAME)) {
  2418. for (Document document : documents) {
  2419. results.add(Long.parseLong(document.get(SearchConstants.PRODUCT_PRIVATE_ENUU_FIELD)));
  2420. }
  2421. }
  2422. Map<String, List<Object>> map = new HashMap<>();
  2423. // 结果去重
  2424. for (Object object : results) {
  2425. if (map.get(String.valueOf(object)) == null) {
  2426. List<Object> storeUuids = new ArrayList<>();
  2427. storeUuids.add(String.valueOf(object));
  2428. map.put(String.valueOf(object), storeUuids);
  2429. } else {
  2430. map.get(String.valueOf(object)).add(object);
  2431. totalHits --;
  2432. }
  2433. }
  2434. // 按批次、物料数量排序
  2435. List<Map.Entry<String, List<Object>>> stuuids = new ArrayList<>(map.entrySet());
  2436. Collections.sort(stuuids, new Comparator<Map.Entry<String, List<Object>>> () {
  2437. @Override
  2438. public int compare(Map.Entry<String, List<Object>> o1, Map.Entry<String, List<Object>> o2) {
  2439. return o1.getValue().size() - o2.getValue().size();
  2440. }
  2441. });
  2442. // 设置总元素个数、页数等信息
  2443. int totalPage = (int) Math.ceil(totalHits / (1.0 * sPage.getSize()));
  2444. sPage.setTotalPage(totalPage);
  2445. int filterPage = sPage.getPage() < totalPage ? sPage.getPage() : totalPage;
  2446. sPage.setPage(filterPage);
  2447. if (!CollectionUtils.isEmpty(stuuids)) {
  2448. for (int i = (filterPage - 1) * size; i < (((filterPage - 1) * size + size > stuuids.size()) ? stuuids.size() : ((filterPage - 1) * size + size)); i ++) {
  2449. content.add(stuuids.get(i).getKey());
  2450. }
  2451. }
  2452. if (totalPage == sPage.getPage()) {
  2453. sPage.setLast(true);
  2454. }
  2455. sPage.setSize(size);
  2456. sPage.setTotalElement(totalHits);
  2457. sPage.setContent(content);
  2458. } finally {
  2459. releaseIndexSearcher(indexSearcher);
  2460. }
  2461. return sPage;
  2462. }
  2463. private Map<String, Query> setSellersBoost(String keyword) {
  2464. // TODO
  2465. Map<String, Query> queryMap = new HashMap<>();
  2466. BooleanQuery goodsQuery = new BooleanQuery();
  2467. BooleanQuery productsQuery = new BooleanQuery();
  2468. return null;
  2469. }
  2470. private SortField[] sortSellers(String keyword) {
  2471. // TODO
  2472. return null;
  2473. }
  2474. /**
  2475. * @param keyword
  2476. * @param keywordFields
  2477. * 要查询的字段
  2478. * @param tokenized
  2479. * 是否分词
  2480. * @param collectedField
  2481. * @param filters
  2482. * @return
  2483. */
  2484. private List<Map<String, Object>> collectBySearchGoods(String keyword, List<String> keywordFields,
  2485. Boolean tokenized, CollectField collectedField, Map<FilterField, Object> filters) {
  2486. if (collectedField == null) {
  2487. throw new IllegalArgumentException("参数为空:collectedField");
  2488. }
  2489. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.GOODS_TABLE_NAME);
  2490. List<Map<String, Object>> result = new ArrayList<>();
  2491. try {
  2492. BooleanQuery booleanQuery = queryGoods(keyword, keywordFields, tokenized);
  2493. setGoodsFilter(filters, booleanQuery);
  2494. logger.info(booleanQuery.toString());
  2495. // 统计
  2496. String uniqueField;
  2497. Set<String> fieldsToLoad = new HashSet<>();
  2498. switch (collectedField) {
  2499. case GOODS_KIND:
  2500. uniqueField = SearchConstants.GOODS_KI_ID_FIELD;
  2501. fieldsToLoad.add(SearchConstants.GOODS_KI_ID_FIELD);
  2502. fieldsToLoad.add(GOODS_KI_NAME_CN_FIELD);
  2503. break;
  2504. case GOODS_BRAND:
  2505. uniqueField = SearchConstants.GOODS_BR_ID_FIELD;
  2506. fieldsToLoad.add(SearchConstants.GOODS_BR_ID_FIELD);
  2507. fieldsToLoad.add(SearchConstants.GOODS_BR_UUID_FIELD);
  2508. fieldsToLoad.add(GOODS_BR_NAME_CN_FIELD);
  2509. fieldsToLoad.add(GOODS_BR_NAME_EN_FIELD);
  2510. break;
  2511. case GOODS_STORE_TYPE:
  2512. uniqueField = SearchConstants.GOODS_ST_TYPE_FIELD;
  2513. fieldsToLoad.add(SearchConstants.GOODS_ST_TYPE_FIELD);
  2514. break;
  2515. case GOODS_CRNAME:
  2516. uniqueField = SearchConstants.GOODS_CRNAME_FIELD;
  2517. fieldsToLoad.add(SearchConstants.GOODS_CRNAME_FIELD);
  2518. break;
  2519. default:
  2520. throw new IllegalArgumentException("不支持该统计字段:" + collectedField);
  2521. }
  2522. GoodsGroupCollector collector = new GoodsGroupCollector(uniqueField, fieldsToLoad);
  2523. indexSearcher.search(booleanQuery, collector);
  2524. result = collector.getValues();
  2525. } catch (IOException e) {
  2526. logger.error("", e);
  2527. } finally {
  2528. releaseIndexSearcher(indexSearcher);
  2529. }
  2530. return result;
  2531. }
  2532. /**
  2533. * 获取查询批次的query
  2534. *
  2535. * @param keyword
  2536. * @param keywordFields
  2537. * @param tokenized
  2538. * @return
  2539. */
  2540. private BooleanQuery queryGoods(String keyword, List<String> keywordFields, Boolean tokenized) {
  2541. BooleanQuery booleanQuery = new BooleanQuery();
  2542. Query goNullQuery = SearchUtils.getNullQuery(SearchConstants.GOODS_GO_ID_FIELD);
  2543. booleanQuery.add(goNullQuery, Occur.MUST_NOT);
  2544. if (!SearchUtils.isKeywordInvalid(keyword)) {
  2545. // 未指定搜索的字段,则采用默认搜索逻辑
  2546. if (CollectionUtils.isEmpty(keywordFields)) {
  2547. booleanQuery.add(setGoodsBoost(keyword), BooleanClause.Occur.MUST);
  2548. } else {
  2549. BooleanQuery booleanQuery2 = new BooleanQuery();
  2550. for (String keywordField : keywordFields) {
  2551. // 是否分词
  2552. if (tokenized == null || !tokenized.booleanValue()) {
  2553. booleanQuery2.add(new TermQuery(new Term(keywordField, keyword.toLowerCase())), BooleanClause.Occur.SHOULD);
  2554. } else {
  2555. booleanQuery2.add(SearchUtils.getBooleanQuery(keywordField, keyword), BooleanClause.Occur.SHOULD);
  2556. }
  2557. }
  2558. booleanQuery.add(booleanQuery2, BooleanClause.Occur.MUST);
  2559. }
  2560. }
  2561. return booleanQuery;
  2562. }
  2563. private BooleanQuery queryProducts(String keyword, List<String> keywordFields, Boolean tokenized) {
  2564. BooleanQuery booleanQuery = new BooleanQuery();
  2565. BooleanQuery q1 = new BooleanQuery();
  2566. q1.add(new TermQuery(new Term(SearchConstants.PRODUCT_PRIVATE_B2CENABLED_FIELD, String.valueOf(1))), BooleanClause.Occur.MUST);
  2567. booleanQuery.add(q1, BooleanClause.Occur.MUST);
  2568. if (!SearchUtils.isKeywordInvalid(keyword)) {
  2569. // 未指定搜索的字段,则采用默认搜索逻辑
  2570. if (!CollectionUtils.isEmpty(keywordFields)) {
  2571. BooleanQuery booleanQuery2 = new BooleanQuery();
  2572. for (String keywordField : keywordFields) {
  2573. // 是否分词
  2574. if (tokenized == null || !tokenized.booleanValue()) {
  2575. booleanQuery2.add(new TermQuery(new Term(keywordField, keyword.toLowerCase())), BooleanClause.Occur.SHOULD);
  2576. } else {
  2577. booleanQuery2.add(SearchUtils.getBooleanQuery(keywordField, keyword), BooleanClause.Occur.SHOULD);
  2578. }
  2579. }
  2580. booleanQuery.add(booleanQuery2, BooleanClause.Occur.MUST);
  2581. }
  2582. }
  2583. return booleanQuery;
  2584. }
  2585. /**
  2586. * 同时搜索器件、类目、品牌等,并设置boost
  2587. */
  2588. private Query setGoodsBoost(String keyword) {
  2589. BooleanQuery booleanQuery = new BooleanQuery();
  2590. // 前缀搜索(字段并未分词,进行分词搜索时,会有边界问题,如搜索 'BC807-40,215')
  2591. booleanQuery.add(new PrefixQuery(new Term(SearchConstants.GOODS_CMP_CODE_FIELD, keyword.toLowerCase())), BooleanClause.Occur.SHOULD);
  2592. booleanQuery.add(new PrefixQuery(new Term(GOODS_PR_PCMPCODE_FIELD, keyword.toLowerCase())), BooleanClause.Occur.SHOULD);
  2593. // 原厂型号
  2594. booleanQuery.add(createQuery(SearchConstants.GOODS_CMP_CODE_FIELD, keyword, 100), BooleanClause.Occur.SHOULD);
  2595. // 非标
  2596. booleanQuery.add(createQuery(GOODS_PR_PCMPCODE_FIELD, keyword, 100), Occur.SHOULD);
  2597. // 品牌
  2598. booleanQuery.add(createQuery(GOODS_BR_NAME_CN_FIELD, keyword, 10), BooleanClause.Occur.SHOULD);
  2599. booleanQuery.add(createQuery(GOODS_BR_NAME_EN_FIELD, keyword, 10), BooleanClause.Occur.SHOULD);
  2600. // 类目
  2601. booleanQuery.add(createQuery(GOODS_KI_NAME_CN_FIELD, keyword, 1), BooleanClause.Occur.SHOULD);
  2602. // 属性值
  2603. booleanQuery.add(createQuery(SearchConstants.GOODS_CMP_DESCRIPTION_FIELD, keyword, 1), BooleanClause.Occur.SHOULD);
  2604. return booleanQuery;
  2605. }
  2606. @Override
  2607. public List<Map<String, Object>> collectBySearchPCBGoods(String keyword, CollectField collectedField,
  2608. Map<FilterField, Object> filters) throws IOException {
  2609. keyword = recursivelyGetPCBGoodsIds(keyword);
  2610. if (collectedField == null) {
  2611. throw new IllegalArgumentException("参数为空:collectedField");
  2612. }
  2613. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.PCB_GOODS_TABLE_NAME);
  2614. List<Map<String, Object>> result = new ArrayList<>();
  2615. try {
  2616. BooleanQuery booleanQuery = queryPCBGoods(keyword);
  2617. setPCBGoodsFilter(filters, booleanQuery);
  2618. logger.info(booleanQuery.toString());
  2619. // 统计
  2620. String uniqueField;
  2621. Set<String> fieldsToLoad = new HashSet<>();
  2622. switch (collectedField) {
  2623. case GOODS_KIND:
  2624. uniqueField = SearchConstants.PCB_GOODS_KI_ID_FIELD;
  2625. fieldsToLoad.add(SearchConstants.PCB_GOODS_KI_ID_FIELD);
  2626. fieldsToLoad.add(SearchConstants.PCB_GOODS_KI_NAME_CN_FIELD);
  2627. break;
  2628. case GOODS_BRAND:
  2629. uniqueField = SearchConstants.PCB_GOODS_BR_ID_FIELD;
  2630. fieldsToLoad.add(SearchConstants.PCB_GOODS_BR_ID_FIELD);
  2631. fieldsToLoad.add(SearchConstants.PCB_GOODS_BR_UUID_FIELD);
  2632. fieldsToLoad.add(SearchConstants.PCB_GOODS_BR_NAME_CN_FIELD);
  2633. fieldsToLoad.add(SearchConstants.PCB_GOODS_BR_NAME_EN_FIELD);
  2634. break;
  2635. case GOODS_CRNAME:
  2636. uniqueField = SearchConstants.PCB_GOODS_CRNAME_FIELD;
  2637. fieldsToLoad.add(SearchConstants.PCB_GOODS_CRNAME_FIELD);
  2638. break;
  2639. default:
  2640. throw new IllegalArgumentException("不支持该统计字段:" + collectedField);
  2641. }
  2642. GoodsGroupCollector collector = new GoodsGroupCollector(uniqueField, fieldsToLoad);
  2643. indexSearcher.search(booleanQuery, collector);
  2644. result = collector.getValues();
  2645. } catch (IOException e) {
  2646. logger.error("", e);
  2647. } finally {
  2648. releaseIndexSearcher(indexSearcher);
  2649. }
  2650. return result;
  2651. }
  2652. /**
  2653. * 获取查询 PCB 批次的query
  2654. *
  2655. * @param keyword
  2656. * @return
  2657. */
  2658. private BooleanQuery queryPCBGoods(String keyword) {
  2659. BooleanQuery booleanQuery = new BooleanQuery();
  2660. if (!SearchUtils.isKeywordInvalid(keyword)) {
  2661. booleanQuery.add(setPCBGoodsBoost(keyword), BooleanClause.Occur.MUST);
  2662. }
  2663. return booleanQuery;
  2664. }
  2665. /**
  2666. * 搜索 PCB,设置boost
  2667. */
  2668. private BooleanQuery setPCBGoodsBoost(String keyword) {
  2669. BooleanQuery booleanQuery = new BooleanQuery();
  2670. // 前缀搜索(字段并未分词,进行分词搜索时,会有边界问题,如搜索 'BC807-40,215')
  2671. booleanQuery.add(new PrefixQuery(new Term(SearchConstants.PCB_GOODS_PR_PCMPCODE_FIELD, keyword.toLowerCase())), BooleanClause.Occur.SHOULD);
  2672. // PCB
  2673. booleanQuery.add(createQuery(SearchConstants.PCB_GOODS_PR_PCMPCODE_FIELD, keyword, 100), Occur.SHOULD);
  2674. // 品牌
  2675. booleanQuery.add(createQuery(SearchConstants.PCB_GOODS_BR_NAME_CN_FIELD, keyword, 10), BooleanClause.Occur.SHOULD);
  2676. booleanQuery.add(createQuery(SearchConstants.PCB_GOODS_BR_NAME_EN_FIELD, keyword, 10), BooleanClause.Occur.SHOULD);
  2677. // 类目
  2678. booleanQuery.add(createQuery(SearchConstants.PCB_GOODS_KI_NAME_CN_FIELD, keyword, 1), BooleanClause.Occur.SHOULD);
  2679. return booleanQuery;
  2680. }
  2681. private BooleanQuery createQuery(String field, String keyword, float boost){
  2682. return createQuery(field, keyword, false, boost);
  2683. }
  2684. private BooleanQuery createQuery(String field, String keyword, boolean useRegexpQuery, float boost){
  2685. BooleanQuery booleanQuery = new BooleanQuery();
  2686. if (StringUtils.isEmpty(field) || StringUtils.isEmpty(keyword)) {
  2687. return booleanQuery;
  2688. }
  2689. // 根据空格分隔关键词,分隔的词取或的关系
  2690. String[] array = keyword.split(" ");
  2691. for(String str : array){
  2692. if(!StringUtils.isEmpty(str)){
  2693. booleanQuery.add(SearchUtils.getBooleanQuery(field, str, useRegexpQuery), Occur.SHOULD);
  2694. }
  2695. }
  2696. booleanQuery.setBoost(boost);
  2697. return booleanQuery;
  2698. }
  2699. /**
  2700. * 过滤
  2701. *
  2702. * @param list
  2703. * 过滤值列表
  2704. * @param field
  2705. * 过滤的字段
  2706. * @param booleanQuery
  2707. * 查询条件
  2708. */
  2709. @SuppressWarnings("unchecked")
  2710. private void filter(Object list, String field, BooleanQuery booleanQuery) {
  2711. List<Object> values;
  2712. if (list instanceof List) {
  2713. values = (List<Object>) list;
  2714. }else{
  2715. values = new ArrayList<>();
  2716. values.add(list);
  2717. }
  2718. BooleanQuery booleanQuery2 = new BooleanQuery();
  2719. for (Object value : values) {
  2720. TermQuery query = new TermQuery(new Term(field, value.toString().toLowerCase()));
  2721. booleanQuery2.add(query, BooleanClause.Occur.SHOULD);
  2722. }
  2723. booleanQuery.add(booleanQuery2, BooleanClause.Occur.FILTER);
  2724. }
  2725. @Override
  2726. public V_Products getProduct(Long id) throws IOException {
  2727. return DocumentToObjectUtils.toProduct(
  2728. SearchUtils.getDocumentById(SearchConstants.PRODUCTS_PRIVATE_TABLE_NAME, SearchConstants.PRODUCT_PRIVATE_ID_FIELD, id));
  2729. }
  2730. /**
  2731. * 获取标准型号联想词
  2732. *
  2733. * @param keyword 关键词
  2734. * @param size 尺寸
  2735. * @return
  2736. */
  2737. @Override
  2738. public List<Map<String, Object>> getSimilarPCmpCodes(String keyword, Integer size) {
  2739. size = size == null || size < 1 ? SIMILAR_NUM_EIGHT : size;
  2740. if (SearchUtils.isKeywordInvalid(keyword)) {
  2741. throw new IllegalArgumentException("输入无效:" + keyword);
  2742. }
  2743. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.PRODUCTS_PRIVATE_TABLE_NAME);
  2744. List<Map<String, Object>> products = new ArrayList<>();
  2745. try {
  2746. PrefixQuery prefixQuery = new PrefixQuery(
  2747. new Term(SearchConstants.PRODUCT_PRIVATE_PCMPCODE_FIELD, keyword.toLowerCase()));
  2748. logger.info(prefixQuery.toString());
  2749. Sort sort = new Sort(new SortField(SearchConstants.PRODUCT_PRIVATE_PCMPCODE_FIELD, new StringFieldComparatorSource(keyword)));
  2750. TopDocs hits = indexSearcher.search(prefixQuery, size, sort);
  2751. ScoreDoc[] scoreDocs = hits.scoreDocs;
  2752. for (ScoreDoc scoreDoc : scoreDocs) {
  2753. Set<String> fieldsToLoad = new HashSet<>();
  2754. fieldsToLoad.add(SearchConstants.PRODUCT_PRIVATE_PCMPCODE_FIELD);
  2755. Document document = indexSearcher.doc(scoreDoc.doc, fieldsToLoad);
  2756. Map<String, Object> map = new HashMap<>();
  2757. map.put("pCmpCode", document.get(SearchConstants.PRODUCT_PRIVATE_PCMPCODE_FIELD));
  2758. products.add(map);
  2759. }
  2760. } catch (IOException e) {
  2761. logger.error("", e);
  2762. } finally {
  2763. releaseIndexSearcher(indexSearcher);
  2764. }
  2765. return products;
  2766. }
  2767. /**
  2768. * 获取类目联想词
  2769. *
  2770. * @param keyword 关键词
  2771. * @param size 尺寸
  2772. * @return
  2773. */
  2774. @Override
  2775. public List<Map<String, Object>> getSimilarKind(String keyword, Integer size) {
  2776. size = size == null || size < 1 ? SIMILAR_NUM_EIGHT : size;
  2777. if (SearchUtils.isKeywordInvalid(keyword)) {
  2778. throw new IllegalArgumentException("输入无效:" + keyword);
  2779. }
  2780. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(SearchConstants.PRODUCTS_PRIVATE_TABLE_NAME);
  2781. List<Map<String, Object>> products = new ArrayList<>();
  2782. try {
  2783. PrefixQuery prefixQuery = new PrefixQuery(
  2784. new Term(SearchConstants.PRODUCT_PRIVATE_KIND_FIELD, keyword.toLowerCase()));
  2785. logger.info(prefixQuery.toString());
  2786. Sort sort = new Sort(new SortField(SearchConstants.PRODUCT_PRIVATE_KIND_FIELD, new StringFieldComparatorSource(keyword)));
  2787. TopDocs hits = indexSearcher.search(prefixQuery, size, sort);
  2788. ScoreDoc[] scoreDocs = hits.scoreDocs;
  2789. for (ScoreDoc scoreDoc : scoreDocs) {
  2790. Set<String> fieldsToLoad = new HashSet<>();
  2791. fieldsToLoad.add(SearchConstants.PRODUCT_PRIVATE_KIND_FIELD);
  2792. Document document = indexSearcher.doc(scoreDoc.doc, fieldsToLoad);
  2793. Map<String, Object> map = new HashMap<>();
  2794. map.put("kind", document.get(SearchConstants.PRODUCT_PRIVATE_KIND_FIELD));
  2795. products.add(map);
  2796. }
  2797. } catch (IOException e) {
  2798. logger.error("", e);
  2799. } finally {
  2800. releaseIndexSearcher(indexSearcher);
  2801. }
  2802. return products;
  2803. }
  2804. @Override
  2805. public Kind getKind(Long id) throws IOException {
  2806. return DocumentToObjectUtils.toKind(
  2807. SearchUtils.getDocumentById(SearchConstants.KIND_TABLE_NAME, SearchConstants.KIND_ID_FIELD, id));
  2808. }
  2809. @Override
  2810. public Brand getBrand(Long id) throws IOException {
  2811. return DocumentToObjectUtils.toBrand(
  2812. SearchUtils.getDocumentById(SearchConstants.BRAND_TABLE_NAME, SearchConstants.BRAND_ID_FIELD, id));
  2813. }
  2814. @Override
  2815. public Component getComponent(Long id) throws IOException {
  2816. return DocumentToObjectUtils.toComponent(SearchUtils.getDocumentById(SearchConstants.COMPONENT_TABLE_NAME,
  2817. SearchConstants.COMPONENT_ID_FIELD, id));
  2818. }
  2819. @Override
  2820. public Goods getGoods(String id) throws IOException {
  2821. return DocumentToObjectUtils.toGoods(
  2822. SearchUtils.getDocumentById(SearchConstants.GOODS_TABLE_NAME, SearchConstants.GOODS_GO_ID_FIELD, id));
  2823. }
  2824. @Override
  2825. public PCBGoods getPCBGoods(String id) throws IOException {
  2826. return DocumentToObjectUtils.toPCBGoods(
  2827. SearchUtils.getDocumentById(SearchConstants.PCB_GOODS_TABLE_NAME, SearchConstants.PCB_GOODS_GO_ID_FIELD, id));
  2828. }
  2829. @Override
  2830. public SPage<Object> getObjects(String tableName, String keyword, String field, Boolean tokenized, @NotEmpty("page") Integer page, @NotEmpty("size") Integer size) throws IOException {
  2831. if (keyword == null) {
  2832. keyword = "";
  2833. }
  2834. if (field == null) {
  2835. field = SearchUtils.getIdField(tableName);
  2836. }
  2837. if (tokenized == null) {
  2838. tokenized = false;
  2839. }
  2840. IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(tableName);
  2841. SPage<Object> sPage = new SPage<>();
  2842. try {
  2843. Query query;
  2844. if (tokenized) {
  2845. query = SearchUtils.getBooleanQuery(field, keyword);
  2846. } else {
  2847. query = new TermQuery(new Term(field, keyword));
  2848. }
  2849. // 分页信息
  2850. if (page > 0) {
  2851. sPage.setPage(page);
  2852. } else {
  2853. sPage.setPage(1);
  2854. sPage.setFirst(true);
  2855. }
  2856. if (size > 0) {
  2857. sPage.setSize(size);
  2858. } else {
  2859. sPage.setSize(20);
  2860. }
  2861. TopDocs topDocs;
  2862. // 如果页码不为1
  2863. if (sPage.getPage() > 1) {
  2864. TopDocs previousTopDocs = indexSearcher.search(query, (sPage.getPage() - 1) * sPage.getSize());
  2865. int totalHits = previousTopDocs.totalHits;
  2866. ScoreDoc[] previousScoreDocs = previousTopDocs.scoreDocs;
  2867. if ((sPage.getPage() - 1) * sPage.getSize() >= totalHits) {
  2868. throw new IllegalArgumentException("页码过大:元素总数量为" + totalHits);
  2869. }
  2870. topDocs = indexSearcher.searchAfter(previousScoreDocs[previousScoreDocs.length - 1], query,
  2871. sPage.getSize());
  2872. } else {
  2873. sPage.setFirst(true);
  2874. topDocs = indexSearcher.search(query, sPage.getSize());
  2875. }
  2876. int totalHits = topDocs.totalHits;
  2877. // 设置总元素个数、页数等信息
  2878. sPage.setTotalElement(totalHits);
  2879. int totalPage = (int) Math.ceil(totalHits / (1.0 * sPage.getSize()));
  2880. sPage.setTotalPage(totalPage);
  2881. if (totalPage == sPage.getPage()) {
  2882. sPage.setLast(true);
  2883. }
  2884. List<Object> content = new ArrayList<>();
  2885. for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
  2886. Document document = indexSearcher.doc(scoreDoc.doc);
  2887. content.add(DocumentToObjectUtils.toObject(document, tableName));
  2888. }
  2889. sPage.setContent(content);
  2890. } finally {
  2891. releaseIndexSearcher(indexSearcher);
  2892. }
  2893. return sPage;
  2894. }
  2895. }