milvus2.4多向量搜索源码分析

milvus2.4多向量搜索源码分析

api入口

HybridSearch是多向量搜索的API。

func (node *Proxy) HybridSearch(ctx context.Context, request *milvuspb.HybridSearchRequest) (*milvuspb.SearchResults, error) {
	var err error
	rsp := &milvuspb.SearchResults{
		Status: merr.Success(),
	}
	err2 := retry.Handle(ctx, func() (bool, error) {
		rsp, err = node.hybridSearch(ctx, request)
		if errors.Is(merr.Error(rsp.GetStatus()), merr.ErrInconsistentRequery) {
			return true, merr.Error(rsp.GetStatus())
		}
		return false, nil
	})
	if err2 != nil {
		rsp.Status = merr.Status(err2)
	}
	return rsp, err
}

func (node *Proxy) hybridSearch(ctx context.Context, request *milvuspb.HybridSearchRequest) (*milvuspb.SearchResults, error) {
	......
    // 转换为milvuspb.SearchRequest
	newSearchReq := convertHybridSearchToSearch(request)
	qt := &searchTask{
		ctx:       ctx,
		Condition: NewTaskCondition(ctx),
		SearchRequest: &internalpb.SearchRequest{
			Base: commonpbutil.NewMsgBase(
				commonpbutil.WithMsgType(commonpb.MsgType_Search),
				commonpbutil.WithSourceID(paramtable.GetNodeID()),
			),
			ReqID: paramtable.GetNodeID(),
		},
		request:             newSearchReq,
		tr:                  timerecord.NewTimeRecorder(method),
		qc:                  node.queryCoord,
		node:                node,
		lb:                  node.lbPolicy,
		mustUsePartitionKey: Params.ProxyCfg.MustUsePartitionKey.GetAsBool(),
	}

	guaranteeTs := request.GuaranteeTimestamp

	log := log.Ctx(ctx).With(
		zap.String("role", typeutil.ProxyRole),
		zap.String("db", request.DbName),
		zap.String("collection", request.CollectionName),
		zap.Any("partitions", request.PartitionNames),
		zap.Any("OutputFields", request.OutputFields),
		zap.Uint64("guarantee_timestamp", guaranteeTs),
	)

	defer func() {
		span := tr.ElapseSpan()
		if span >= paramtable.Get().ProxyCfg.SlowQuerySpanInSeconds.GetAsDuration(time.Second) {
			log.Info(rpcSlow(method), zap.Duration("duration", span))
			metrics.ProxySlowQueryCount.WithLabelValues(
				strconv.FormatInt(paramtable.GetNodeID(), 10),
				metrics.HybridSearchLabel,
			).Inc()
		}
	}()

	log.Debug(rpcReceived(method))

	if err := node.sched.dqQueue.Enqueue(qt); err != nil {
		......
	}
	......
}

从代码中可以看出HybridSearch最终调用的和Search() API是同一个task。

milvuspb.HybridSearchRequest有一个[]*SearchRequest变量,这个存储了多个查询结构体,如果是普通的Search(),传参直接就是SearchRequest结构体,如果是HybridSearch(),就是多个查询结构体,下一步做转换。

convertHybridSearchToSearch

进入convertHybridSearchToSearch()看看是如何转换的。

func convertHybridSearchToSearch(req *milvuspb.HybridSearchRequest) *milvuspb.SearchRequest {
	ret := &milvuspb.SearchRequest{
		Base:                  req.GetBase(),
		DbName:                req.GetDbName(),
		CollectionName:        req.GetCollectionName(),
		PartitionNames:        req.GetPartitionNames(),
		OutputFields:          req.GetOutputFields(),
		SearchParams:          req.GetRankParams(),
		TravelTimestamp:       req.GetTravelTimestamp(),
		GuaranteeTimestamp:    req.GetGuaranteeTimestamp(),
		Nq:                    0,
		NotReturnAllMeta:      req.GetNotReturnAllMeta(),
		ConsistencyLevel:      req.GetConsistencyLevel(),
		UseDefaultConsistency: req.GetUseDefaultConsistency(),
		SearchByPrimaryKeys:   false,
		SubReqs:               nil,
	}

	for _, sub := range req.GetRequests() {
		subReq := &milvuspb.SubSearchRequest{
			Dsl:              sub.GetDsl(),
			PlaceholderGroup: sub.GetPlaceholderGroup(),
			DslType:          sub.GetDslType(),
			SearchParams:     sub.GetSearchParams(),
			Nq:               sub.GetNq(),
		}
		ret.SubReqs = append(ret.SubReqs, subReq)
	}
	return ret
}

milvuspb.SearchRequest结构体增加了一个SubReqs变量,类型是[]*SubSearchRequest。

type SubSearchRequest struct {
    Dsl                  string
    PlaceholderGroup     []byte
    DslType              commonpb.DslType
    SearchParams         []*commonpb.KeyValuePair
    Nq                   int64
    XXX_NoUnkeyedLiteral struct{}
    XXX_unrecognized     []byte
    XXX_sizecache        int32
}

searchTask

PreExecute()

t.SearchRequest.IsAdvanced = len(t.request.GetSubReqs()) > 0

Search()和HybridSearch()最终都是走的searchTask,如果是HybridSearch,IsAdvanced会置为true,如果是Search,IsAdvanced会置为false。

func (t *searchTask) PreExecute(ctx context.Context) error {
	......
	t.SearchRequest.IsAdvanced = len(t.request.GetSubReqs()) > 0
	......

	if t.SearchRequest.GetIsAdvanced() {
		if len(t.request.GetSubReqs()) > defaultMaxSearchRequest {
			return errors.New(fmt.Sprintf("maximum of ann search requests is %d", defaultMaxSearchRequest))
		}
	}
	if t.SearchRequest.GetIsAdvanced() {
		t.rankParams, err = parseRankParams(t.request.GetSearchParams())
		if err != nil {
			return err
		}
	}
	......

	if t.SearchRequest.GetIsAdvanced() {
		t.requery = len(t.request.OutputFields) > 0
		err = t.initAdvancedSearchRequest(ctx)
	} else {
		t.requery = len(vectorOutputFields) > 0
		err = t.initSearchRequest(ctx)
	}
	......
}

在initAdvancedSearchRequest填充SubReqs

Search

最终会转换为多次search。

// Search preforms search operation on shard.
func (sd *shardDelegator) Search(ctx context.Context, req *querypb.SearchRequest) ([]*internalpb.SearchResults, error) {
	......
	if req.GetReq().GetIsAdvanced() {
		futures := make([]*conc.Future[*internalpb.SearchResults], len(req.GetReq().GetSubReqs()))
        // 多次调用search
		for index, subReq := range req.GetReq().GetSubReqs() {
			newRequest := &internalpb.SearchRequest{
				Base:               req.GetReq().GetBase(),
				ReqID:              req.GetReq().GetReqID(),
				DbID:               req.GetReq().GetDbID(),
				CollectionID:       req.GetReq().GetCollectionID(),
				PartitionIDs:       subReq.GetPartitionIDs(),
				Dsl:                subReq.GetDsl(),
				PlaceholderGroup:   subReq.GetPlaceholderGroup(),
				DslType:            subReq.GetDslType(),
				SerializedExprPlan: subReq.GetSerializedExprPlan(),
				OutputFieldsId:     req.GetReq().GetOutputFieldsId(),
				MvccTimestamp:      req.GetReq().GetMvccTimestamp(),
				GuaranteeTimestamp: req.GetReq().GetGuaranteeTimestamp(),
				TimeoutTimestamp:   req.GetReq().GetTimeoutTimestamp(),
				Nq:                 subReq.GetNq(),
				Topk:               subReq.GetTopk(),
				MetricType:         subReq.GetMetricType(),
				IgnoreGrowing:      req.GetReq().GetIgnoreGrowing(),
				Username:           req.GetReq().GetUsername(),
				IsAdvanced:         false,
			}
			future := conc.Go(func() (*internalpb.SearchResults, error) {
				searchReq := &querypb.SearchRequest{
					Req:             newRequest,
					DmlChannels:     req.GetDmlChannels(),
					TotalChannelNum: req.GetTotalChannelNum(),
				}
				searchReq.Req.GuaranteeTimestamp = req.GetReq().GetGuaranteeTimestamp()
				searchReq.Req.TimeoutTimestamp = req.GetReq().GetTimeoutTimestamp()
				if searchReq.GetReq().GetMvccTimestamp() == 0 {
					searchReq.GetReq().MvccTimestamp = tSafe
				}
                // 执行搜索
				results, err := sd.search(ctx, searchReq, sealed, growing)
				if err != nil {
					return nil, err
				}

				return segments.ReduceSearchResults(ctx,
					results,
					searchReq.Req.GetNq(),
					searchReq.Req.GetTopk(),
					searchReq.Req.GetMetricType())
			})
			futures[index] = future
		}
        // 等待所有任务执行完成
		err = conc.AwaitAll(futures...)
		if err != nil {
			return nil, err
		}
		results := make([]*internalpb.SearchResults, len(futures))
		for i, future := range futures {
			result := future.Value()
			if result.GetStatus().GetErrorCode() != commonpb.ErrorCode_Success {
				log.Debug("delegator hybrid search failed",
					zap.String("reason", result.GetStatus().GetReason()))
				return nil, merr.Error(result.GetStatus())
			}
			results[i] = result
		}
		var ret *internalpb.SearchResults
		ret, err = segments.MergeToAdvancedResults(ctx, results)
		if err != nil {
			return nil, err
		}
        // 走这里
		return []*internalpb.SearchResults{ret}, nil
	}
	return sd.search(ctx, req, sealed, growing)
}

总结

HybridSearch会转换为多个Search搜索。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/604650.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

企业怎样进行IT外包以及IT外包服务内容

在数字化时代的浪潮中,企业逐渐认识到信息技术的关键作用,特别是制造业基地对于IT外包和运维服务的需求持续增长。然而,在诸多可供选择的IT外包和运维方案中,企业如何推动与IT外包公司的合作?本文将深入介绍IT外包方案…

Python解释器3.8.2版本安装详细教程

Python解释器提取链接链接: https://pan.baidu.com/s/1eDvwYmUJ4l7kIBXewtN4EA?pwd1111 提取码:1111 演示版本为3.6.8,链接安装包为3.8.2版,包中附加pytharm安装包。 1.双击提取好的python-exe安装文件,会…

泛型编程四:容器

文章目录 前言一、序列容器verctor 总结 前言 STL有六大部件,容器、算法、仿函数、迭代器、适配器和分配器。除了算法是函数模板,其他都是类模板。容器可以分为序列容器和关联容器。常见的序列容器有vector、array、deque、list、forward-list&#xff…

Ansible——playbook编写

一、简介 1.什么是playbook Ansible Playbook 是设定自动化任务的一种蓝图,可在无需人工干预或有限干预的前提下执行复杂的 IT 操作。Ansible Playbook 对一组或一类共同构成 Ansible 清单的主机执行。 Ansible Playbook 本质上是一些框架,是一些预先编…

C/C++ 入门(10)list类(STL)

个人主页:仍有未知等待探索-CSDN博客 专题分栏:C 欢迎来指教! 一、标准库中的list 1、了解 list:是一个双向带头循环链表,不支持随机访问(即下标访问),任意位置的插入删除效率高。 …

1.使用uniapp搭建微信小程序项目并引入前端组件资源

文章目录 1. 项目配置1.1. 新建vue3项目1.2. 关联云空间1.3. 运行到微信开发者工具 2. 前端组件2.1. uniCloud的内置组件和扩展组件2.2. uView3.02.3. 在uniapp项目引入uview3 1. 项目配置 1.1. 新建vue3项目 由于我们要使用vue3而不是vue2,所以要选好版本&#x…

1688数据分析实操技巧||1688商品数据采集接口 数据分析

今天,聊一聊B2B平台的数据分析,以1688国内站为例。 1688平台数据接口 1688也属于阿里巴巴的体系,跟淘宝天猫运营很像,因此很多淘宝天猫的玩法调整后也适用于1688。数据分析也是如此。 在1688搞数据分析,搞数据化运营可…

路由策略与路由控制

1.路由控制工具 匹配工具1:访问控制列表 (1)通配符 当进行IP地址匹配的时候,后面会跟着32位掩码位,这32位称为通配符。 通配符,也是点分十进制格式,换算成二进制后,“0”表示“匹配…

(二刷)代码随想录第1天|704. 二分查找 27. 移除元素

704. 二分查找 704. 二分查找 - 力扣(LeetCode) 代码随想录 (programmercarl.com) 手把手带你撕出正确的二分法 | 二分查找法 | 二分搜索法 | LeetCode:704. 二分查找_哔哩哔哩_bilibili 给定一个 n 个元素有序的(升序&#xff09…

(六)JSP教程——out对象

out对象是在JSP中经常使用到的对象,它本质上是一个输出流,前面已经多次使用,我们经常使用它的print()和println()方法,这些方法主要用于实现客户端数据的输出。通过out对象也可以直接向客户端发送一个由程序动态生成的HTML文件。 …

时序图详解

1.这是iic总线在回应时候的时序图,data in代表eeprom收到数据,回stm32的ack,数据回应,data out代表stm32收到eeprom的消息,数据输出ack回应 2.交叉线 代表在这一次输出高电平,或者在这一次也可能输出低电…

JAVA队列相关习题4

1. 用队列实现栈。 225. 用队列实现栈 - 力扣(LeetCode) 一个队列无法实现栈 尝试使用两个队列 1)push元素的时候应当放在那里?哪个队列不为空就放在哪里 2)出栈的时候,出不为空的队列size-1元素,剩余元…

学QT的第三天~

ikun登录界面完善 #include "mywidget.h" void MyWidget::bth1() { if(edit3 ->text()"520cxk"&&edit4 ->text()"1314520") { //1.实例化一个QmessageBox类的对象 QMessageBox box(QMessageBox::Information, //图标 "恭喜…

文字转语音软件下载教程

文字转语音软件下载教程 一,Whisper下载二,ggml-medium语言模型下载三,导入模型下载四,使用方法 一,Whisper下载 网址:https://bittly.cc/uL9xs 下拉选择: 进入下载页面,下载Whis…

分享一些跟客户降价时候可以用的方法

这几天碰到一个沙特的客户,一开始发了一个别人的设计图来问价格。因为产品都是根据图纸定制的,我就问他是否确定了场地,有没有详细的场地尺寸,客户说有,稍后就给。 过了一天,他就给了一个超大的尺寸&#x…

图像处理之SVD检测显示屏缺陷(C++)

图像处理之SVD检测显示屏缺陷(C) 文章目录 图像处理之SVD检测显示屏缺陷(C)前言一、SVD算法简介二、代码实现总结 前言 显示屏缺陷检测是机器视觉领域的一处较广泛的应用场景,显示屏主要有LCD和OLED,缺陷类…

爬虫:爬取豆瓣电影

文章目录 前言一、pandas是什么?二、使用步骤 1.引入库2.读入数据总结 前言 上篇我们将到如何利用xpath的规则,那么这一次,我们将通过案例来告诉读者如何使用Xpath来定位到我们需要的数据,就算你不懂H5代码是怎么个嵌套或者十分复…

cesium 雷达遮罩(电弧球效果)

cesium 雷达遮罩(电弧球效果) 以下为源码直接复制可用 1、实现思路 通过修改“material”材质来实现轨迹球效果 2、代码示例 2.1 index.html <!DOCTYPE html> <html lang="en"><head><!

Python | Leetcode Python题解之第70题爬楼梯

题目&#xff1a; 题解&#xff1a; class Solution:def climbStairs(self, n: int) -> int:a, b 1, 1for _ in range(n - 1):a, b b, a breturn b

C#winfrom三层架构实现简单课程管理系统管理系统,三层架构实现增删改查

1. 项目展示 1.1登录展示 1.2添加课程信息展示 1.3课程信息管理-查询-修改-删除 1.4修改登录密码 2.项目功能介绍&#xff08;图&#xff09; 3.数据库设计 3.1 教师表设计 3.2 课程分类表 3.3 课程信息表 4. 创建样式界面 winfrom 超详细UI创建过程 实现双色球选号器UI界面…
最新文章