Grafana MongoDB插件变量查询编辑器Query Text字段不显示排查
问题描述
我正在开发一款从MongoDB获取并展示数据的Grafana插件,其他功能均正常,但创建仪表盘变量时遇到异常:Variable Query Editor中的"Query Text"字段无法正常渲染。启用"Is Variable"选项后,相关字段可正常显示,但查询文本输入框渲染异常。怀疑是VariableQueryEditor.tsx前端代码与datasource.go后端的交互存在问题,需确认前端实现是否有误,以及如何让后端MetricFindQuery函数适配Grafana变量功能。
现有代码
VariableQueryEditor.tsx
//VariableQueryEditor.tsx import React, { ChangeEvent } from 'react'; import { VerticalGroup, InlineField, InlineFieldRow, Input, Checkbox, TextArea, Button } from '@grafana/ui'; import { QueryEditorProps } from '@grafana/data'; import { DataSource } from '../datasource'; import { MyDataSourceOptions, MyQuery } from '../types'; type Props = QueryEditorProps<DataSource, MyQuery, MyDataSourceOptions>; export function VariableQueryEditor({ query, onChange, onRunQuery }: Props) { const onVariableFieldChange = (event: ChangeEvent<HTMLInputElement>) => { onChange({ ...query, variableField: event.target.value }); }; const onQueryTextChange = (event: ChangeEvent<HTMLTextAreaElement>) => { onChange({ ...query, queryText: event.target.value }); }; const onIsVariableChange = (event: ChangeEvent<HTMLInputElement>) => { onChange({ ...query, isVariable: event.target.checked }); }; const { variableField, queryText, isVariable } = query; return ( <div className="gf-form"> <VerticalGroup spacing="sm"> <InlineFieldRow> <InlineField label="Is Variable"> <Checkbox onChange={onIsVariableChange} value={isVariable || false} /> </InlineField> </InlineFieldRow> {isVariable && ( <InlineFieldRow> <InlineField label="Variable Field"> <Input onChange={onVariableFieldChange} value={variableField || ''} placeholder="Field name for variable options" autoComplete="off" /> </InlineField> </InlineFieldRow> )} <InlineFieldRow style={{ width: '100%' }}> <InlineField grow style={{ width: '100%' }}> <TextArea onChange={onQueryTextChange} value={queryText || ''} placeholder="Enter a query…" rows={8} /> </InlineField> </InlineFieldRow> <InlineFieldRow> <Button variant="secondary" onClick={onRunQuery}> Run Query </Button> </InlineFieldRow> </VerticalGroup> </div> ); }
module.ts
//module.ts import { DataSourcePlugin } from '@grafana/data'; import { DataSource } from './datasource'; import { ConfigEditor } from './components/ConfigEditor'; import { QueryEditor } from './components/QueryEditor'; import { VariableQueryEditor } from './components/VariableQueryEditor'; import { MyQuery, MyDataSourceOptions } from './types'; export const plugin = new DataSourcePlugin<DataSource, MyQuery, MyDataSourceOptions>(DataSource) .setConfigEditor(ConfigEditor) .setQueryEditor(QueryEditor) .setVariableQueryEditor(VariableQueryEditor as any);
datasource.go
//datasource.go func (d *Datasource) MetricFindQuery(ctx context.Context, query backend.DataQuery) backend.DataResponse { var queryData queryModel if err := json.Unmarshal(query.JSON, &queryData); err != nil { return newErrorResponse(backend.StatusBadRequest, fmt.Errorf("invalid query JSON: %v", err)) } collection := d.getCollection(queryData.Database, queryData.Collection) if collection == nil { return newErrorResponse(backend.StatusBadRequest, fmt.Errorf("invalid collection")) } pipeline := mongo.Pipeline{ bson.D{ {Key: "$group", Value: bson.D{ {Key: "_id", Value: "$" + queryData.VariableField}, }}, }, } cursor, err := collection.Aggregate(ctx, pipeline) if err != nil { log.DefaultLogger.Error("Error executing aggregation:", err) return newErrorResponse(backend.StatusBadRequest, err) } defer cursor.Close(ctx) var results []string for cursor.Next(ctx) { var result struct { ID string `bson:"_id"` } if err := cursor.Decode(&result); err != nil { log.DefaultLogger.Error("Decode error:", err) continue } results = append(results, result.ID) } return createVariableResponse(results) }
问题修复与适配方案
前端VariableQueryEditor.tsx修复
- Checkbox组件属性修正:Checkbox需用
checked属性控制选中状态,而非value,否则会导致"Is Variable"状态切换异常,影响关联字段渲染:
<Checkbox onChange={onIsVariableChange} checked={isVariable || false} />
- Props类型修正:变量查询编辑器应使用
VariableQueryEditorProps而非普通的QueryEditorProps,同时移除module.ts中的类型断言,避免隐藏类型错误:
// VariableQueryEditor.tsx中替换导入和类型定义 import { VariableQueryEditorProps } from '@grafana/data'; type Props = VariableQueryEditorProps<DataSource, MyQuery, MyDataSourceOptions>; // module.ts中移除类型断言 .setVariableQueryEditor(VariableQueryEditor);
- 类型定义补全:确保
types.ts中的MyQuery接口包含所有必要字段的可选标记,避免初始值未定义导致的渲染问题:
export interface MyQuery { // 其他原有字段 isVariable?: boolean; variableField?: string; queryText?: string; }
后端MetricFindQuery函数适配
- 支持自定义查询文本:当前后端硬编码了聚合管道,未使用前端传入的
queryText,需修改逻辑支持自定义查询:
func (d *Datasource) MetricFindQuery(ctx context.Context, query backend.DataQuery) backend.DataResponse { var queryData queryModel if err := json.Unmarshal(query.JSON, &queryData); err != nil { return newErrorResponse(backend.StatusBadRequest, fmt.Errorf("invalid query JSON: %v", err)) } collection := d.getCollection(queryData.Database, queryData.Collection) if collection == nil { return newErrorResponse(backend.StatusBadRequest, fmt.Errorf("invalid collection")) } var pipeline mongo.Pipeline // 优先使用自定义查询文本 if queryData.QueryText != "" { if err := bson.UnmarshalExtJSON([]byte(queryData.QueryText), true, &pipeline); err != nil { return newErrorResponse(backend.StatusBadRequest, fmt.Errorf("invalid query text: %v", err)) } } else if queryData.VariableField != "" { // 回退到原有分组逻辑 pipeline = mongo.Pipeline{ bson.D{ {Key: "$group", Value: bson.D{ {Key: "_id", Value: "$" + queryData.VariableField}, }}, }, } } else { return newErrorResponse(backend.StatusBadRequest, fmt.Errorf("either query text or variable field must be provided")) } cursor, err := collection.Aggregate(ctx, pipeline) if err != nil { log.DefaultLogger.Error("Error executing aggregation:", err) return newErrorResponse(backend.StatusBadRequest, err) } defer cursor.Close(ctx) // 返回Grafana标准的VariableOption格式 var results []backend.VariableOption for cursor.Next(ctx) { var result bson.M if err := cursor.Decode(&result); err != nil { log.DefaultLogger.Error("Decode error:", err) continue } var value string if id, ok := result["_id"]; ok { value = fmt.Sprintf("%v", id) } else if len(result) > 0 { for _, v := range result { value = fmt.Sprintf("%v", v) break } } if value != "" { results = append(results, backend.VariableOption{Text: value, Value: value}) } } return backend.DataResponse{ Status: backend.StatusOK, JSON: results, } }
- queryModel字段匹配:在
queryModel结构体中新增QueryText字段,与前端queryText对应:
type queryModel struct { Database string `json:"database"` Collection string `json:"collection"` VariableField string `json:"variableField"` QueryText string `json:"queryText"` }
- 返回标准格式:Grafana变量期望返回
backend.VariableOption数组(包含Text和Value字段),而非单纯字符串数组,确保变量选项正确显示。
内容的提问来源于stack exchange,提问作者Asghar Shakourinia
相关产品推荐
相关产品推荐

