Postgres: create a new jsonb object with matched values - postgresql

So I have this table questions that have a settings jsonb column:
{
id: 'question-id-1',
settings: {
foo1: true,
foo2: true,
bar: false
}
},
{
id: 'question-id-2',
settings: {
bar: true
}
}
now I want to make a postgres db update migration script that results to:
{
id: 'question-id-1',
settings: {
foo1: true,
foo2: true,
bar: false,
opts: ['foo1', 'foo2']
}
},
{
id: 'question-id-2',
settings: {
bar: true,
opts: ['bar']
}
}
so only those that has values true is added to a new opts array inside settings.
This is for Postgres 9.5.
Thank you in advanced.

Create a function to update the column:
create or replace function add_opts(jsonb)
returns jsonb language sql as $$
select $1 || jsonb_build_object('opts', jsonb_agg(key))
from jsonb_each_text($1)
where value::bool;
$$;
Test:
with questions(settings) as (
values
('{
"foo1": true,
"foo2": true,
"bar": false
}'::jsonb)
)
select add_opts(settings)
from questions;
add_opts
----------------------------------------------------------------------
{"bar": false, "foo1": true, "foo2": true, "opts": ["foo1", "foo2"]}
(1 row)
Your update query should look like this:
update questions
set settings = add_opts(settings);
The variant which eliminates the opts array when there are no set options:
create or replace function add_opts(jsonb)
returns jsonb language sql as $$
select case
when jsonb_agg(key) is null then $1
else $1 || jsonb_build_object('opts', jsonb_agg(key))
end
from jsonb_each_text($1)
where value::bool;
$$;

Related

Vscode language server extension connection.onCompletion

I am creating a language server. Specifically, the issue is with completions. I'm returning a big list of completion items but whenever I test my language the completion provider simply will not suggest anything after I type a dot(.) when I'm expecting it to suggest, essentially, class members associated with the symbol left of said dot(.). Don't get me wrong, suggestions work until I type the dot(.) character and then it says, "No Suggestions".
Also, I'm trying to implement this server side and not client side.
EDIT:
Essentially, I'm assembling a big list and returning it.
connection.onInitialize((params: node.InitializeParams) => {
workspaceFolder = params.workspaceFolders![0].uri;
const capabilities = params.capabilities;
// Does the client support the `workspace/configuration` request?
// If not, we fall back using global settings.
hasConfigurationCapability = !!(
capabilities.workspace && !!capabilities.workspace.configuration
);
hasWorkspaceFolderCapability = !!(
capabilities.workspace && !!capabilities.workspace.workspaceFolders
);
hasDiagnosticRelatedInformationCapability = !!(
capabilities.textDocument &&
capabilities.textDocument.publishDiagnostics &&
capabilities.textDocument.publishDiagnostics.relatedInformation
);
capabilities.workspace!.workspaceEdit!.documentChanges = true;
const result: node.InitializeResult = {
capabilities: {
textDocumentSync: node.TextDocumentSyncKind.Incremental,
colorProvider: true,
hoverProvider: true,
definitionProvider: true,
typeDefinitionProvider: true,
referencesProvider: true,
documentHighlightProvider: true,
documentSymbolProvider: true,
workspaceSymbolProvider: true,
// codeActionProvider: true,
codeLensProvider: {
resolveProvider: true,
workDoneProgress: false
},
// Tell the client that this server supports code completion.
completionProvider: {
resolveProvider: true,
workDoneProgress: false,
triggerCharacters: ['.', '/'],
allCommitCharacters: ['.']
},
signatureHelpProvider: {
triggerCharacters: ['('],
retriggerCharacters: [','],
workDoneProgress: false
},
executeCommandProvider: {
commands: ["compile"],
workDoneProgress: false
},
semanticTokensProvider: {
documentSelector: [{ scheme: 'file', language: 'agc' }],
legend: {
tokenTypes: gTokenTypes,
tokenModifiers: gTokenModifiers
},
full: true,
workDoneProgress: false
}
}
};
if (hasWorkspaceFolderCapability) {
result.capabilities.workspace = {
workspaceFolders: {
supported: true
}
};
}
return result;
});
// This handler provides the initial list of the completion items.
connection.onCompletion(
async (params: node.TextDocumentPositionParams): Promise<node.CompletionItem[] | node.CompletionList | undefined> => {
console.log("completion");
let doc = documents.get(params.textDocument.uri);
let list:node.CompletionList = node.CompletionList.create([], true);
list.items = list.items.concat(comp.GetAGKKeywordCompletionItems(), comp.GetAGKCommandCompletionItems(), agkDocs.getALLCompletionItems());
list.items.push(sense.GetCommentSnippetCompletionItem(doc, params.position));
list.items.push(sense.GetCommentSnippetCompletionItem(doc, params.position, true));
console.log("END completion");
return list;
}
);

Issue Populating Filter Value for AG Grid agSetColumnFilter

I'm trying to populate the value for the agSetColumnFilter, but I'm getting an error that I cannot find anything where in documentation (or anywhere online). Has anyone ever run into this issue?
This is what the column definition looks like:
columnDefs.push({
headerName: col.name,
field: col.name,
def: col,
rowGroup: k < groupedColumnCount ? true : false,
pinned: k < _this.groupBy.length ? 'left' : null,
lockPinned: k < _this.groupBy.length ? true : false,
hide: k < groupedColumnCount ? true : false,
suppressToolPanel: _this.groupBy.length ? true : false,
valueGetter: function(data){
if(data.data){
var def = data.colDef.def;
var value = data.data[data.colDef.field];
if(value){
return value.value;
}else{
return null;
}
}else{
return data.value;
}
},
valueFormatter: function(data){
if(data.data){
var def = data.colDef.def;
var value = data.data[data.colDef.field];
if(!value) return null;
if(value.formatted){
_this.cache[data.colDef.field + value.value] = value.formatted;
}
return value.formatted ? value.formatted : value.value;
}else{
if(_this.cache[data.colDef.field + data.value]){
return _this.cache[data.colDef.field + data.value];
}else{
return data.value;
}
}
},
keyCreator: function(params){
console.log(params);
},
filter: 'agSetColumnFilter',
filterParams: {
values: function (params) {
params.success([{
$uri: 'nhuihi',
value: {
$value: 'some text'
}
}]);
}
}
});
I'm only printing out keyCreator params for now since I don't know what will actually be available in the data. The idea is that I can set values using complex objects returned from the server and display a formatted value instead of a key. This is the error I'm getting.
ag-grid-enterprise.min.noStyle.js:formatted:27684 Uncaught TypeError: Cannot read property 'onFilterValuesReady' of undefined
at t.setFilterValues (ag-grid-enterprise.min.noStyle.js:formatted:27684)
at e.modelUpdatedFunc (ag-grid-enterprise.min.noStyle.js:formatted:27609)
at e.onAsyncValuesLoaded (ag-grid-enterprise.min.noStyle.js:formatted:27917)
at values (comparison-table-v7.js:1253)
at e.createAllUniqueValues (ag-grid-enterprise.min.noStyle.js:formatted:27909)
at new e (ag-grid-enterprise.min.noStyle.js:formatted:27867)
at t.initialiseFilterBodyUi (ag-grid-enterprise.min.noStyle.js:formatted:27608)
at t.init (ag-grid-enterprise.min.noStyle.js:formatted:18945)
at e.initialiseComponent (ag-grid-enterprise.min.noStyle.js:formatted:10602)
at e.createAgGridComponent (ag-grid-enterprise.min.noStyle.js:formatted:10574)
Here's a test case for it as well. I simply modified the example by AG Grid. https://plnkr.co/edit/GURQHP0KKFpJ9kwaU83M?p=preview
If you open up console, you will see an error when you click on Athletes filter.
Also reported on GitHub: https://github.com/ag-grid/ag-grid/issues/2829
If you need to configure filter values without async requests
filterParams: {
values: getFilterValuesData()
}
getFilterValuesData(){
//data preparation
//little bit modified sample to present that you can handle your logic here
let data = [];
[
'John Joe Nevin',
'Katie Taylor',
'Paddy Barnes',
'Kenny Egan',
'Darren Sutherland',
'Margaret Thatcher',
'Tony Blair',
'Ronald Regan',
'Barack Obama'
].forEach(i=>{
data.push(i);
});
return data;
}
If it requires to make an async request for data preparation you can use callback function:
filterParams: {
values: (params)=>{
setTimeout(()=>{ -- setTimeout on this case only for async request imitation
params.success(['value 1', 'value 2'])
}, 5000)
}
}
Notice: params.success(...) should be used only with an async request
Doc: ag-grid Asynchronous Values

Sails.js one to many association with postgreSQL: column does not exist

I need some help with associations in sails 0.12.13 with postgresql.
I have an "App" model and a "Membership" model. Relation should be one to many (one app can be associated with many relationships).
This is the App model db table schema (table is called "apps"):
Table "public.apps"
Column | Type | Modifiers
------------+-----------------------------+---------------------------------------------------
id | integer | not null default nextval('apps_id_seq'::regclass)
name | character varying | not null
Indexes:
"apps_pkey" PRIMARY KEY, btree (id)
"apps_name_key" UNIQUE CONSTRAINT, btree (name)
Referenced by:
TABLE "memberships" CONSTRAINT "app_fk" FOREIGN KEY (app_id) REFERENCES apps(id) ON UPDATE RESTRICT ON DELETE CASCADE
And this is memberships:
Table "public.memberships"
Column | Type | Modifiers
------------+-----------------------------+----------------------------------------------------------
id | integer | not null default nextval('memberships_id_seq'::regclass)
app_id | integer | not null
Foreign-key constraints:
"app_fk" FOREIGN KEY (app_id) REFERENCES apps(id) ON UPDATE RESTRICT ON DELETE CASCADE
in my user model, i have this:
module.exports = {
tableName: 'apps',
autoCreatedAt: false,
autoUpdatedAt: false,
attributes: {
name: { type: 'string', unique: true, required: true, alphanumericdashed: true },
memberships: { collection: 'memberships', model: 'Membership' },
}
}
And this is the Membership model:
module.exports = {
tableName: 'memberships',
autoCreatedAt: false,
autoUpdatedAt: false,
attributes: {
app: { model: 'app', columnName: 'app_id' },
},
};
When I try to query an app and get its memberships:
App.find({ id: 1 }).populate('memberships').exec((err, app) => {
if (err) throw err;
console.log(app.memberships);
});
I get this error:
Error (E_UNKNOWN) :: Encountered an unexpected error
error: column apps.memberships does not exist
at Connection.parseE (/usr/src/app/node_modules/sails-postgresql/node_modules/pg/lib/connection.js:539:11)
at Connection.parseMessage (/usr/src/app/node_modules/sails-postgresql/node_modules/pg/lib/connection.js:366:17)
at Socket.<anonymous> (/usr/src/app/node_modules/sails-postgresql/node_modules/pg/lib/connection.js:105:22)
at emitOne (events.js:115:13)
at Socket.emit (events.js:210:7)
at addChunk (_stream_readable.js:252:12)
at readableAddChunk (_stream_readable.js:239:11)
at Socket.Readable.push (_stream_readable.js:197:10)
at TCP.onread (net.js:589:20)
Looks like the association is not "enabled" and waterline is searching for an actual column "membership" in my model. Can anybody explain me what I am doing wrong? thx
According to the documentation, I would guess that you have a bad association.
// App.js
module.exports = {
tableName: 'apps',
autoCreatedAt: false,
autoUpdatedAt: false,
attributes: {
name: {
type: 'string',
unique: true,
required: true,
alphanumericdashed: true
},
memberships: {
collection: 'membership', // <-- changed to singular (as your model should be)
via: 'app' // <-- use "via" instead of "model"
},
}
}
// Membership.js
module.exports = {
tableName: 'memberships',
autoCreatedAt: false,
autoUpdatedAt: false,
attributes: {
app: {
model: 'app'
// <-- removed the "columnName" here
},
},
};
Also, convention generally says name your model as a singular instance. For example, its "User.js" and not "Users.js". It's valid to refer to the collection as a plural. I made some changes in your naming, but you'll have to see how that affects your files (since you didn't give those names).

node-postgres does not show result set for stored procedure

I'm trying to use a stored procedure to create a character. The procedure does some validation to make sure that the params are valid before inserting them into the database(let's keep this validation on the database). However when I attempt to use the procedure in my web interface with invalid data, there is no result set, and the error promise does not happen. What do I have to do to make this procedure cause an error on the node side?
-- add a character with the universe performing the universe name lookup
CREATE OR REPLACE FUNCTION add_character(
IN p_chr_name VARCHAR(255),
IN p_unv_name VARCHAR(255),
IN p_chr_bio TEXT
)
RETURNS INT AS $$
DECLARE
unv_id INTEGER := (SELECT unv_id
FROM universes
WHERE LOWER(unv_name) = LOWER(p_unv_name));
new_id INTEGER := NULL;
BEGIN
IF unv_id IS NULL THEN
RAISE EXCEPTION 'unv_id is null for %', p_unv_name;
END IF;
INSERT INTO characters(chr_name, chr_unv_id, chr_bio) VALUES
(p_chr_name, unv_id, p_chr_bio)
RETURNING chr_id INTO new_id;
RETURN new_id;
END $$
LANGUAGE PLPGSQL;
The javascript
app.post('/contrib/chr', (req, res) => {
console.log(req.body);
pool.query('SELECT add_character($1, $2, $3)'
[req.body.chr_name, req.body.unv_name, req.body.chr_bio])
.then((rows) => {
console.log(rows);
res.render('contrib');
}).catch((err) => {
console.error('contrib-chr-err', err);
res.render('contrib');
});
})
The row object returned.
{ command: '', rowCount: NaN, rows: [], fields: null }
working sample:
sql:
t=# create or replace function s110(_b boolean) returns int as
$$
begin
if not _b then
raise info '%','here is notice';
raise warning '%','warning it is';
end if;
if _b then
raise exception '%','final exception it is';
end if;
return 9;
end;
$$ language plpgsql;
CREATE FUNCTION
js
var pg = require('pg');
var client = new pg.Client({"database": "t"});
client.connect(function (err) {
if (err) throw err;
client.query('SELECT * from s110(true)', [], function (err, result) {
if (err) throw err;
console.log(result);
client.end(function (err) {
if (err) throw err;
});
});
});
run
MacBook-Air:n vao$ vi q1.js && node q1.js
/Users/vao/n/q1.js:6
if (err) throw err;
^
error: final exception it is
at Connection.parseE (/Users/vao/node_modules/pg/lib/connection.js:569:11)
at Connection.parseMessage (/Users/vao/node_modules/pg/lib/connection.js:396:17)
at Socket.<anonymous> (/Users/vao/node_modules/pg/lib/connection.js:132:22)
at emitOne (events.js:77:13)
at Socket.emit (events.js:169:7)
at readableAddChunk (_stream_readable.js:153:18)
at Socket.Readable.push (_stream_readable.js:111:10)
at TCP.onread (net.js:531:20)
if I change SELECT * from s110(true) to SELECT * from s110(false):
MacBook-Air:n vao$ vi q1.js && node q1.js
{ command: 'SELECT',
rowCount: 1,
oid: NaN,
rows: [ { s110: 9 } ],
fields:
[ { name: 's110',
tableID: 0,
columnID: 0,
dataTypeID: 23,
dataTypeSize: 4,
dataTypeModifier: -1,
format: 'text' } ],
_parsers: [ [Function] ],
RowCtor: [Function],
rowAsArray: false,
_getTypeParser: [Function: bound ] }
UPDATE
Also you can get STDOUT messages with little change. I'm not good in javascript, hopefully I don't advice very silly things, here. If you add this line above .query:
client.connection.on('message', function(a) {console.log(a);});
client.query('SELECT s110(false)', [], function (err, result) {...
You will get all RAISE output to console.log:
{ [notice: here is notice]
name: 'notice',
length: 123,
severity: 'INFO',
code: '00000',
detail: undefined,
hint: undefined,
position: undefined,
internalPosition: undefined,
internalQuery: undefined,
where: 'PL/pgSQL function s110(boolean) line 4 at RAISE',
schema: undefined,
table: undefined,
column: undefined,
dataType: undefined,
constraint: undefined,
file: 'pl_exec.c',
line: '3165',
routine: 'exec_stmt_raise' }
{ [notice: warning it is]
name: 'notice',
length: 128,
severity: 'WARNING',
code: '01000',
detail: undefined,
hint: undefined,
position: undefined,
internalPosition: undefined,
internalQuery: undefined,
where: 'PL/pgSQL function s110(boolean) line 5 at RAISE',
schema: undefined,
table: undefined,
column: undefined,
dataType: undefined,
constraint: undefined,
file: 'pl_exec.c',
line: '3165',
routine: 'exec_stmt_raise' }
{ name: 'dataRow', length: 11, fieldCount: 1, fields: [ '9' ] }
{ name: 'commandComplete', length: 13, text: 'SELECT 1' }
{ name: 'readyForQuery', length: 5, status: 'I' }

Sequelize.js - How to create non-trivial associations without raw SQL?

Here is my situation:
I'm using postgres 9.4, Sequelize ORM and have following models:
Service
serviceCode - primary key, string of 6 characters
serviceTitle - string
ServiceGroup
serviceCodePrefixes - array of strings that are prefixes for Service.serviceCode
serviceGroupTitle - string
Task
serviceCode - reference to Service
I need to build Task object populated with Service and ServiceGroup objects. Example:
In database:
Service {
serviceCode: '123232',
serviceTitle: 'svc title #1',
}
ServiceGroup {
serviceCodePrefix: ['12', '13', '92', ...],
serviceGroupTitle: 'svc grp title #1',
}
Task {
serviceCode: '123232',
}
Result:
Task {
service: {
serviceTitle: 'svc title #1',
},
serviceGroup: {
serviceGroupTitle: 'svc grp title #1',
},
}
The problem is that serviceCodePrefix contains not simple IDs, which can be used to create association using hasOne/belongsTo/etc., but prefix for ID.
So questions is: how this can be done without raw sql?
Turns out that right now Sequelize has experimental feature: 'on' option for 'include'. This option allows users to customize joining conditions. So my problem can be solved this way:
const Service = sequelize.define('service', {
serviceTitle: Sequelize.STRING,
serviceCode: Sequelize.STRING,
});
const ServiceGroup = sequelize.define('service_group', {
serviceGroupTitle: Sequelize.STRING,
// Array of prefixes (e.g. ['01%', '023%'])
serviceCodePrefix: Sequelize.ARRAY(Sequelize.STRING),
});
const Task = sequelize.define('task', {
taskTitle: Sequelize.STRING,
serviceCode: Sequelize.STRING,
});
Task.belongsTo(Service, { foreignKey: 'serviceCode' });
// Hack needed to allow 'include' option to work
Task.hasMany(ServiceGroup, { foreignKey: 'serviceCodePrefix', constraints: false });
// And finally
Task.findAll({
include: [
{ model: Service },
{
model: ServiceGroup,
on: [' "task"."serviceCode" LIKE ANY("serviceGroup"."serviceCodePrefix") '],
},
],
});
Not sure about the performance though.