From 846bc3f2035e53792656beed91af5a0122124e26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gera=20G=2E=20G=C3=BCiles?= Date: Tue, 20 Oct 2026 10:38:49 +0200 Subject: [PATCH] added: autoqueryjson() --- changelog.txt | 1 + querybuilder.js | 84 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/changelog.txt b/changelog.txt index dcfdae0..3ffaa47 100644 --- a/changelog.txt +++ b/changelog.txt @@ -9,6 +9,7 @@ - Any `NEWACTION` with the `mcp: true` property will be available in the endpoint - added a new method `JSONSchema.toAIObject(name, [description])` - added a new method `JSONSchema.toAITool(name)` +- added `QueryBuilder.autoqueryjson($, column, schema, [defsort], [maxlimit])` that works like `autoquery()` but for properties stored in a JSON(B) column ; the builder uses the neutral name `json:column:property:type` and the database adapter translates it into SQL - fixed `AIPARSER()` with tool calling - added a new controller method `$.content(value, content_type)` - added a new event `reset` in the FlowStream diff --git a/querybuilder.js b/querybuilder.js index 44489e7..7c4e218 100644 --- a/querybuilder.js +++ b/querybuilder.js @@ -2,6 +2,7 @@ // The MIT License // Copyright 2023 (c) Peter Širka +const REG_JSONKEY = /[^a-z0-9_]/i; const REG_FIELDS_CLEANER = /"|`|\||'|\s/g; var LANGUAGE_SKIP = '_'; @@ -1192,6 +1193,89 @@ QBP.autoquery = function(query, schema, defsort, maxlimit) { return t; }; +// Works like autoquery() but for properties stored in a JSON column +// Usage: .autoqueryjson($, 'data', 'id,name,email,age:Number,dtcreated:Date,tags:[string]', 'dtcreated_desc', 100) +// The builder doesn't generate any SQL. Fields, filters and sorting use the neutral name "json:column:property:type" +// and each database adapter translates it into its own syntax. +QBP.autoqueryjson = function($, column, schema, defsort, maxlimit) { + + let t = this; + let query = $ && $.query && typeof($.query) === 'object' ? $.query : ($ || {}); + + if (!column || REG_JSONKEY.test(column)) + throw new Error('QueryBuilder.autoqueryjson(): invalid column name "' + column + '"'); + + let key = 'QBJ' + column + '|' + schema; + let allowed = F.temporary.querybuilders[key]; + + if (!allowed) { + + let fields = {}; + let filter = []; + + for (let field of schema.split(',').trim()) { + + let k = field.split(':').trim(); + let name = k[0]; + + if (!name || REG_JSONKEY.test(name)) + continue; + + let type = (k[1] || 'string').toLowerCase(); + fields[name] = 'json:' + column + ':' + name + ':' + type; + filter.push({ name: name, type: type }); + } + + allowed = F.temporary.querybuilders[key] = { fields: fields, filter: filter }; + } + + let fieldscount = 0; + + if (!t.options.fields) + t.options.fields = []; + + if (query.fields) { + for (let field of query.fields.replace(REG_FIELDS_CLEANER, '').split(',')) { + if (allowed.fields[field]) { + t.options.fields.push(allowed.fields[field]); + fieldscount++; + } + } + } + + if (!fieldscount) { + for (let item of allowed.filter) + t.options.fields.push(allowed.fields[item.name]); + } + + for (let item of allowed.filter) + t.gridfilter(item.name, query, item.type, allowed.fields[item.name]); + + if (query.sort) { + + let count = 0; + + for (let item of query.sort.split(',')) { + let index = item.lastIndexOf('_'); + let name = index === -1 ? item : item.substring(0, index); + + if (!allowed.fields[name]) + continue; + + t.sort(allowed.fields[name], index !== -1 && item.substring(index + 1).toLowerCase() === 'desc'); + count++; + } + + if (!count && defsort) + t.gridsort(defsort, allowed.fields); + + } else if (defsort) + t.gridsort(defsort, allowed.fields); + + maxlimit && t.paginate(query.page, query.limit, maxlimit); + return t; +}; + QBP.query = function(value) { this.filter.push({ type: 'query', value: value }); return this;