diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index 805fce3b16..d424762293 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -1839,6 +1839,79 @@ func excludeTags(ctx context.Context, tags []model.TagFilters) []model.TagFilter return newTags } +func (r *ClickHouseReader) GetTagValues(ctx context.Context, queryParams *model.TagFilterParams) (*[]model.TagValues, *model.ApiError) { + + excludeMap := make(map[string]struct{}) + for _, e := range queryParams.Exclude { + if e == constants.OperationRequest { + excludeMap[constants.OperationDB] = struct{}{} + continue + } + excludeMap[e] = struct{}{} + } + + var query string + args := []interface{}{queryParams.TagKey, strconv.FormatInt(queryParams.Start.UnixNano(), 10), strconv.FormatInt(queryParams.End.UnixNano(), 10)} + if len(queryParams.ServiceName) > 0 { + args = buildFilterArrayQuery(ctx, excludeMap, queryParams.ServiceName, constants.ServiceName, &query, args) + } + if len(queryParams.HttpRoute) > 0 { + args = buildFilterArrayQuery(ctx, excludeMap, queryParams.HttpRoute, constants.HttpRoute, &query, args) + } + if len(queryParams.HttpCode) > 0 { + args = buildFilterArrayQuery(ctx, excludeMap, queryParams.HttpCode, constants.HttpCode, &query, args) + } + if len(queryParams.HttpHost) > 0 { + args = buildFilterArrayQuery(ctx, excludeMap, queryParams.HttpHost, constants.HttpHost, &query, args) + } + if len(queryParams.HttpMethod) > 0 { + args = buildFilterArrayQuery(ctx, excludeMap, queryParams.HttpMethod, constants.HttpMethod, &query, args) + } + if len(queryParams.HttpUrl) > 0 { + args = buildFilterArrayQuery(ctx, excludeMap, queryParams.HttpUrl, constants.HttpUrl, &query, args) + } + if len(queryParams.Component) > 0 { + args = buildFilterArrayQuery(ctx, excludeMap, queryParams.Component, constants.Component, &query, args) + } + if len(queryParams.Operation) > 0 { + args = buildFilterArrayQuery(ctx, excludeMap, queryParams.Operation, constants.OperationDB, &query, args) + } + if len(queryParams.MinDuration) != 0 { + query = query + " AND durationNano >= ?" + args = append(args, queryParams.MinDuration) + } + if len(queryParams.MaxDuration) != 0 { + query = query + " AND durationNano <= ?" + args = append(args, queryParams.MaxDuration) + } + + query = getStatusFilters(query, queryParams.Status, excludeMap) + + tagValues := []model.TagValues{} + + finalQuery := fmt.Sprintf(`SELECT tagMap[?] as tagValues FROM %s WHERE timestamp >= ? AND timestamp <= ?`, r.indexTable) + finalQuery += query + fmt.Println(finalQuery) + finalQuery += "GROUP BY tagMap[?]" + args = append(args, queryParams.TagKey) + err := r.db.Select(&tagValues, finalQuery, args...) + + zap.S().Info(query) + + if err != nil { + zap.S().Debug("Error in processing sql query: ", err) + return nil, &model.ApiError{model.ErrorExec, fmt.Errorf("Error in processing sql query")} + } + + cleanedTagValues := []model.TagValues{} + for _, e := range tagValues { + if e.TagValues != "" { + cleanedTagValues = append(cleanedTagValues, e) + } + } + return &cleanedTagValues, nil +} + func (r *ClickHouseReader) GetServiceDBOverview(ctx context.Context, queryParams *model.GetServiceOverviewParams) (*[]model.ServiceDBOverviewItem, error) { var serviceDBOverviewItems []model.ServiceDBOverviewItem diff --git a/pkg/query-service/app/druidReader/reader.go b/pkg/query-service/app/druidReader/reader.go index bd44ad2670..de390d98e2 100644 --- a/pkg/query-service/app/druidReader/reader.go +++ b/pkg/query-service/app/druidReader/reader.go @@ -173,6 +173,10 @@ func (druid *DruidReader) GetTagFilters(_ context.Context, _ *model.TagFilterPar return nil, &model.ApiError{model.ErrorNotImplemented, fmt.Errorf("druid does not support getting tagFilters")} } +func (druid *DruidReader) GetTagValues(_ context.Context, _ *model.TagFilterParams) (*[]model.TagValues, *model.ApiError) { + return nil, &model.ApiError{model.ErrorNotImplemented, fmt.Errorf("druid does not support getting tagValues")} +} + func (druid *DruidReader) GetFilteredSpans(_ context.Context, _ *model.GetFilteredSpansParams) (*model.GetFilterSpansResponse, *model.ApiError) { return nil, &model.ApiError{model.ErrorNotImplemented, fmt.Errorf("druid does not support getting FilteredSpans")} } diff --git a/pkg/query-service/app/http_handler.go b/pkg/query-service/app/http_handler.go index d76e8c6ca1..54e740577f 100644 --- a/pkg/query-service/app/http_handler.go +++ b/pkg/query-service/app/http_handler.go @@ -214,6 +214,7 @@ func (aH *APIHandler) RegisterRoutes(router *mux.Router) { router.HandleFunc("/api/v1/getFilteredSpans", aH.getFilteredSpans).Methods(http.MethodPost) router.HandleFunc("/api/v1/getFilteredSpans/aggregates", aH.getFilteredSpanAggregates).Methods(http.MethodPost) + router.HandleFunc("/api/v1/getTagValues", aH.getTagValues).Methods(http.MethodPost) router.HandleFunc("/api/v1/errors", aH.getErrors).Methods(http.MethodGet) router.HandleFunc("/api/v1/errorWithId", aH.getErrorForId).Methods(http.MethodGet) router.HandleFunc("/api/v1/errorWithType", aH.getErrorForType).Methods(http.MethodGet) @@ -1040,6 +1041,22 @@ func (aH *APIHandler) getTagFilters(w http.ResponseWriter, r *http.Request) { aH.writeJSON(w, r, result) } +func (aH *APIHandler) getTagValues(w http.ResponseWriter, r *http.Request) { + + query, err := parseTagValueRequest(r) + if aH.handleError(w, err, http.StatusBadRequest) { + return + } + + result, apiErr := (*aH.reader).GetTagValues(context.Background(), query) + + if apiErr != nil && aH.handleError(w, apiErr.Err, http.StatusInternalServerError) { + return + } + + aH.writeJSON(w, r, result) +} + func (aH *APIHandler) setTTL(w http.ResponseWriter, r *http.Request) { ttlParams, err := parseDuration(r) if aH.handleError(w, err, http.StatusBadRequest) { diff --git a/pkg/query-service/app/interface.go b/pkg/query-service/app/interface.go index de5bdec35b..158a900215 100644 --- a/pkg/query-service/app/interface.go +++ b/pkg/query-service/app/interface.go @@ -39,6 +39,7 @@ type Reader interface { GetTTL(ctx context.Context, ttlParams *model.GetTTLParams) (*model.GetTTLResponseItem, *model.ApiError) GetSpanFilters(ctx context.Context, query *model.SpanFilterParams) (*model.SpanFiltersResponse, *model.ApiError) GetTagFilters(ctx context.Context, query *model.TagFilterParams) (*[]model.TagFilters, *model.ApiError) + GetTagValues(ctx context.Context, query *model.TagFilterParams) (*[]model.TagValues, *model.ApiError) GetFilteredSpans(ctx context.Context, query *model.GetFilteredSpansParams) (*model.GetFilterSpansResponse, *model.ApiError) GetFilteredSpansAggregates(ctx context.Context, query *model.GetFilteredSpanAggregatesParams) (*model.GetFilteredSpansAggregatesResponse, *model.ApiError) diff --git a/pkg/query-service/app/parser.go b/pkg/query-service/app/parser.go index 89d1a6c07f..b329c85a34 100644 --- a/pkg/query-service/app/parser.go +++ b/pkg/query-service/app/parser.go @@ -650,6 +650,31 @@ func parseTagFilterRequest(r *http.Request) (*model.TagFilterParams, error) { return postData, nil } + +func parseTagValueRequest(r *http.Request) (*model.TagFilterParams, error) { + var postData *model.TagFilterParams + err := json.NewDecoder(r.Body).Decode(&postData) + + if err != nil { + return nil, err + } + if postData.TagKey == "" { + return nil, fmt.Errorf("%s param missing in query", postData.TagKey) + } + + postData.Start, err = parseTimeStr(postData.StartStr, "start") + if err != nil { + return nil, err + } + postData.End, err = parseTimeMinusBufferStr(postData.EndStr, "end") + if err != nil { + return nil, err + } + + return postData, nil + +} + func parseErrorsRequest(r *http.Request) (*model.GetErrorsParams, error) { startTime, err := parseTime("start", r) diff --git a/pkg/query-service/model/queryParams.go b/pkg/query-service/model/queryParams.go index 8ceaddd9a6..d5ba693855 100644 --- a/pkg/query-service/model/queryParams.go +++ b/pkg/query-service/model/queryParams.go @@ -201,6 +201,7 @@ type TagFilterParams struct { MaxDuration string `json:"maxDuration"` StartStr string `json:"start"` EndStr string `json:"end"` + TagKey string `json:"tagKey"` Start *time.Time End *time.Time } diff --git a/pkg/query-service/model/response.go b/pkg/query-service/model/response.go index eeeabeaa9c..fb5c460747 100644 --- a/pkg/query-service/model/response.go +++ b/pkg/query-service/model/response.go @@ -268,6 +268,10 @@ type TagItem struct { type TagFilters struct { TagKeys string `json:"tagKeys" db:"tagKeys"` } + +type TagValues struct { + TagValues string `json:"tagValues" db:"tagValues"` +} type ServiceMapDependencyResponseItem struct { Parent string `json:"parent,omitempty" db:"parent,omitempty"` Child string `json:"child,omitempty" db:"child,omitempty"`