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 グループ

各条件はフィールド、演算子、1 つまたは 2 つの値を指定し、各グループは子要素を AND または OR で結合します。グループは最大 MaxDepth 階層までネストでき、ツリーが保持できるルールは最大 MaxRules 個です。「条件を追加」と「グループを追加」は、有効な先頭のフィールドから始まります。

データ型ごとの演算子

フィールドの DataType によって、ユーザーが選べる演算子が決まります。等しい、等しくない、含む、で始まる、で終わる、より大きい、より小さい、範囲、次のいずれか、空である、空ではない、です。値の閉じたリストである Options を持つフィールドでは、より短いリストになります。

自然言語で質問する

AIAssistant を設定して AIPrompt を True にすると、質問ボックスが質問を、有効なフィールドに対する AND ルールに変換します。ルールはビルダーに入り、適用する前にユーザーが変更でき、回答の説明はボックスの下に表示されます。

API キーなしで動作

プロバイダーが設定されていないアシスタントは、ルールベースのパーサーで回答するため、質問ボックスはオフラインでも動作します。プロバイダーの呼び出しが失敗した場合は、UseRuleBasedFallback (既定は True) により同じパーサーが回答します。

Grid との連携

TsgcHTMLComponent_Grid をリンクすると、Fields が空のとき、ビルダーはグリッドの列からフィールドを取得します。ApplyToGrid はグリッドの RowFilter を通して行をフィルターし、LoadFromGridOps はグリッドの操作セットのフィルターごとに 1 つのルールをツリーに読み込みます。

通常のフォームでも使用可能

すべてのコントロールには <ElementName>.<path>.<key> という名前が付きます。path はノードのインデックスパス、key は結合子、フィールド、演算子、値のいずれかを表します。LoadFromRequest はこれらの名前を読み戻すため、htmx を使わずに送信する通常のフォームの中でもビルダーは動作します。

主なプロパティ、メソッド、イベント

最もよく使うメンバーです。

Fields

Fields は、ルールが指定できるフィールドを保持します。各フィールドには Caption、DataType、SQLName、省略可能な Options (1 行につき 1 つの値、または 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 のペアで与えられた 1 行にツリーを評価します。すべてのヘルパーは省略可能なツリーを受け取り、nil は Rules を意味するため、OnApply ハンドラーは共有コンポーネントに触れずにリクエストのツリーを扱えます。

複数のユーザー

サーバーイベントはリクエストのツリーに対して動作するため、1 つのコンポーネントがすべてのセッションに対応します。複数のユーザーが共有するページでは、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 の注文ページで、グリッドの上にビルダーがあり、同じルールがデータベースで実行するパラメーター化された WHERE 句を表示する SQL カードがあります。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 UI の構築を始めましょう。