You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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修复

  1. Checkbox组件属性修正:Checkbox需用checked属性控制选中状态,而非value,否则会导致"Is Variable"状态切换异常,影响关联字段渲染:
<Checkbox onChange={onIsVariableChange} checked={isVariable || false} />
  1. Props类型修正:变量查询编辑器应使用VariableQueryEditorProps而非普通的QueryEditorProps,同时移除module.ts中的类型断言,避免隐藏类型错误:
// VariableQueryEditor.tsx中替换导入和类型定义
import { VariableQueryEditorProps } from '@grafana/data';
type Props = VariableQueryEditorProps<DataSource, MyQuery, MyDataSourceOptions>;

// module.ts中移除类型断言
.setVariableQueryEditor(VariableQueryEditor);
  1. 类型定义补全:确保types.ts中的MyQuery接口包含所有必要字段的可选标记,避免初始值未定义导致的渲染问题:
export interface MyQuery {
  // 其他原有字段
  isVariable?: boolean;
  variableField?: string;
  queryText?: string;
}

后端MetricFindQuery函数适配

  1. 支持自定义查询文本:当前后端硬编码了聚合管道,未使用前端传入的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,
    }
}
  1. queryModel字段匹配:在queryModel结构体中新增QueryText字段,与前端queryText对应:
type queryModel struct {
    Database     string `json:"database"`
    Collection   string `json:"collection"`
    VariableField string `json:"variableField"`
    QueryText    string `json:"queryText"`
}
  1. 返回标准格式:Grafana变量期望返回backend.VariableOption数组(包含Text和Value字段),而非单纯字符串数组,确保变量选项正确显示。

内容的提问来源于stack exchange,提问作者Asghar Shakourinia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 07:58:13