Query Builder

TsgcHTMLComponent_QueryBuilder: un constructor visual de consultas y filtros. Los usuarios combinan condiciones (campo, operador, valor) en grupos AND y OR, a mano o a partir de una pregunta en lenguaje natural respondida por el AI Assistant, y el servidor convierte el resultado en una condición SQL parametrizada, una coincidencia de filas o un filtro de un Grid, en Delphi, C++ Builder y .NET.

TsgcHTMLComponent_QueryBuilder

Añade los campos que una regla puede nombrar, o enlaza un TsgcHTMLComponent_Grid para usar sus columnas, y renderízalo. Cada botón envía un evento de servidor, el componente valida el árbol de esa petición y OnApply te entrega el árbol para consultar tus datos.

Clase del componente

TsgcHTMLComponent_QueryBuilder, con TsgcHTMLQueryNode para el árbol de condiciones

Renderiza

Un árbol de condiciones y grupos como marcado Bootstrap 5, renderizado en el servidor y sin script

Lenguajes

Delphi, C++ Builder, .NET

Define los campos y ejecuta las reglas sobre tus datos

Fields lista lo que una regla puede nombrar. OnApply recibe el árbol validado de la petición, GetSQLWhere lo escribe como una condición SQL con parámetros con nombre y ToJSON lo conserva para la sesión del usuario.

uses
  sgcHTML_AI, sgcHTML_Component_QueryBuilder;

// Se crea una sola vez: responde a sus propios eventos de servidor
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 es el árbol de esta petición, Rules no se toca
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));      // tu almacén de sesión
    aResponse := DoOrdersHTML(FDQuery1);   // filas marcadas con hx-swap-oob
  finally
    oParams.Free;
  end;
end;
// includes: 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 es el árbol de esta petición
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;

Lo que obtienen tus usuarios

Una pantalla de filtros para personas que no escriben SQL, y una condición que tu código puede ejecutar con seguridad en cualquier base de datos.

Grupos AND y OR

Cada condición nombra un campo, un operador y uno o dos valores, y cada grupo une a sus hijos con AND u OR. Los grupos se anidan hasta MaxDepth niveles y un árbol contiene como máximo MaxRules reglas. Añadir condición y Añadir grupo empiezan por el primer campo en vigor.

Operadores según el tipo de dato

El DataType de un campo decide qué operadores puede elegir el usuario: igual, distinto, contiene, empieza por, termina en, mayor, menor, entre, en lista, está vacío y no está vacío. Un campo con Options, una lista cerrada de valores, admite una lista más corta.

Pregunta en lenguaje natural

Con un AIAssistant y AIPrompt en True, un cuadro Preguntar convierte una pregunta en reglas AND sobre los campos en vigor. Las reglas llegan al constructor, donde el usuario aún puede cambiarlas antes de aplicar, y la explicación de la respuesta aparece bajo el cuadro.

Funciona sin clave de API

Un asistente sin proveedor configurado responde con su analizador basado en reglas, así que el cuadro Preguntar funciona sin conexión. Cuando falla una llamada al proveedor, UseRuleBasedFallback (True por defecto) deja que responda el mismo analizador.

Enlazado al Grid

Enlaza un TsgcHTMLComponent_Grid y el constructor toma sus campos de las columnas del grid cuando Fields está vacío. ApplyToGrid filtra las filas del grid mediante su RowFilter, y LoadFromGridOps rellena un árbol con una regla por cada filtro de un conjunto de operaciones del grid.

También con formularios simples

Cada control se llama <ElementName>.<path>.<key>, donde path es la ruta de índices del nodo y key nombra el combinador, el campo, el operador o el valor. LoadFromRequest lee esos nombres de vuelta, de modo que el constructor también funciona dentro de un formulario simple enviado sin htmx.

Propiedades, métodos y eventos clave

Los miembros que más vas a usar.

Campos

Fields contiene los campos que una regla puede nombrar, cada uno con su Caption, DataType, SQLName y Options opcional, un valor o value=caption por línea. Sin elementos, las columnas de Grid son los campos en vigor. FindField, GetFieldCount e IsOperatorAllowed responden preguntas sobre ellos.

OnApply

OnApply(Sender, aRules, aResponse) se dispara tras Aplicar, Borrar y una pregunta respondida, con el árbol validado de la petición. Lo que devuelvas en aResponse se añade a la respuesta, por ejemplo tus filas marcadas con hx-swap-oob. Sin OnApply, ApplyToGrid copia el árbol en Rules y filtra el Grid enlazado.

GetSQLWhere

Devuelve la condición sin la palabra clave WHERE, o una cadena vacía para un árbol vacío. Los valores solo se escriben como los parámetros con nombre :qb1, :qb2 y así sucesivamente, cada uno añadido a aParams como qb1=<value>, de modo que el texto del usuario no puede cambiar la sentencia. La columna es el SQLName del campo, o su Name.

Validado, nunca de fiar

Todo lo que rellena un árbol desde fuera, JSON, una respuesta de IA o un formulario enviado, pasa por la misma validación. Se descartan las reglas sobre campos que no están en vigor, los operadores que el tipo no permite, las reglas por encima de MaxRules y los grupos más profundos que MaxDepth, los valores se recortan a 200 caracteres y se eliminan los subgrupos vacíos. SQLName entra en el SQL tal cual, así que defínelo en código y nunca desde una petición.

El formato JSON

ToJSON escribe un grupo como {"c":"and","r":[...]} y una regla como {"f":"field","o":"operator","v":"value"}, más "v2" para entre. ParseJSON y LoadFromJSON lo leen de vuelta y lo validan.

Matches

Matches evalúa el árbol sobre una fila dada como pares Name=Value, para filas en memoria. Cada método auxiliar acepta un árbol opcional, y nil significa Rules, de modo que un manejador de OnApply trabaja sobre el árbol de la petición sin tocar el componente compartido.

Varios usuarios

Un componente sirve a todas las sesiones, porque el evento de servidor trabaja sobre el árbol de la petición. En una página compartida por varios usuarios, asigna OnApply, guarda ToJSON(aRules) en la sesión y renderiza el constructor de cada usuario con ParseJSON y GetBuilderHTML.

El evento de servidor

Cada botón, y los selectores de campo y de operador cuando cambian, envían el evento de servidor qb al endpoint de eventos de componentes del motor HTMX, o a ActionURL. La respuesta es el constructor marcado con hx-swap-oob="true", que htmx reemplaza por su id: QueryBuilderID, o qb_ más ElementName.

La demo

Demos\60.HTML\01.RunTime\25.QueryBuilder es una página de pedidos en el puerto 8127 con el constructor sobre el grid y una tarjeta SQL que muestra la cláusula WHERE parametrizada que las mismas reglas ejecutarían en una base de datos. Su AI Assistant no tiene clave de API, así que el cuadro Preguntar responde sin conexión, y cada visitante conserva sus propias reglas en la sesión. Las versiones de tiempo de diseño, Lazarus y .NET lo replican.

Sigue explorando

Pruébalo en líneaMíralo en la demo Constructor de consultas, funcionando en vivo en el servidor de demos de eSeGeCe.
Ayuda en líneaReferencia completa de la API y guía de uso para este componente.
Componente GridEl grid que filtra el constructor y del que toma sus columnas.
AI AssistantConvierte una pregunta en lenguaje natural en las reglas del cuadro Preguntar.
Todos los componentes de sgcHTMLExplora la matriz de características completa de más de 80 componentes.
Descargar prueba gratuitaLa prueba de 30 días incluye los proyectos de demostración 60.HTML.
PreciosLicencias Single, Team y Site con todo el código fuente.
La mejor opción: All-AccessTodos los productos de eSeGeCe, con Premium Support incluido, desde €1,059 al año.
Ver precios de All-Access

¿Listo para empezar?

Descarga la prueba gratuita y empieza a crear interfaces web en Delphi, C++ Builder y .NET.