Query Builder

TsgcHTMLComponent_QueryBuilder:可视化的查询和筛选构建器。用户把条件(字段、运算符、值)组合成 AND 和 OR 分组,可以手动操作,也可以用自然语言提问并由 AI 助手回答,服务器再把结果转换成参数化的 SQL 条件、行匹配或 Grid 上的筛选。适用于 Delphi、C++ Builder 和 .NET。

TsgcHTMLComponent_QueryBuilder

添加规则可以引用的字段,或链接一个 TsgcHTMLComponent_Grid 以使用它的列,然后渲染。每个按钮都会提交一个服务器事件,组件验证该请求的树,OnApply 把树交给您,用来查询您的数据。

组件类

TsgcHTMLComponent_QueryBuilder,条件树使用 TsgcHTMLQueryNode

渲染为

以 Bootstrap 5 标记渲染的条件和分组树,由服务器渲染,无需脚本

语言

Delphi, C++ Builder, .NET

定义字段,在您的数据上运行规则

Fields 列出规则可以引用的内容。OnApply 接收请求中已验证的树,GetSQLWhere 把它写成带命名参数的 SQL 条件,ToJSON 把它保存在用户的会话中。

uses
  sgcHTML_AI, sgcHTML_Component_QueryBuilder;

// 只创建一次:它自行响应服务器事件
oQB := TsgcHTMLComponent_QueryBuilder.Create(Self);
oQB.ElementName := 'orders';
oField := oQB.Fields.Add;
oField.Name := 'customer';
oField.Caption := 'Customer';
oField := oQB.Fields.Add;
oField.Name := 'amount';
oField.Caption := 'Amount';
oField.DataType := aidtInteger;
oField.SQLName := 'o.amount';
oField := oQB.Fields.Add;
oField.Name := 'status';
oField.Caption := 'Status';
oField.Options.Add('open=Open');
oField.Options.Add('paid=Paid');
oQB.HTMXEngine := HTMXEngine1;
oQB.AIAssistant := sgcHTMLAIAssistant1;
oQB.OnApply := QueryBuilderApply;
Result := oQB.HTML;

// aRules 是本次请求的树,不会改动 Rules
procedure TForm1.QueryBuilderApply(Sender: TObject;
  const aRules: TsgcHTMLQueryNode; var aResponse: string);
var
  oQB: TsgcHTMLComponent_QueryBuilder;
  oParams: TStringList;
  vWhere: string;
  i: Integer;
begin
  oQB := TsgcHTMLComponent_QueryBuilder(Sender);
  oParams := TStringList.Create;
  try
    vWhere := oQB.GetSQLWhere(oParams, aRules);
    FDQuery1.Close;
    FDQuery1.SQL.Text := 'select * from orders';
    if vWhere <> '' then
      FDQuery1.SQL.Add('where ' + vWhere);
    for i := 0 to oParams.Count - 1 do
      FDQuery1.Params.ParamByName(oParams.Names[i]).AsString :=
        oParams.ValueFromIndex[i];
    FDQuery1.Open;
    DoSaveFilter(oQB.ToJSON(aRules));      // 您自己的会话存储
    aResponse := DoOrdersHTML(FDQuery1);   // 标有 hx-swap-oob 的行
  finally
    oParams.Free;
  end;
end;
// 包含:sgcHTML_AI.hpp, sgcHTML_Component_QueryBuilder.hpp
oQB = new TsgcHTMLComponent_QueryBuilder(this);
oQB->ElementName = "orders";
TsgcHTMLQueryField *oField = oQB->Fields->Add();
oField->Name = "customer";
oField->Caption = "Customer";
oField = oQB->Fields->Add();
oField->Name = "amount";
oField->Caption = "Amount";
oField->DataType = aidtInteger;
oField->SQLName = "o.amount";
oQB->HTMXEngine = HTMXEngine1;
oQB->OnApply = QueryBuilderApply;
html = oQB->HTML;

void __fastcall TForm1::QueryBuilderApply(TObject *Sender,
  TsgcHTMLQueryNode* const aRules, UnicodeString &aResponse)
{
  TsgcHTMLComponent_QueryBuilder *oQB = (TsgcHTMLComponent_QueryBuilder *)Sender;
  TStringList *oParams = new TStringList();
  try
  {
    UnicodeString vWhere = oQB->GetSQLWhere(oParams, aRules);
    FDQuery1->Close();
    FDQuery1->SQL->Text = "select * from orders";
    if (vWhere != "")
      FDQuery1->SQL->Add("where " + vWhere);
    for (int i = 0; i < oParams->Count; i++)
      FDQuery1->Params->ParamByName(oParams->Names[i])->AsString =
        oParams->ValueFromIndex[i];
    FDQuery1->Open();
    DoSaveFilter(oQB->ToJSON(aRules));
    aResponse = DoOrdersHTML(FDQuery1);
  }
  __finally
  {
    delete oParams;
  }
}
using esegece.sgcWebSockets;

var qb = new TsgcHTMLComponent_QueryBuilder();
qb.ElementName = "orders";
var field = qb.Fields.Add();
field.Name = "customer";
field.Caption = "Customer";
field = qb.Fields.Add();
field.Name = "amount";
field.Caption = "Amount";
field.DataType = TsgcHTMLAIDataType.aidtInteger;
field.SQLName = "o.amount";
qb.HTMXEngine = htmxEngine;
qb.AIAssistant = aiAssistant;

// aRules 是本次请求的树
qb.OnApply += (object sender, TsgcHTMLQueryNode aRules, ref string aResponse) =>
{
    var parameters = new List<string>();
    string where = qb.GetSQLWhere(parameters, aRules);
    using var cmd = connection.CreateCommand();
    cmd.CommandText = "select * from orders" + (where != "" ? " where " + where : "");
    foreach (var p in parameters)
    {
        var kv = p.Split(new[] { '=' }, 2);
        cmd.Parameters.AddWithValue(":" + kv[0], kv[1]);
    }
    DoSaveFilter(qb.ToJSON(aRules));
    aResponse = DoOrdersHTML(cmd.ExecuteReader());
};

string html = qb.HTML;

您的用户能得到什么

为不会写 SQL 的人准备的筛选界面,以及您的代码可以在任何数据库上安全运行的条件。

AND 和 OR 分组

每个条件都指定一个字段、一个运算符以及一个或两个值,每个分组用 AND 或 OR 连接其子项。分组最多嵌套 MaxDepth 层,一棵树最多包含 MaxRules 条规则。“添加条件”和“添加分组”以当前生效的第一个字段开始。

按数据类型提供运算符

字段的 DataType 决定用户可以选择哪些运算符:等于、不等于、包含、开头是、结尾是、大于、小于、介于、是以下任一项、为空和不为空。带有 Options(封闭的值列表)的字段只提供较短的列表。

用自然语言提问

设置 AIAssistant 并把 AIPrompt 设为 True 后,提问框会把问题转换成针对当前生效字段的 AND 规则。规则会进入构建器,用户在应用之前仍可修改,回答的说明显示在提问框下方。

无需 API 密钥即可使用

未配置提供程序的助手会用基于规则的解析器回答,因此提问框可以离线使用。当提供程序调用失败时,UseRuleBasedFallback(默认为 True)会让同一个解析器来回答。

与 Grid 联动

链接一个 TsgcHTMLComponent_Grid 后,当 Fields 为空时,构建器会从网格的列中获取字段。ApplyToGrid 通过网格的 RowFilter 筛选行,LoadFromGridOps 则为网格操作集的每个筛选器在树中填入一条规则。

普通表单也适用

每个控件都命名为 <ElementName>.<path>.<key>,其中 path 是节点的索引路径,key 表示组合符、字段、运算符或值。LoadFromRequest 会读回这些名称,因此构建器也可以在不使用 htmx 提交的普通表单中工作。

关键属性、方法与事件

您最常用到的成员。

Fields

Fields 保存规则可以引用的字段,每个字段都有 Caption、DataType、SQLName 和可选的 Options(每行一个值,或 value=caption)。没有项目时,Grid 的列就是生效的字段。FindField、GetFieldCount 和 IsOperatorAllowed 可用来查询这些字段。

OnApply

OnApply(Sender, aRules, aResponse) 在“应用”“清除”和已回答的提问之后触发,带有请求中已验证的树。您在 aResponse 中返回的内容会追加到响应中,例如标有 hx-swap-oob 的行。没有 OnApply 时,ApplyToGrid 会把树复制到 Rules 并筛选所链接的 Grid。

GetSQLWhere

返回不含 WHERE 关键字的条件,树为空时返回空字符串。值只会写成命名参数 :qb1、:qb2 等,并以 qb1=<value> 的形式添加到 aParams,因此用户文本无法改变语句。列是字段的 SQLName,或其 Name。

经过验证,绝不轻信

所有从外部填充树的内容,无论是 JSON、AI 的回答还是提交的表单,都要经过同样的验证。针对未生效字段的规则、该类型不允许的运算符、超过 MaxRules 的规则以及比 MaxDepth 更深的分组都会被丢弃,值会被截断为 200 个字符,空的子分组会被删除。SQLName 会原样进入 SQL,因此请在代码中设置它,绝不要从请求中获取。

JSON 格式

ToJSON 把分组写成 {"c":"and","r":[...]},把规则写成 {"f":"field","o":"operator","v":"value"},介于运算符还会多一个 "v2"。ParseJSON 和 LoadFromJSON 会读回并验证它。

Matches

Matches 对以 Name=Value 键值对给出的一行数据计算这棵树,适用于内存中的行。每个辅助方法都接受一个可选的树,nil 表示 Rules,因此 OnApply 处理程序可以操作请求中的树,而不触碰共享组件。

多个用户

一个组件为所有会话服务,因为服务器事件作用于请求中的树。在多个用户共享的页面上,请分配 OnApply,把 ToJSON(aRules) 保存在会话中,并用 ParseJSON 和 GetBuilderHTML 渲染每个用户的构建器。

服务器事件

每个按钮,以及字段和运算符选择框在变化时,都会把服务器事件 qb 提交到 HTMX 引擎的组件事件端点,或提交到 ActionURL。响应是标有 hx-swap-oob="true" 的构建器,htmx 按它的 id 替换:QueryBuilderID,或 qb_ 加上 ElementName。

演示

Demos\60.HTML\01.RunTime\25.QueryBuilder 是端口 8127 上的订单页面,构建器位于网格上方,还有一张 SQL 卡片,显示同样的规则在数据库上会运行的参数化 WHERE 子句。它的 AI 助手没有 API 密钥,因此提问框离线回答,每位访问者在会话中保存自己的规则。设计时、Lazarus 和 .NET 版本与之对应。

继续探索

在线试用查看 查询构建器 演示,它在 eSeGeCe 演示服务器上实时运行。
在线帮助此组件的完整 API 参考和使用指南。
Grid 组件构建器所筛选并从中获取列的网格。
AI 助手把自然语言的问题转换成提问框的规则。
所有 sgcHTML 组件浏览 80 多个组件的完整功能矩阵。
下载免费试用版30 天试用版包含 60.HTML 演示项目。
价格Single、Team 和 Site 授权,均含完整源代码。
超值之选:All-AccesseSeGeCe 全部产品,含高级支持,每年 €1,059 起。
查看 All-Access 价格

准备好开始了吗?

下载免费试用版,开始在 Delphi、C++ Builder 和 .NET 中构建 Web 界面。