搜索推荐引擎 - 让用户秒找心仪商品的智能大脑

# 搜索推荐引擎 - 让用户秒找心仪商品的智能大脑

# 📖 故事继续

小明在商品目录页面看到了琳琅满目的手表,但面对数百款选择,他有些眼花缭乱。于是他在搜索框输入了"瑞士 自动机械 防水",瞬间页面就筛选出了20多款符合条件的手表。更神奇的是,系统还在页面右侧推荐了"您可能还喜欢"的商品,包括手表配件、同品牌的其他产品,甚至还有根据他浏览历史推荐的相似风格商品。

小红是小明的女朋友,她经常在这个平台购买化妆品。当她登录平台时,首页就展示了最新的口红新品、她关注品牌的促销信息,以及基于她购买历史的个性化推荐。这一切看似魔法般的体验,背后是一套复杂而精密的搜索推荐引擎在默默工作。

今天,我们就来揭秘这个让用户"秒找心仪商品"的智能大脑是如何工作的。

# 🎯 系统概述

搜索推荐引擎是跨境电商平台的核心竞争力之一,它需要在毫秒级时间内从海量商品中找到用户最想要的结果。系统不仅要支持传统的关键词搜索,还要具备智能纠错、同义词识别、多语言搜索、个性化推荐等高级功能。

# 核心功能

  • 全文搜索与筛选
  • 智能查询理解
  • 个性化推荐
  • 实时热搜统计
  • A/B测试支持
  • 搜索结果排序优化

# 🏗️ 系统架构设计

# 整体架构图

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   用户界面      │    │   搜索网关      │    │   查询理解服务  │
│                 │────│                 │────│                 │
│ - 搜索框        │    │ - 查询预处理    │    │ - 意图识别      │
│ - 筛选器        │    │ - 限流控制      │    │ - 查询扩展      │
│ - 结果展示      │    │ - 缓存管理      │    │ - 同义词处理    │
└─────────────────┘    └─────────────────┘    └─────────────────┘
                                                        │
                       ┌─────────────────┐    ┌─────────────────┐
                       │   推荐引擎      │    │   搜索引擎      │
                       │                 │    │                 │
                       │ - 协同过滤      │    │ - Elasticsearch │
                       │ - 内容推荐      │    │ - 倒排索引      │
                       │ - 深度学习      │    │ - 相关性评分    │
                       └─────────────────┘    └─────────────────┘
                                                        │
                                               ┌─────────────────┐
                                               │   数据存储层    │
                                               │                 │
                                               │ - 商品索引      │
                                               │ - 用户行为数据  │
                                               │ - 推荐模型      │
                                               │ - 搜索日志      │
                                               └─────────────────┘

# 💻 核心代码实现

# 1. 搜索服务核心实现

/**
 * 搜索服务核心实现类
 * 职责:处理用户搜索请求,整合搜索和推荐结果
 */
@Service
public class SearchService {
    
    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;
    
    @Autowired
    private QueryUnderstandingService queryUnderstandingService;
    
    @Autowired
    private RecommendationService recommendationService;
    
    @Autowired
    private SearchMetricsService metricsService;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    /**
     * 执行搜索请求
     * 
     * @param request 搜索请求参数
     * @return 搜索结果
     */
    public SearchResponse search(SearchRequest request) {
        
        // 1. 记录搜索开始时间(用于性能监控)
        long startTime = System.currentTimeMillis();
        
        try {
            // 2. 查询理解和预处理
            ProcessedQuery processedQuery = queryUnderstandingService.processQuery(request);
            
            // 3. 构建搜索响应
            SearchResponse response = new SearchResponse();
            
            // 4. 执行主搜索
            SearchResult mainSearchResult = executeMainSearch(processedQuery);
            response.setMainResult(mainSearchResult);
            
            // 5. 获取推荐结果(异步执行)
            CompletableFuture<RecommendationResult> recommendationFuture = 
                CompletableFuture.supplyAsync(() -> 
                    recommendationService.getRecommendations(request.getUserId(), processedQuery));
            
            // 6. 获取相关搜索建议
            List<String> suggestions = getSuggestions(processedQuery.getOriginalQuery());
            response.setSuggestions(suggestions);
            
            // 7. 等待推荐结果并设置
            try {
                RecommendationResult recommendations = recommendationFuture.get(100, TimeUnit.MILLISECONDS);
                response.setRecommendations(recommendations);
            } catch (TimeoutException e) {
                log.warn("推荐服务超时,使用默认推荐");
                response.setRecommendations(getDefaultRecommendations());
            }
            
            // 8. 记录搜索指标
            long duration = System.currentTimeMillis() - startTime;
            metricsService.recordSearch(request, response, duration);
            
            return response;
            
        } catch (Exception e) {
            log.error("搜索执行失败", e);
            metricsService.recordSearchError(request, e);
            throw new SearchException("搜索服务异常: " + e.getMessage());
        }
    }
    
    /**
     * 执行主搜索逻辑
     * 核心算法:多字段搜索 + 权重计算 + 结果排序
     */
    private SearchResult executeMainSearch(ProcessedQuery processedQuery) {
        
        // 1. 构建Elasticsearch查询
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        
        // 2. 主查询:多字段匹配
        if (StringUtils.hasText(processedQuery.getProcessedQuery())) {
            
            // 2.1 构建多字段查询
            MultiMatchQueryBuilder multiMatchQuery = QueryBuilders
                .multiMatchQuery(processedQuery.getProcessedQuery())
                .field("name", 3.0f)          // 商品名称权重最高
                .field("description", 1.0f)   // 描述权重中等
                .field("brand", 2.0f)         // 品牌权重较高
                .field("category", 1.5f)      // 分类权重中等
                .field("tags", 1.2f)          // 标签权重中等
                .type(MultiMatchQueryBuilder.Type.BEST_FIELDS)
                .fuzziness(Fuzziness.AUTO);    // 自动模糊匹配
            
            boolQuery.must(multiMatchQuery);
        }
        
        // 3. 筛选条件
        addFilters(boolQuery, processedQuery.getFilters());
        
        // 4. 构建搜索请求
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
                .query(boolQuery)
                .from(processedQuery.getOffset())
                .size(processedQuery.getSize())
                .timeout(TimeValue.timeValueMillis(500)); // 500ms超时
        
        // 5. 添加排序
        addSorting(searchSourceBuilder, processedQuery.getSortOptions());
        
        // 6. 添加聚合(用于筛选器统计)
        addAggregations(searchSourceBuilder);
        
        // 7. 添加高亮
        addHighlighting(searchSourceBuilder);
        
        // 8. 执行搜索
        SearchRequest searchRequest = new SearchRequest("products")
                .source(searchSourceBuilder);
        
        try {
            SearchResponse elasticResponse = elasticsearchTemplate.search(searchRequest, RequestOptions.DEFAULT);
            return convertToSearchResult(elasticResponse, processedQuery);
            
        } catch (Exception e) {
            log.error("Elasticsearch查询失败", e);
            throw new SearchException("搜索引擎查询失败");
        }
    }
    
    /**
     * 添加筛选条件
     */
    private void addFilters(BoolQueryBuilder boolQuery, Map<String, Object> filters) {
        
        filters.forEach((key, value) -> {
            switch (key) {
                case "category":
                    if (value instanceof List) {
                        boolQuery.filter(QueryBuilders.termsQuery("categoryId", (List<?>) value));
                    } else {
                        boolQuery.filter(QueryBuilders.termQuery("categoryId", value));
                    }
                    break;
                    
                case "brand":
                    if (value instanceof List) {
                        boolQuery.filter(QueryBuilders.termsQuery("brandId", (List<?>) value));
                    } else {
                        boolQuery.filter(QueryBuilders.termQuery("brandId", value));
                    }
                    break;
                    
                case "priceRange":
                    if (value instanceof Map) {
                        Map<String, Object> priceRange = (Map<String, Object>) value;
                        RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("basePrice");
                        
                        if (priceRange.containsKey("min")) {
                            rangeQuery.gte(priceRange.get("min"));
                        }
                        if (priceRange.containsKey("max")) {
                            rangeQuery.lte(priceRange.get("max"));
                        }
                        
                        boolQuery.filter(rangeQuery);
                    }
                    break;
                    
                case "inStock":
                    if (Boolean.TRUE.equals(value)) {
                        boolQuery.filter(QueryBuilders.rangeQuery("stockQuantity").gt(0));
                    }
                    break;
                    
                default:
                    log.warn("未知的筛选条件: {}", key);
            }
        });
        
        // 默认只显示上架商品
        boolQuery.filter(QueryBuilders.termQuery("status", "ACTIVE"));
    }
    
    /**
     * 添加排序逻辑
     */
    private void addSorting(SearchSourceBuilder searchSourceBuilder, List<SortOption> sortOptions) {
        
        if (sortOptions == null || sortOptions.isEmpty()) {
            // 默认排序:相关性 + 销量 + 评分
            searchSourceBuilder
                .sort(SortBuilders.scoreSort().order(SortOrder.DESC))           // 相关性评分
                .sort(SortBuilders.fieldSort("salesCount").order(SortOrder.DESC)) // 销量
                .sort(SortBuilders.fieldSort("rating").order(SortOrder.DESC));    // 评分
            return;
        }
        
        for (SortOption option : sortOptions) {
            switch (option.getField()) {
                case "price":
                    searchSourceBuilder.sort(SortBuilders.fieldSort("basePrice")
                            .order(option.isAscending() ? SortOrder.ASC : SortOrder.DESC));
                    break;
                    
                case "sales":
                    searchSourceBuilder.sort(SortBuilders.fieldSort("salesCount")
                            .order(SortOrder.DESC)); // 销量总是降序
                    break;
                    
                case "rating":
                    searchSourceBuilder.sort(SortBuilders.fieldSort("rating")
                            .order(SortOrder.DESC)); // 评分总是降序
                    break;
                    
                case "newest":
                    searchSourceBuilder.sort(SortBuilders.fieldSort("createdAt")
                            .order(SortOrder.DESC)); // 最新总是降序
                    break;
                    
                default:
                    log.warn("未知的排序字段: {}", option.getField());
            }
        }
    }
    
    /**
     * 添加聚合统计(用于筛选器)
     */
    private void addAggregations(SearchSourceBuilder searchSourceBuilder) {
        
        // 品牌聚合
        TermsAggregationBuilder brandAgg = AggregationBuilders
                .terms("brands")
                .field("brandId")
                .size(50);
        
        // 分类聚合
        TermsAggregationBuilder categoryAgg = AggregationBuilders
                .terms("categories")
                .field("categoryId")
                .size(20);
        
        // 价格区间聚合
        RangeAggregationBuilder priceRangeAgg = AggregationBuilders
                .range("priceRanges")
                .field("basePrice")
                .addRange("0-50", 0, 50)
                .addRange("50-100", 50, 100)
                .addRange("100-200", 100, 200)
                .addRange("200-500", 200, 500)
                .addRange("500+", 500, Double.MAX_VALUE);
        
        searchSourceBuilder
                .aggregation(brandAgg)
                .aggregation(categoryAgg)
                .aggregation(priceRangeAgg);
    }
    
    /**
     * 添加搜索结果高亮
     */
    private void addHighlighting(SearchSourceBuilder searchSourceBuilder) {
        
        HighlightBuilder highlightBuilder = new HighlightBuilder()
                .field("name")
                .field("description")
                .preTags("<em class='highlight'>")
                .postTags("</em>")
                .fragmentSize(100)
                .numOfFragments(3);
        
        searchSourceBuilder.highlighter(highlightBuilder);
    }
}

# 2. 查询理解服务

/**
 * 查询理解服务
 * 职责:对用户输入进行智能处理,提升搜索准确性
 */
@Service
public class QueryUnderstandingService {
    
    @Autowired
    private SynonymService synonymService;
    
    @Autowired
    private SpellCheckService spellCheckService;
    
    @Autowired
    private IntentRecognitionService intentRecognitionService;
    
    /**
     * 处理用户查询
     * 核心流程:拼写纠错 → 同义词扩展 → 意图识别 → 查询重写
     */
    public ProcessedQuery processQuery(SearchRequest request) {
        
        String originalQuery = request.getQuery();
        log.info("开始处理查询: {}", originalQuery);
        
        ProcessedQuery processedQuery = new ProcessedQuery();
        processedQuery.setOriginalQuery(originalQuery);
        processedQuery.setUserId(request.getUserId());
        processedQuery.setLanguage(request.getLanguage());
        processedQuery.setFilters(request.getFilters());
        processedQuery.setOffset(request.getOffset());
        processedQuery.setSize(request.getSize());
        processedQuery.setSortOptions(request.getSortOptions());
        
        // 1. 查询预处理
        String cleanedQuery = preprocessQuery(originalQuery);
        
        // 2. 拼写检查和纠错
        String correctedQuery = spellCheckService.correctSpelling(cleanedQuery, request.getLanguage());
        if (!cleanedQuery.equals(correctedQuery)) {
            processedQuery.setSpellCorrected(true);
            processedQuery.setSuggestedQuery(correctedQuery);
            log.info("拼写纠错: {} → {}", cleanedQuery, correctedQuery);
        }
        
        // 3. 同义词扩展
        String expandedQuery = synonymService.expandQuery(correctedQuery, request.getLanguage());
        
        // 4. 意图识别
        QueryIntent intent = intentRecognitionService.recognizeIntent(expandedQuery, request);
        processedQuery.setIntent(intent);
        
        // 5. 根据意图调整查询
        String finalQuery = adjustQueryByIntent(expandedQuery, intent);
        processedQuery.setProcessedQuery(finalQuery);
        
        // 6. 提取查询特征(用于个性化)
        extractQueryFeatures(processedQuery, originalQuery);
        
        log.info("查询处理完成: {} → {}", originalQuery, finalQuery);
        
        return processedQuery;
    }
    
    /**
     * 查询预处理
     * 清理特殊字符、标准化格式
     */
    private String preprocessQuery(String query) {
        if (StringUtils.isEmpty(query)) {
            return "";
        }
        
        return query
                .trim()                           // 去除首尾空格
                .toLowerCase()                    // 转小写
                .replaceAll("[^\\w\\s\\u4e00-\\u9fa5]", " ") // 保留字母、数字、空格、中文
                .replaceAll("\\s+", " ")            // 多个空格合并为一个
                .trim();
    }
    
    /**
     * 根据意图调整查询
     */
    private String adjustQueryByIntent(String query, QueryIntent intent) {
        
        switch (intent.getType()) {
            case BRAND_SEARCH:
                // 品牌搜索:提升品牌字段权重
                return query + "^brand:2.0";
                
            case CATEGORY_SEARCH:
                // 分类搜索:提升分类字段权重
                return query + "^category:2.0";
                
            case PRICE_SENSITIVE:
                // 价格敏感:添加价格排序提示
                return query + "^sort:price";
                
            case FEATURE_SEARCH:
                // 功能特性搜索:提升描述和特性字段权重
                return query + "^description:1.5^features:1.5";
                
            default:
                return query;
        }
    }
    
    /**
     * 提取查询特征
     */
    private void extractQueryFeatures(ProcessedQuery processedQuery, String originalQuery) {
        
        Map<String, Object> features = new HashMap<>();
        
        // 查询长度特征
        features.put("queryLength", originalQuery.length());
        features.put("wordCount", originalQuery.split("\\s+").length);
        
        // 查询类型特征
        features.put("hasNumbers", originalQuery.matches(".*\\d.*"));
        features.put("hasChinese", originalQuery.matches(".*[\\u4e00-\\u9fa5].*"));
        features.put("hasEnglish", originalQuery.matches(".*[a-zA-Z].*"));
        
        // 商业意图特征
        features.put("hasPriceKeywords", containsPriceKeywords(originalQuery));
        features.put("hasBrandKeywords", containsBrandKeywords(originalQuery));
        
        processedQuery.setFeatures(features);
    }
    
    private boolean containsPriceKeywords(String query) {
        String[] priceKeywords = {"便宜", "贵", "价格", "多少钱", "cheap", "expensive", "price", "cost"};
        return Arrays.stream(priceKeywords)
                .anyMatch(keyword -> query.toLowerCase().contains(keyword));
    }
    
    private boolean containsBrandKeywords(String query) {
        // 这里可以维护一个品牌关键词库
        String[] brandKeywords = {"苹果", "三星", "华为", "apple", "samsung", "huawei", "nike", "adidas"};
        return Arrays.stream(brandKeywords)
                .anyMatch(keyword -> query.toLowerCase().contains(keyword));
    }
}

# 3. 推荐引擎实现

/**
 * 推荐引擎服务
 * 实现多种推荐算法:协同过滤、内容推荐、深度学习推荐
 */
@Service
public class RecommendationService {
    
    @Autowired
    private UserBehaviorService userBehaviorService;
    
    @Autowired
    private ProductSimilarityService productSimilarityService;
    
    @Autowired
    private DeepLearningModelService deepLearningModelService;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    /**
     * 获取推荐结果
     * 策略:多路召回 + 融合排序
     */
    public RecommendationResult getRecommendations(String userId, ProcessedQuery query) {
        
        RecommendationResult result = new RecommendationResult();
        
        try {
            // 1. 多路召回
            List<RecommendationCandidate> candidates = new ArrayList<>();
            
            // 1.1 协同过滤召回
            List<RecommendationCandidate> cfCandidates = 
                getCollaborativeFilteringRecommendations(userId, 50);
            candidates.addAll(cfCandidates);
            
            // 1.2 内容推荐召回
            List<RecommendationCandidate> contentCandidates = 
                getContentBasedRecommendations(userId, query, 50);
            candidates.addAll(contentCandidates);
            
            // 1.3 热门商品召回
            List<RecommendationCandidate> popularCandidates = 
                getPopularRecommendations(query.getLanguage(), 30);
            candidates.addAll(popularCandidates);
            
            // 1.4 深度学习模型召回
            if (StringUtils.hasText(userId)) {
                List<RecommendationCandidate> dlCandidates = 
                    deepLearningModelService.getRecommendations(userId, 40);
                candidates.addAll(dlCandidates);
            }
            
            // 2. 去重和融合排序
            List<RecommendationItem> finalRecommendations = 
                mergeAndRankCandidates(candidates, userId, query);
            
            // 3. 分组返回
            result.setPersonalizedRecommendations(
                finalRecommendations.stream()
                    .filter(item -> "PERSONALIZED".equals(item.getType()))
                    .limit(10)
                    .collect(Collectors.toList())
            );
            
            result.setSimilarProducts(
                finalRecommendations.stream()
                    .filter(item -> "SIMILAR".equals(item.getType()))
                    .limit(8)
                    .collect(Collectors.toList())
            );
            
            result.setTrendingProducts(
                finalRecommendations.stream()
                    .filter(item -> "TRENDING".equals(item.getType()))
                    .limit(6)
                    .collect(Collectors.toList())
            );
            
        } catch (Exception e) {
            log.error("推荐服务异常", e);
            // 降级处理:返回热门商品
            result = getFallbackRecommendations();
        }
        
        return result;
    }
    
    /**
     * 协同过滤推荐
     * 算法:基于用户的协同过滤 + 基于物品的协同过滤
     */
    private List<RecommendationCandidate> getCollaborativeFilteringRecommendations(
            String userId, int limit) {
        
        if (StringUtils.isEmpty(userId)) {
            return Collections.emptyList();
        }
        
        // 1. 获取用户行为历史
        UserBehaviorProfile userProfile = userBehaviorService.getUserProfile(userId);
        
        // 2. 找到相似用户
        List<String> similarUsers = findSimilarUsers(userId, userProfile, 100);
        
        // 3. 获取相似用户喜欢的商品
        Map<String, Double> productScores = new HashMap<>();
        
        for (String similarUserId : similarUsers) {
            UserBehaviorProfile similarProfile = userBehaviorService.getUserProfile(similarUserId);
            double userSimilarity = calculateUserSimilarity(userProfile, similarProfile);
            
            // 计算商品推荐分数
            similarProfile.getLikedProducts().forEach((productId, score) -> {
                if (!userProfile.getLikedProducts().containsKey(productId)) {
                    productScores.merge(productId, score * userSimilarity, Double::sum);
                }
            });
        }
        
        // 4. 排序并返回Top N
        return productScores.entrySet().stream()
                .sorted(Map.Entry.<String, Double>comparingByValue().reversed())
                .limit(limit)
                .map(entry -> new RecommendationCandidate(
                    entry.getKey(), 
                    entry.getValue(), 
                    "COLLABORATIVE_FILTERING",
                    "因为购买了相似商品的用户也喜欢"
                ))
                .collect(Collectors.toList());
    }
    
    /**
     * 内容推荐
     * 基于商品特征相似度推荐
     */
    private List<RecommendationCandidate> getContentBasedRecommendations(
            String userId, ProcessedQuery query, int limit) {
        
        List<RecommendationCandidate> candidates = new ArrayList<>();
        
        // 1. 基于用户历史行为的内容推荐
        if (StringUtils.hasText(userId)) {
            UserBehaviorProfile userProfile = userBehaviorService.getUserProfile(userId);
            
            // 获取用户喜欢的商品特征
            ProductFeatureProfile featureProfile = extractUserFeaturePreference(userProfile);
            
            // 基于特征相似度推荐
            List<String> similarProducts = productSimilarityService
                    .findSimilarProductsByFeatures(featureProfile, limit / 2);
            
            similarProducts.forEach(productId -> {
                double score = calculateContentSimilarityScore(featureProfile, productId);
                candidates.add(new RecommendationCandidate(
                    productId, score, "CONTENT_BASED", "基于您的偏好推荐"
                ));
            });
        }
        
        // 2. 基于当前搜索的内容推荐
        if (StringUtils.hasText(query.getProcessedQuery())) {
            List<String> relatedProducts = productSimilarityService
                    .findProductsByQuery(query.getProcessedQuery(), limit / 2);
            
            relatedProducts.forEach(productId -> {
                double score = calculateQueryRelevanceScore(query, productId);
                candidates.add(new RecommendationCandidate(
                    productId, score, "QUERY_RELATED", "与搜索内容相关"
                ));
            });
        }
        
        return candidates;
    }
    
    /**
     * 融合排序算法
     * 综合考虑多种因素:推荐分数、商品质量、用户偏好、业务策略
     */
    private List<RecommendationItem> mergeAndRankCandidates(
            List<RecommendationCandidate> candidates, String userId, ProcessedQuery query) {
        
        // 1. 去重(同一商品可能来自多个召回源)
        Map<String, RecommendationCandidate> uniqueCandidates = new HashMap<>();
        
        for (RecommendationCandidate candidate : candidates) {
            String productId = candidate.getProductId();
            
            if (uniqueCandidates.containsKey(productId)) {
                // 如果已存在,取最高分数
                RecommendationCandidate existing = uniqueCandidates.get(productId);
                if (candidate.getScore() > existing.getScore()) {
                    uniqueCandidates.put(productId, candidate);
                }
            } else {
                uniqueCandidates.put(productId, candidate);
            }
        }
        
        // 2. 计算最终排序分数
        List<RecommendationItem> rankedItems = uniqueCandidates.values().stream()
                .map(candidate -> {
                    double finalScore = calculateFinalScore(candidate, userId, query);
                    return new RecommendationItem(
                        candidate.getProductId(),
                        finalScore,
                        determineRecommendationType(candidate),
                        candidate.getReason()
                    );
                })
                .sorted(Comparator.comparingDouble(RecommendationItem::getScore).reversed())
                .limit(50) // 最多返回50个推荐
                .collect(Collectors.toList());
        
        return rankedItems;
    }
    
    /**
     * 计算最终推荐分数
     * 综合算法:基础分数 × 商品质量分数 × 个性化分数 × 业务权重
     */
    private double calculateFinalScore(RecommendationCandidate candidate, 
                                     String userId, ProcessedQuery query) {
        
        double baseScore = candidate.getScore();
        
        // 1. 商品质量分数(评分、销量、库存等)
        double qualityScore = getProductQualityScore(candidate.getProductId());
        
        // 2. 个性化分数(用户偏好匹配度)
        double personalizationScore = getPersonalizationScore(candidate.getProductId(), userId);
        
        // 3. 时效性分数(新品、热门等)
        double freshnessScore = getFreshnessScore(candidate.getProductId());
        
        // 4. 业务策略分数(利润率、库存周转等)
        double businessScore = getBusinessScore(candidate.getProductId());
        
        // 5. 多样性惩罚(避免推荐过于相似的商品)
        double diversityPenalty = getDiversityPenalty(candidate.getProductId(), query);
        
        // 综合计算最终分数
        double finalScore = baseScore * 0.4 +           // 基础推荐分数权重40%
                           qualityScore * 0.25 +       // 商品质量权重25%
                           personalizationScore * 0.2 + // 个性化权重20%
                           freshnessScore * 0.1 +       // 时效性权重10%
                           businessScore * 0.05;        // 业务策略权重5%
        
        // 应用多样性惩罚
        finalScore *= (1.0 - diversityPenalty);
        
        return Math.max(0.0, Math.min(1.0, finalScore)); // 确保分数在[0,1]范围内
    }
    
    /**
     * 获取商品质量分数
     */
    private double getProductQualityScore(String productId) {
        // 这里可以从缓存或数据库获取商品质量指标
        // 包括:用户评分、销量、退货率、库存状态等
        
        // 示例实现
        String cacheKey = "product:quality:" + productId;
        Double cachedScore = (Double) redisTemplate.opsForValue().get(cacheKey);
        
        if (cachedScore != null) {
            return cachedScore;
        }
        
        // 计算质量分数的逻辑
        double rating = getProductRating(productId);        // 用户评分 (1-5)
        int salesCount = getProductSalesCount(productId);   // 销量
        double returnRate = getProductReturnRate(productId); // 退货率
        boolean inStock = isProductInStock(productId);       // 库存状态
        
        double qualityScore = (rating / 5.0) * 0.4 +                    // 评分权重40%
                             Math.min(1.0, salesCount / 1000.0) * 0.3 + // 销量权重30%
                             (1.0 - returnRate) * 0.2 +                  // 退货率权重20%
                             (inStock ? 1.0 : 0.5) * 0.1;               // 库存权重10%
        
        // 缓存结果(1小时过期)
        redisTemplate.opsForValue().set(cacheKey, qualityScore, Duration.ofHours(1));
        
        return qualityScore;
    }
    
    // 其他辅助方法的实现...
    private double getProductRating(String productId) { return 4.2; }
    private int getProductSalesCount(String productId) { return 500; }
    private double getProductReturnRate(String productId) { return 0.05; }
    private boolean isProductInStock(String productId) { return true; }
    private double getPersonalizationScore(String productId, String userId) { return 0.8; }
    private double getFreshnessScore(String productId) { return 0.7; }
    private double getBusinessScore(String productId) { return 0.6; }
    private double getDiversityPenalty(String productId, ProcessedQuery query) { return 0.1; }
}

# 🔧 技术要点解析

# 1. Elasticsearch索引设计

{
  "mappings": {
    "properties": {
      "name": {
        "type": "text",
        "analyzer": "ik_max_word",
        "search_analyzer": "ik_smart",
        "fields": {
          "keyword": {
            "type": "keyword"
          }
        }
      },
      "description": {
        "type": "text",
        "analyzer": "ik_max_word"
      },
      "brand": {
        "type": "keyword"
      },
      "category": {
        "type": "keyword"
      },
      "tags": {
        "type": "keyword"
      },
      "basePrice": {
        "type": "double"
      },
      "rating": {
        "type": "double"
      },
      "salesCount": {
        "type": "integer"
      },
      "stockQuantity": {
        "type": "integer"
      },
      "status": {
        "type": "keyword"
      },
      "createdAt": {
        "type": "date"
      },
      "features": {
        "type": "nested",
        "properties": {
          "name": {
            "type": "keyword"
          },
          "value": {
            "type": "text"
          }
        }
      }
    }
  },
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1,
    "analysis": {
      "analyzer": {
        "ik_max_word": {
          "type": "ik_max_word"
        },
        "ik_smart": {
          "type": "ik_smart"
        }
      }
    }
  }
}

# 2. 搜索性能优化

/**
 * 搜索性能优化配置
 */
@Configuration
public class SearchOptimizationConfig {
    
    /**
     * 搜索线程池配置
     */
    @Bean("searchExecutor")
    public ThreadPoolTaskExecutor searchExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(50);
        executor.setQueueCapacity(200);
        executor.setThreadNamePrefix("search-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
    
    /**
     * 搜索缓存配置
     */
    @Bean
    public CacheManager searchCacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        cacheManager.setCaffeine(Caffeine.newBuilder()
                .maximumSize(10000)
                .expireAfterWrite(Duration.ofMinutes(10))
                .recordStats());
        return cacheManager;
    }
}

# 📊 监控与分析

# 1. 搜索指标监控

/**
 * 搜索指标服务
 */
@Service
public class SearchMetricsService {
    
    private final Counter searchCounter;
    private final Timer searchTimer;
    private final Counter clickCounter;
    private final Gauge cacheHitRate;
    
    public SearchMetricsService(MeterRegistry meterRegistry) {
        this.searchCounter = Counter.builder("search.requests.total")
                .description("搜索请求总数")
                .register(meterRegistry);
                
        this.searchTimer = Timer.builder("search.duration")
                .description("搜索耗时")
                .register(meterRegistry);
                
        this.clickCounter = Counter.builder("search.clicks.total")
                .description("搜索结果点击数")
                .register(meterRegistry);
    }
    
    public void recordSearch(SearchRequest request, SearchResponse response, long duration) {
        searchCounter.increment(
            Tags.of(
                "query_type", determineQueryType(request.getQuery()),
                "result_count", String.valueOf(response.getMainResult().getTotal()),
                "has_results", String.valueOf(response.getMainResult().getTotal() > 0)
            )
        );
        
        searchTimer.record(duration, TimeUnit.MILLISECONDS);
    }
    
    public void recordClick(String query, String productId, int position) {
        clickCounter.increment(
            Tags.of(
                "position", String.valueOf(position),
                "page", String.valueOf(position / 20 + 1)
            )
        );
    }
}

# 🎉 总结

通过本文的深入学习,我们全面了解了跨境电商搜索推荐引擎的设计与实现。从小明输入"瑞士手表"到获得精准的搜索结果和个性化推荐,背后是一套复杂而精密的技术体系在支撑。

# 关键收获

  1. 搜索架构:多层次的搜索架构设计,支持高并发和高可用
  2. 查询理解:智能的查询预处理,提升搜索准确性
  3. 推荐算法:多种推荐算法的融合,提供个性化体验
  4. 性能优化:缓存策略、索引优化、异步处理等技术手段
  5. 监控分析:完善的指标监控和数据分析体系

# 最佳实践

  • 采用Elasticsearch构建高性能搜索引擎
  • 实现多路召回和融合排序的推荐策略
  • 使用多级缓存提升系统响应速度
  • 建立完善的A/B测试和效果评估体系
  • 重视用户体验,提供智能的搜索建议和纠错

# 技术演进方向

  • AI增强:集成更先进的NLP和深度学习模型
  • 实时性:构建实时的用户行为分析和推荐系统
  • 个性化:更精细的用户画像和个性化推荐
  • 多模态:支持图像、语音等多模态搜索

下一篇文章,我们将继续探讨跨境电商交易链路的下一个关键环节:购物车系统,看看如何设计一个高性能、高可用的购物车服务。