Commit 946c0ffae135b2bfa2743026628de379e9972884
1 parent
51e537e0
优化产品搜索逻辑,提升匹配准确性
Showing
1 changed file
with
71 additions
and
20 deletions
shop/src/main/java/com/canrd/shop/service/impl/ProductServiceImpl.java
@@ -462,54 +462,105 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, ProductDO> im | @@ -462,54 +462,105 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, ProductDO> im | ||
462 | Set<String> productIds = null; | 462 | Set<String> productIds = null; |
463 | Map<String,List<TicketTypeDO>> pId2ttDOsMap = null; | 463 | Map<String,List<TicketTypeDO>> pId2ttDOsMap = null; |
464 | boolean needLikeMatch = false; | 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 | productIds = tickeyTypeDOList.stream().map(TicketTypeDO::getProductId).collect(Collectors.toSet()); | 488 | productIds = tickeyTypeDOList.stream().map(TicketTypeDO::getProductId).collect(Collectors.toSet()); |
468 | needLikeMatch = true; | 489 | needLikeMatch = true; |
469 | pId2ttDOsMap = new HashMap<>(); | 490 | pId2ttDOsMap = new HashMap<>(); |
491 | + | ||
470 | for (TicketTypeDO ticketTypeDO : tickeyTypeDOList) { | 492 | for (TicketTypeDO ticketTypeDO : tickeyTypeDOList) { |
471 | pId2ttDOsMap.computeIfAbsent(ticketTypeDO.getProductId(), k -> new ArrayList<>()); | 493 | pId2ttDOsMap.computeIfAbsent(ticketTypeDO.getProductId(), k -> new ArrayList<>()); |
472 | pId2ttDOsMap.get(ticketTypeDO.getProductId()).add(ticketTypeDO); | 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 | List<ProductDO> productDOS = this.baseMapper.queryList(queryWapper); | 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 | List<ProductDO> accurateProducts = Lists.newArrayList(); | 533 | List<ProductDO> accurateProducts = Lists.newArrayList(); |
481 | Set<String> accurateProductIdSet = Sets.newHashSet(); | 534 | Set<String> accurateProductIdSet = Sets.newHashSet(); |
482 | //区分大小写 | 535 | //区分大小写 |
483 | productDOS.forEach(x -> { | 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 | List<ProductDO> similarProductList = Lists.newArrayList(); | 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 | continue; | 543 | continue; |
493 | } | 544 | } |
494 | String name = product.getName(); | 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 | similarProductList.add(product); | 548 | similarProductList.add(product); |
498 | product.setSimilar(similar); | 549 | product.setSimilar(similar); |
499 | } | 550 | } |
500 | - if(needLikeMatch&&productIds.contains(product.getId())){ | 551 | + if (needLikeMatch && productIds.contains(product.getId())) { |
501 | pId2ttDOsMap.get(product.getId()).forEach(x -> { | 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 | product.setTtsTotalSimilar(product.getTtsTotalSimilar() + ttSimilar); | 554 | product.setTtsTotalSimilar(product.getTtsTotalSimilar() + ttSimilar); |
504 | }); | 555 | }); |
505 | } | 556 | } |
506 | } | 557 | } |
507 | Collections.sort(similarProductList, (o1, o2) -> | 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 | ); |