go库riot详解

引擎原理

引擎中处理用户请求,分词,索引和排序分别由不同的协程完成

1.主协程,用于收发用户请求

2.分词器协程,负责分词

3.索引器协程,负责建立和查找索引表

4.排序器协程,负责对文档评分排序

索引流程

当一个将文档加入索引的请求进来以后,主协程会通过一个信道(channel)将要分词的文本发送给某个分词协程,该协程将文本分词后通过另一个信道发送给一个索引器协程。索引器协程建立从搜索键到文档的反向索引,反向索引保存在内存中

搜索流程

主协程接到用户请求,将请求短语在主协程内分词,然后通过信道发送给索引器,索引器查找每个搜索键对应的文档然后进行逻辑操作(归并求交集)得到一个精简的文档列表,此列表通过信道传递给排序器,排序器对文档进行评分(scoring)、筛选和排序,然后将排好序的文档通过指定的信道发送给主协程,主协程将结果返回给用户。

分词、索引和排序都有多个协程完成,中间结果保存在信道缓冲队列以避免阻塞。为了提高搜索的并发度降低延迟,riot 引擎将文档做了裂分(裂分数目可以由用户指定),索引和排序请求会发送到所有裂分上并行处理,结果在主协程进行二次归并排序

索引

需要用到两个包

import (

"github.com/go-ego/riot"

"github.com/go-ego/riot/types" '

)

第一个包定义了引擎的功能,第二个包定义了常用的结构体,在使用引擎前需要初始化

var searcher riot.Engine
searcher.Init(types.EngineOpts{
    GseDict: "../../data/dict/dictionary.txt", //
    StopTokenFile:         "../../data/dict/stop_tokens.txt", //停用词词典,
    IndexerOpts: &types.IndexerOpts{
        IndexType: types.LocsIndex,
    },
})

GseDict:分词词典

作用:

字典对于提高搜索的准确性和效率至关重要,因为它帮助搜索引擎理解文档内容并快速定位到相关的词汇

StopTokenFile:停用词词典

作用:

停用词是指在搜索和索引过程中通常被忽略的词汇,因为它们对于搜索结果的相关性贡献不大。常见的停用词包括“的”、“和”、“是”等在很多语言中频繁出现但信息量较低的词汇。通过排除这些词汇,可以减少索引的大小,提高搜索速度,并且使得搜索结果更加精准。

IndexerOpts定义了索引器初始化选项

// IndexerOpts 初始化索引器选项]
type IndexerOpts struct {
    // 索引表的类型,见上面的常数    
    IndexType int    
    // 待插入索引表文档 CACHE SIZE    
    DocCacheSize int    
    // BM25 参数    
    BM25Parameters *BM25Parameters
    }

其中IndexType需要特别注意,IndexType有三种类型

  1. DocIdsIndex,提供了最基本的索引,仅仅记录搜索键出现的文档 docid。
  2. FrequenciesIndex,除了记录 docid 外,还保存了搜索键在每个文档中出现的频率,如果你需要BM25那么 FrequenciesIndex 是你需要的。
  3. LocsIndex,这个不仅包括上两种索引的内容,还额外存储了关键词在文档中的具体位置,这用来计算紧邻距离
// DocIdsIndex 仅存储文档的

docIdDocIdsIndex = 0

// FrequenciesIndex 存储关键词的词频,用于计算

BM25FrequenciesIndex = 1

// LocsIndex 存储关键词在文档中出现的具体字节位置(可能有多个)

// 如果你希望得到关键词紧邻度数据,必须使用 LocsIndex 类型的索引

LocsIndex = 2

三种索引由上到下提供了更多的计算能力的同时也消耗了更多的内存,特别是LocsIndex,当文档很长时会占用大量内存

初始化索引完成将文章加入索引

调用searcher.Index方法将文章加入到索引中

searcher.Index(docId, types.DocData{
    Content: weibo.Text, // Weibo结构体见上文的定义。必须是UTF-8格式。
    Fields: WeiboScoringFields{
        Timestamp:    weibo.Timestamp,
        RepostsCount: weibo.RepostsCount,
    },
})

文档的 docId 必须大于 0 且唯一,对微博来说可以直接用微博的 ID。riot 引擎允许你加入三种索引数据:

  1. 文档的正文(content),会被分词为关键词(tokens)加入索引。
  2. 文档的关键词(tokens)。当正文为空的时候,允许用户绕过 riot 内置的分词器直接输入文档关键词,这使得在引擎外部进行文档分词成为可能。
  3. 文档的属性标签(labels),比如微博的作者,类别等。标签并不出现在正文中。
  4. 自定义评分字段(scoring fields),这允许你给文档添加 任意类型 、 任意结构 的数据用于排序。“搜索”一节会进一步介绍自定义评分字段的用法。
type DocData struct {
    // 文档全文(必须是 UTF-8 格式),用于生成待索引的关键词    
    Content string    
    // new 类别    
    // Class string    
    // new 属性    
    Attri interface{}

    // 文档的关键词    
    // 当 Content 不为空的时候,优先从 Content 中分词得到关键词    
    // 并叠加 
    Tokens。    
    // Tokens 存在的意义在于可以绕过 riot 内置的分词器,在引擎外部    
    // 进行分词和预处理。    
    // Tokens []*TokenData    
    Tokens []TokenData    
    // 文档标签(必须是 UTF-8 格式),比如文档的类别属性等,    
    // 这些标签并不出现在文档文本中    
    Labels []string    
    // 文档的评分字段,可以接纳任何类型的结构体    
    Fields interface{}
}

引擎采用了非同步的索引方式,也就是说当Index返回时索引可能还没有加入索引表中,这方便你循环并发地加入索引。如果你需要等待索引添加完毕再进行后续操作使用 searcher.Flush()

搜索

搜索的过程分为两步:

第一步是在索引表中查找包含搜索键的文档

实现代码:

使用搜索引擎中的SearchDoc函数,传入types.SearchReq结构体,该结构体中的RankOpts需要使用types.RankOpts结构体

output := searcher.SearchDoc(types.SearchReq{
    Text: query,    
    RankOpts: &types.RankOpts{
       ScoringCriteria: &WeiboScoringCriteria{},       
       OutputOffset:    0,       
       MaxOutputs:      2,    
       },
  })

types.SearchReq结构体详细

type SearchReq struct {
    // 搜索的短语(必须是 UTF-8 格式),会被分词
    // 当值为空字符串时关键词会从下面的 Tokens 读入
    Text string

    // 关键词(必须是 UTF-8 格式),当 Text 不为空时优先使用 Text
    // 通常你不需要自己指定关键词,除非你运行自己的分词程序
    Tokens []string

    // 文档标签(必须是 UTF-8 格式),标签不存在文档文本中,
    // 但也属于搜索键的一种
    Labels []string

    // 类别
    // Class string

    // Logic 逻辑检索表达式
    Logic Logic

    // 当不为 nil 时,仅从这些 DocIds 包含的键中搜索(忽略值)
    DocIds map[string]bool

    // 排序选项
    RankOpts *RankOpts

    // 超时,单位毫秒(千分之一秒)。此值小于等于零时不设超时。
    // 搜索超时的情况下仍有可能返回部分排序结果。
    Timeout int

    // 设为 true 时仅统计搜索到的文档个数,不返回具体的文档
    CountDocsOnly bool

    // 不排序,对于可在引擎外部(比如客户端)排序情况适用
    // 对返回文档很多的情况打开此选项可以有效节省时间
    Orderless bool
}

types.RankOpts结构体详细

type RankOpts struct {
    // 文档的评分规则,值为 nil 时使用 Engine 初始化时设定的规则
    ScoringCriteria ScoringCriteria

    // 默认情况下(ReverseOrder = false)按照分数从大到小排序,否则从小到大排序
    ReverseOrder bool

    // 从第几条结果开始输出
    OutputOffset int

    // 最大输出的搜索结果数,为 0 时无限制
    MaxOutputs int
}

第二步是对所有索引到的文档进行排序

对所有搜索到的文档,需要进行排序, 按照每个文档的评分来进行排序

riot支持自定义评分规则

微博搜索的例子(评分规则)

  1. 首先按照关键词紧邻距离排序,比如搜索“自行车运动”,这个短语会被切分成两个关键词,“自行车”和“运动”,出现两个关键词紧邻的文章应该排在两个关键词分开的文章前面。
  2. 然后按照微博的发布时间大致排序,每三天为一个梯队,较晚梯队的文章排在前面。
  3. 最后给微博打分为 BM25*(1+转发数/10000)

实现举例

// WeiboScoringFields  weibo scoring fields
type WeiboScoringFields struct {
    Timestamp    uint64
    RepostsCount uint64
}

// WeiboScoringCriteria custom weibo scoring criteria
type WeiboScoringCriteria struct {
}

// Score score and sort
func (criteria WeiboScoringCriteria) Score(
    doc types.IndexedDoc, fields interface{}) []float32 {
    if reflect.TypeOf(fields) != reflect.TypeOf(WeiboScoringFields{}) {
        return []float32{}
    }
    wsf := fields.(WeiboScoringFields)
    output := make([]float32, 3)
    if doc.TokenProximity > MaxTokenProximity {
        output[0] = 1.0 / float32(doc.TokenProximity)
    } else {
        output[0] = 1.0
    }
    output[1] = float32(wsf.Timestamp / (SecondsInADay * 3))
    output[2] = float32(doc.BM25 * (1 + float32(wsf.RepostsCount)/10000))
    return output
}

WeiboScoringCriteria 实际上继承了 types.ScoringCriteria 接口,这个接口实现了 Score 函数。这个函数带有两个参数:

  1. types.IndexedDoc 参数传递了从索引器中得到的数据,比如词频,词的具体位置,BM25值,紧邻度等信息,具体见types/index.go
  2. 第二个参数是 interface{} 类型的,你可以把这个类型理解成 C 语言中的 void 指针,它可以指向任何数据类型。在我们的例子中指向的是 WeiboScoringFields 结构体,并通过反射机制检查是否是正确的类型。

需要实现该接口的Score方法

package types

// ScoringCriteria 评分规则通用接口
type ScoringCriteria interface {
    // 给一个文档评分,文档排序时先用第一个分值比较,如果
    // 分值相同则转移到第二个分值,以此类推。
    // 返回空切片表明该文档应该从最终排序结果中剔除。
    Score(doc IndexedDoc, fields interface{}) []float32
}

// RankByBM25 一个简单的评分规则,文档分数为BM25
type RankByBM25 struct {
}

// Score score
func (rule RankByBM25) Score(doc IndexedDoc, fields interface{}) []float32 {
    return []float32{doc.BM25}
}

有了自定义评分数据和自定义评分规则,就可以进行检索,

将自定义评分规则加入到,riot引擎中的Search方法中的types.SearchReq结构体参数中的RankOpts中

response := searcher.Search(types.SearchReq{
    Text: "自行车运动",
    RankOpts: &types.RankOpts{
        ScoringCriteria: &WeiboScoringCriteria{},
        OutputOffset: 0,
        MaxOutputs:   100,
    },
})

其中,Text是输入的搜索短语(必须是UTF-8格式),会被分词为关键词。和索引时相同,riot 引擎允许绕过内置的分词器直接输入关键词和文档标签,见 types.SearchReq 结构体的注释。RankOpts 定义了排序选项。WeiboScoringCriteria 就是我们在上面定义的评分规则。另外你也可以通过 OutputOffset 和 MaxOutputs 参数控制分页输出。搜索结果保存在 response 变量中

  • 14
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智慧校园整体解决方案是响应国家教育信息化政策,结合教育改革和技术创新的产物。该方案以物联网、大数据、人工智能和移动互联技术为基础,旨在打造一个安全、高效、互动且环保的教育环境。方案强调从数字化校园向智慧校园的转变,通过自动数据采集、智能分析和按需服务,实现校园业务的智能化管理。 方案的总体设计原则包括应用至上、分层设计和互联互通,确保系统能够满足不同用户角色的需求,并实现数据和资源的整合与共享。框架设计涵盖了校园安全、管理、教学、环境等多个方面,构建了一个全面的校园应用生态系统。这包括智慧安全系统、校园身份识别、智能排课及选课系统、智慧学习系统、精品录播教室方案等,以支持个性化学习和教学评估。 建设内容突出了智慧安全和智慧管理的重要性。智慧安全管理通过分布式录播系统和紧急预案一键启动功能,增强校园安全预警和事件响应能力。智慧管理系统则利用物联网技术,实现人员和设备的智能管理,提高校园运营效率。 智慧教学部分,方案提供了智慧学习系统和精品录播教室方案,支持专业级学习硬件和智能化网络管理,促进个性化学习和教学资源的高效利用。同时,教学质量评估中心和资源应用平台的建设,旨在提升教学评估的科学性和教育资源的共享性。 智慧环境建设则侧重于基于物联网的设备管理,通过智慧教室管理系统实现教室环境的智能控制和能效管理,打造绿色、节能的校园环境。电子班牌和校园信息发布系统的建设,将作为智慧校园的核心和入口,提供教务、一卡通、图书馆等系统的集成信息。 总体而言,智慧校园整体解决方案通过集成先进技术,不仅提升了校园的信息化水平,而且优化了教学和管理流程,为学生、教师和家长提供了更加便捷、个性化的教育体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值