Commit 946c0ffae135b2bfa2743026628de379e9972884

Authored by boyang
1 parent 51e537e0

优化产品搜索逻辑,提升匹配准确性

shop/src/main/java/com/canrd/shop/service/impl/ProductServiceImpl.java
... ... @@ -462,54 +462,105 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, ProductDO> im
462 462 Set<String> productIds = null;
463 463 Map<String,List<TicketTypeDO>> pId2ttDOsMap = null;
464 464 boolean needLikeMatch = false;
465   - if(StringUtils.isNotBlank(productQueryVO.getKeyword())){
466   - List<TicketTypeDO> tickeyTypeDOList = ticketTypeService.lambdaQuery().like(TicketTypeDO::getRank, productQueryVO.getKeyword()).list();
  465 +// if(StringUtils.isNotBlank(productQueryVO.getKeyword())){
  466 +// List<TicketTypeDO> tickeyTypeDOList = ticketTypeService.lambdaQuery().like(TicketTypeDO::getRank, productQueryVO.getKeyword()).list();
  467 +// productIds = tickeyTypeDOList.stream().map(TicketTypeDO::getProductId).collect(Collectors.toSet());
  468 +// needLikeMatch = true;
  469 +// pId2ttDOsMap = new HashMap<>();
  470 +// for (TicketTypeDO ticketTypeDO : tickeyTypeDOList) {
  471 +// pId2ttDOsMap.computeIfAbsent(ticketTypeDO.getProductId(), k -> new ArrayList<>());
  472 +// pId2ttDOsMap.get(ticketTypeDO.getProductId()).add(ticketTypeDO);
  473 +// }
  474 +// Set<String> finalProductIds = productIds;
  475 +// queryWapper.and(subQueryWapper -> {
  476 +// subQueryWapper.like("p.name", productQueryVO.getKeyword()).or().in(CollUtil.isNotEmpty(finalProductIds),"p.id", finalProductIds);
  477 +// });
  478 +// }
  479 + String keyword = productQueryVO.getKeyword().trim();
  480 + if (StringUtils.isNotBlank(productQueryVO.getKeyword())) {
  481 +// String keyword = productQueryVO.getKeyword().trim();
  482 +
  483 + // 先精确匹配完整的关键字
  484 + List<TicketTypeDO> tickeyTypeDOList = ticketTypeService.lambdaQuery()
  485 + .like(TicketTypeDO::getRank, keyword)
  486 + .list();
  487 +
467 488 productIds = tickeyTypeDOList.stream().map(TicketTypeDO::getProductId).collect(Collectors.toSet());
468 489 needLikeMatch = true;
469 490 pId2ttDOsMap = new HashMap<>();
  491 +
470 492 for (TicketTypeDO ticketTypeDO : tickeyTypeDOList) {
471 493 pId2ttDOsMap.computeIfAbsent(ticketTypeDO.getProductId(), k -> new ArrayList<>());
472 494 pId2ttDOsMap.get(ticketTypeDO.getProductId()).add(ticketTypeDO);
473 495 }
474   - Set<String> finalProductIds = productIds;
475   - queryWapper.and(subQueryWapper -> {
476   - subQueryWapper.like("p.name", productQueryVO.getKeyword()).or().in(CollUtil.isNotEmpty(finalProductIds),"p.id", finalProductIds);
477   - });
  496 +
  497 + if (productIds.isEmpty()) {
  498 + // 如果没有匹配到,分词处理
  499 + String[] keywords = keyword.split("\\s+");
  500 +
  501 + queryWapper.and(subQueryWapper -> {
  502 + // 查询包含所有分词的记录
  503 + for (String key : keywords) {
  504 + subQueryWapper.like("p.name", key).or();
  505 + }
  506 + });
  507 + } else {
  508 + Set<String> finalProductIds = productIds;
  509 +
  510 + queryWapper.and(subQueryWapper -> {
  511 + subQueryWapper.like("p.name", keyword).or().in(CollUtil.isNotEmpty(finalProductIds), "p.id", finalProductIds);
  512 + });
  513 + }
478 514 }
479 515 List<ProductDO> productDOS = this.baseMapper.queryList(queryWapper);
  516 + // Split the keyword into individual words
  517 + String[] keywords = keyword.split("\\s+");
  518 + Map<ProductDO, Integer> collect = productDOS.stream().collect(Collectors.toMap(Function.identity(), productDO -> {
  519 + int matchCount = 0;
  520 + for (String key : keywords) {
  521 + if (productDO.getName().toLowerCase().contains(key.toLowerCase())) {
  522 + matchCount++;
  523 + }
  524 + }
  525 + return matchCount;
  526 + }));
  527 + productDOS.sort((product1, product2) -> {
  528 + int matchCount1 = collect.get(product1);
  529 + int matchCount2 = collect.get(product2);
  530 + // Return the comparison result: higher match count comes first
  531 + return Integer.compare(matchCount2, matchCount1);
  532 + });
480 533 List<ProductDO> accurateProducts = Lists.newArrayList();
481 534 Set<String> accurateProductIdSet = Sets.newHashSet();
482 535 //区分大小写
483 536 productDOS.forEach(x -> {
484   - if(x.getName().trim().contains(productQueryVO.getKeyword().trim())){
485   - accurateProducts.add(x);
486   - accurateProductIdSet.add(x.getId());
487   - }
  537 + accurateProducts.add(x);
  538 + accurateProductIdSet.add(x.getId());
488 539 });
489 540 List<ProductDO> similarProductList = Lists.newArrayList();
490   - for(ProductDO product:productDOS){
491   - if(accurateProductIdSet.contains(product.getId())){
  541 + for (ProductDO product : productDOS) {
  542 + if (accurateProductIdSet.contains(product.getId())) {
492 543 continue;
493 544 }
494 545 String name = product.getName();
495   - int similar = LevenshteinDistanceService.LevenshteinDistancePercent(productQueryVO.getKeyword(),name);
496   - if(similar>=0) {//模糊匹配
  546 + int similar = LevenshteinDistanceService.LevenshteinDistancePercent(productQueryVO.getKeyword(), name);
  547 + if (similar >= 0) {//模糊匹配
497 548 similarProductList.add(product);
498 549 product.setSimilar(similar);
499 550 }
500   - if(needLikeMatch&&productIds.contains(product.getId())){
  551 + if (needLikeMatch && productIds.contains(product.getId())) {
501 552 pId2ttDOsMap.get(product.getId()).forEach(x -> {
502   - int ttSimilar = LevenshteinDistanceService.LevenshteinDistancePercent(productQueryVO.getKeyword(),x.getRank());
  553 + int ttSimilar = LevenshteinDistanceService.LevenshteinDistancePercent(productQueryVO.getKeyword(), x.getRank());
503 554 product.setTtsTotalSimilar(product.getTtsTotalSimilar() + ttSimilar);
504 555 });
505 556 }
506 557 }
507 558 Collections.sort(similarProductList, (o1, o2) ->
508 559 {
509   - if (o2.getSimilar()-o1.getSimilar()!=0) {
510   - return o2.getSimilar()-o1.getSimilar();
511   - }else {
512   - return o2.getTtsTotalSimilar()-o1.getTtsTotalSimilar();
  560 + if (o2.getSimilar() - o1.getSimilar() != 0) {
  561 + return o2.getSimilar() - o1.getSimilar();
  562 + } else {
  563 + return o2.getTtsTotalSimilar() - o1.getTtsTotalSimilar();
513 564 }
514 565 }
515 566 );
... ...