wukong引擎源码分析之索引——part 3 文档评分 无非就是将docid对应的fields信息存储起来,为搜索结果rank评分用

简介:

之前的文章分析过,接受索引请求处理的代码在segmenter_worker.go里:

复制代码
复制代码
func (engine *Engine) segmenterWorker() {
    for {
        request := <-engine.segmenterChannel //关键

        tokensMap := make(map[string][]int)
        numTokens := 0
        if !engine.initOptions.NotUsingSegmenter && request.data.Content != "" {
            // 当文档正文不为空时,优先从内容分词中得到关键词
            segments := engine.segmenter.Segment([]byte(request.data.Content))
            for _, segment := range segments {
                token := segment.Token().Text()
                if !engine.stopTokens.IsStopToken(token) {
                    tokensMap[token] = append(tokensMap[token], segment.Start())
                }
            }
            numTokens = len(segments)
        } else {
            // 否则载入用户输入的关键词
            for _, t := range request.data.Tokens {
                if !engine.stopTokens.IsStopToken(t.Text) {
                    tokensMap[t.Text] = t.Locations
                }
            }
            numTokens = len(request.data.Tokens)
        }

        // 加入非分词的文档标签
        for _, label := range request.data.Labels {
            if !engine.initOptions.NotUsingSegmenter {
                if !engine.stopTokens.IsStopToken(label) {
                    tokensMap[label] = []int{}
                }
            } else {
                tokensMap[label] = []int{}
            }
        }

        indexerRequest := indexerAddDocumentRequest{
            document: &types.DocumentIndex{
                DocId:       request.docId,
                TokenLength: float32(numTokens),
                Keywords:    make([]types.KeywordIndex, len(tokensMap)),
            },
        }
        iTokens := 0
        for k, v := range tokensMap {
            indexerRequest.document.Keywords[iTokens] = types.KeywordIndex{
                Text: k,
                // 非分词标注的词频设置为0,不参与tf-idf计算
                Frequency: float32(len(v)),
                Starts:    v}
            iTokens++
        }

        var dealDocInfoChan = make(chan bool, 1)

        indexerRequest.dealDocInfoChan = dealDocInfoChan
        engine.indexerAddDocumentChannels[request.shard] <- indexerRequest

        rankerRequest := rankerAddDocRequest{
            docId:           request.docId,
            fields:          request.data.Fields,
            dealDocInfoChan: dealDocInfoChan,
        }
        engine.rankerAddDocChannels[request.shard] <- rankerRequest
    }
}
复制代码
复制代码

上面代码的作用就是在统计词频和单词位置(注意:tag也是作为搜索的单词,不过其词频是0,而无法参与tf-idf计算),并封装为indexerRequest,发送给engine.indexerAddDocumentChannels[request.shard]

此外,红色部分代码是在为文档评分做准备,engine/ranker_worker.go:

复制代码
func (engine *Engine) rankerAddDocWorker(shard int) {                                                                                                                                           
    for {
        request := <-engine.rankerAddDocChannels[shard] //关键
        docInfo := engine.rankers[shard].AddDoc(request.docId, request.fields, request.dealDocInfoChan)                                                                                         
        // save
        if engine.initOptions.UsePersistentStorage {
            engine.persistentStorageIndexDocumentChannels[shard] <- persistentStorageIndexDocumentRequest{                                                                                      
                typ:     "info",                
                docId:   request.docId,         
                docInfo: docInfo,           
            }                  
        }
    }
}
复制代码

AddDoc无非就是将docid对应的fields信息存储起来,为搜索结果rank评分用!

复制代码
// 给某个文档添加评分字段
func (ranker *Ranker) AddDoc(docId uint64, fields interface{}, dealDocInfoChan <-chan bool) *types.DocInfo {
    if ranker.initialized == false {
        log.Fatal("排序器尚未初始化")   
    }

    <-dealDocInfoChan // 等待索引器处理完成

    ranker.DocInfosShard.Lock()
    defer ranker.DocInfosShard.Unlock()
    if _, found := ranker.DocInfosShard.DocInfos[docId]; !found {
        ranker.DocInfosShard.DocInfos[docId] = new(types.DocInfo)
        ranker.DocInfosShard.NumDocuments++
    }
    ranker.DocInfosShard.DocInfos[docId].Fields = fields
    return ranker.DocInfosShard.DocInfos[docId]
}
复制代码

 














本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/bonelee/p/6582369.html,如需转载请自行联系原作者


相关文章
|
3月前
|
存储 自然语言处理 搜索推荐
字节面试题:Agent 的记忆系统怎么设计?短期记忆和长期记忆到底有什么区别?
Agent记忆系统是其从“聪明聊天框”升级为可靠助手的核心。短期记忆保障单次会话连贯性(如滚动摘要、结构化任务状态);长期记忆实现跨会话个性化(如用户偏好、项目事实),需分类型存储与精准检索;而记忆治理(准入、更新、清理、权限)决定系统能否长期稳定运行——这才是大厂面试高频考点与落地成败关键。
|
5月前
|
存储 人工智能 安全
2026年各大厂商OpenClaw中文生态分析调研汇报
OpenClaw(原Moltbot)是开源AI助手框架,ClaudeCowork为Anthropic官方企业协作工具;生态涵盖轻量版(Pico/NanoClaw)、高性能版(MaxClaw)、行业定制版(MedClaw、ClawWork等)及社区衍生项目(LobsterAI、RedClaw等),以Obsidian为知识库,OpenFang为交互协议。
|
运维 监控 数据可视化
运维那些事儿(2):做好资产生命周期动态管理,筑牢数据中心全流程运维根基
本期聚焦数据中心资产生命周期管理,详解设备从入库启用、正常运行、维保巡检到报废处置的4大核心阶段。结合自动发现、自定义台账、监控联动与3D可视化,实现维保预警、巡检闭环、数据安全与全程可溯,助力运维从“被动排障”升级为“主动防控”,降本增效。(239字)
|
10月前
|
数据采集 存储 数据可视化
完整工具链:从爬取、解析到可视化12306城市数据的全流程实现
完整工具链:从爬取、解析到可视化12306城市数据的全流程实现
|
12月前
|
存储 监控 安全
RFID车辆无感识别进出管理更高效
RFID技术广泛应用于车辆无感识别进出管理,如停车场、园区、物流等场景。通过自动识别车辆身份,实现无需停车或人工操作的高效通行,提升管理便利性与智能化水平。
|
安全 Java 开发者
|
人工智能 自然语言处理 算法
DeepSeek模型的突破:性能超越R1满血版的关键技术解析
上海AI实验室周伯文团队的最新研究显示,7B版本的DeepSeek模型在性能上超越了R1满血版。该成果强调了计算最优Test-Time Scaling的重要性,并提出了一种创新的“弱到强”优化监督机制的研究思路,区别于传统的“从强到弱”策略。这一方法不仅提升了模型性能,还为未来AI研究提供了新方向。
1845 9
|
算法 知识图谱
极简ECDSA
该文章以极简的方式介绍了ECDSA(椭圆曲线数字签名算法)的基本原理,包括私钥和公钥的生成、签名过程、以及验证签名的方法,旨在帮助读者轻松掌握ECDSA的核心概念。
440 6
极简ECDSA
|
JSON 程序员 C#
使用 C# 比较两个对象是否相等的7个方法总结
比较对象是编程中的一项基本技能,在实际业务中经常碰到,比如在ERP系统中,企业的信息非常重要,每一次更新,都需要比较记录更新前后企业的信息,直接比较通常只能告诉我们它们是否指向同一个内存地址,那我们应该怎么办呢?分享 7 个方法给你!
855 2