diff --git a/internal/distributed/proxy/httpserver/constant.go b/internal/distributed/proxy/httpserver/constant.go index 34895ac293..c6754cd4ca 100644 --- a/internal/distributed/proxy/httpserver/constant.go +++ b/internal/distributed/proxy/httpserver/constant.go @@ -38,6 +38,7 @@ const ( ResourceGroupCategory = "/resource_groups/" SegmentCategory = "/segments/" QuotaCenterCategory = "/quotacenter/" + CommonCategory = "/common/" ListAction = "list" HasAction = "has" @@ -77,6 +78,8 @@ const ( AddPrivilegesToGroupAction = "add_privileges_to_group" RemovePrivilegesFromGroupAction = "remove_privileges_from_group" TransferReplicaAction = "transfer_replica" + + RunAnalyzerAction = "run_analyzer" ) const ( diff --git a/internal/distributed/proxy/httpserver/handler_v2.go b/internal/distributed/proxy/httpserver/handler_v2.go index 60eea4d422..ef0dce6276 100644 --- a/internal/distributed/proxy/httpserver/handler_v2.go +++ b/internal/distributed/proxy/httpserver/handler_v2.go @@ -209,6 +209,9 @@ func (h *HandlersV2) RegisterRoutesToV2(router gin.IRouter) { // segment group router.POST(SegmentCategory+DescribeAction, timeoutMiddleware(wrapperPost(func() any { return &GetSegmentsInfoReq{} }, wrapperTraceLog(h.getSegmentsInfo)))) router.POST(QuotaCenterCategory+DescribeAction, timeoutMiddleware(wrapperPost(func() any { return &GetQuotaMetricsReq{} }, wrapperTraceLog(h.getQuotaMetrics)))) + + // common + router.POST(CommonCategory+RunAnalyzerAction, timeoutMiddleware(wrapperPost(func() any { return &RunAnalyzerReq{} }, wrapperTraceLog(h.runAnalyzer)))) } type ( @@ -2847,3 +2850,64 @@ func (h *HandlersV2) getQuotaMetrics(ctx context.Context, c *gin.Context, anyReq return resp, err } + +func (h *HandlersV2) runAnalyzer(ctx context.Context, c *gin.Context, anyReq any, dbName string) (interface{}, error) { + httpReq := anyReq.(*RunAnalyzerReq) + + // Convert text strings to byte slices + placeholder := make([][]byte, len(httpReq.Text)) + for i, text := range httpReq.Text { + placeholder[i] = []byte(text) + } + + req := &milvuspb.RunAnalyzerRequest{ + DbName: dbName, + AnalyzerParams: httpReq.AnalyzerParams, + Placeholder: placeholder, + WithDetail: httpReq.WithDetail, + WithHash: httpReq.WithHash, + CollectionName: httpReq.CollectionName, + FieldName: httpReq.FieldName, + AnalyzerNames: httpReq.AnalyzerNames, + } + + c.Set(ContextRequest, req) + resp, err := wrapperProxyWithLimit(ctx, c, req, h.checkAuth, false, "/milvus.proto.milvus.MilvusService/RunAnalyzer", true, h.proxy, func(reqCtx context.Context, req any) (interface{}, error) { + return h.proxy.RunAnalyzer(reqCtx, req.(*milvuspb.RunAnalyzerRequest)) + }) + + if err == nil { + analyzerResp := resp.(*milvuspb.RunAnalyzerResponse) + // Convert response to HTTP-friendly format + results := make([]gin.H, len(analyzerResp.Results)) + for i, result := range analyzerResp.Results { + tokens := make([]gin.H, len(result.Tokens)) + for j, token := range result.Tokens { + tokenData := gin.H{ + "token": token.Token, + } + if httpReq.WithDetail { + tokenData["startOffset"] = token.StartOffset + tokenData["endOffset"] = token.EndOffset + tokenData["position"] = token.Position + tokenData["positionLength"] = token.PositionLength + } + if httpReq.WithHash { + tokenData["hash"] = token.Hash + } + tokens[j] = tokenData + } + results[i] = gin.H{ + "tokens": tokens, + } + } + HTTPReturn(c, http.StatusOK, gin.H{ + HTTPReturnCode: merr.Code(nil), + HTTPReturnData: gin.H{ + "results": results, + }, + }) + } + + return resp, err +} diff --git a/internal/distributed/proxy/httpserver/request_v2.go b/internal/distributed/proxy/httpserver/request_v2.go index 2a0337c625..130f5d39e2 100644 --- a/internal/distributed/proxy/httpserver/request_v2.go +++ b/internal/distributed/proxy/httpserver/request_v2.go @@ -766,3 +766,16 @@ func (req *GetSegmentsInfoReq) GetSegmentIDs() []int64 { } type GetQuotaMetricsReq struct{} + +type RunAnalyzerReq struct { + DbName string `json:"dbName"` + AnalyzerParams string `json:"analyzerParams"` + Text []string `json:"text" binding:"required"` + WithDetail bool `json:"withDetail"` + WithHash bool `json:"withHash"` + CollectionName string `json:"collectionName"` + FieldName string `json:"fieldName"` + AnalyzerNames []string `json:"analyzerNames"` +} + +func (req *RunAnalyzerReq) GetDbName() string { return req.DbName }