How to insert jsonb[] data into column using pg-promise - pg-promise

Given a table with a column of type jsonb[], how do I insert a json array into the column?
Using the provided formatters :array, :json won't work in this instance - unless I am missing the correct combination or something.
const links = [
{
title: 'IMDB',
url: 'https://www.imdb.com/title/tt0076759'
},
{
title: 'Rotten Tomatoes',
url: 'https://www.rottentomatoes.com/m/star_wars'
}
];
const result = await db.none(`INSERT INTO tests (links) VALUES ($1:json)`, [links]);

You do not need the library's :json filter in this case, as you need an array of JSON objects, and not a JSON with an array of JSON objects.
The former is formatted correctly by default, which then only needs ::json[] type casting:
await db.none(`INSERT INTO tests(links) VALUES($1::json[])`, [links]);
Other Notes
Use pg-monitor or event query to output queries being executed, for easier diagnostics.
Method none can only resolve with null, no point storing the result in a variable.
Library pg-promise does not have any :array filter, see supported filters.

Related

KnexJS Postgres: Adds extra double quotation marks to raw like query

Consider below code:
const items = await Item.query().where(
"type",
"like",
raw("'??'", [`%${term}%`])
);
I'm not getting any errors with the code above, but the database returns an empty result set. The created SQL query is below:
select "items".* from "items" where type LIKE '%"mobiles"%'
Please look at the like mobiles in the above SQL '%"mobiles"%' "" are treated as part of the value and returns an empty result set.
How can avoid "" in the query above?
Edit: Please note that I'm using ObjectionJS as well which uses Knex.
?? are supposed to be used for a column name.
I've 2 suggestions for you,
use the query without raw at all,
const items = await Item.query().where('type', 'like', `%${term}%`);
use single ?,
const items = await Item.query().where('type', 'like', raw("'?'", [`%${term}%`]));

how to convert map<anydata> to json

In my CRUD Rest Service I do an insert into a DB and want to respond to the caller with the created new record. I am looking for a nice way to convert the map to json.
I am running on ballerina 0.991.0 and using a postgreSQL.
The return of the Update ("INSERT ...") is a map.
I tried with convert and stamp but i did not work for me.
import ballerinax/jdbc;
...
jdbc:Client certificateDB = new({
url: "jdbc:postgresql://localhost:5432/certificatedb",
username: "USER",
password: "PASS",
poolOptions: { maximumPoolSize: 5 },
dbOptions: { useSSL: false }
}); ...
var ret = certificateDB->update("INSERT INTO certificates(certificate, typ, scope_) VALUES (?, ?, ?)", certificate, typ, scope_);
// here is the data, it is map<anydata>
ret.generatedKeys
map should know which data type it is, right?
then it should be easy to convert it to json like this:
{"certificate":"{certificate:
"-----BEGIN
CERTIFICATE-----\nMIIFJjCCA...tox36A7HFmlYDQ1ozh+tLI=\n-----END
CERTIFICATE-----", typ: "mqttCertificate", scope_: "QARC", id_:
223}"}
Right now i do a foreach an build the json manually. Quite ugly. Maybe somebody has some tips to do this in a nice way.
It cannot be excluded that it is due to my lack of programming skills :-)
The return value of JDBC update remote function is sql:UpdateResult|error.
The sql:UpdateResult is a record with two fields. (Refer https://ballerina.io/learn/api-docs/ballerina/sql.html#UpdateResult)
UpdatedRowCount of type int- The number of rows which got affected/updated due to the given statement execution
generatedKeys of type map - This contains a map of auto generated column values due to the update operation (only if the corresponding table has auto generated columns). The data is given as key value pairs of column name and column value. So this map contains only the auto generated column values.
But your requirement is to get the entire row which is inserted by the given update function. It can’t be returned with the update operation if self. To get that you have to execute the jdbc select operation with the matching criteria. The select operation will return a table or an error. That table can be converted to a json easily using convert() function.
For example: Lets say the certificates table has a auto generated primary key column name ‘cert_id’. Then you can retrieve that id value using below code.
int generatedID = <int>updateRet.generatedKeys.CERT_ID;
Then use that generated id to query the data.
var ret = certificateDB->select(“SELECT certificate, typ, scope_ FROM certificates where id = ?”, (), generatedID);
json convertedJson = {};
if (ret is table<record {}>) {
var jsonConversionResult = json.convert(ret);
if (jsonConversionResult is json) {
convertedJson = jsonConversionResult;
}
}
Refer the example https://ballerina.io/learn/by-example/jdbc-client-crud-operations.html for more details.?

get route only with specified parameter

I am new to MongoDB and CRUD APIs.
I have created my first database and inserted some data. I can do get, post and delete requests.
Now I want to request a 'get' by adding a parameter, so I do the following:
router.get('/:story_name', async function (req, res, next) {
const selectedStory = await loadStoryToRead()
res.send(await selectedStory.find({}).toArray())
})
say that story_name is S1C1,
I can do http://localhost:3000/api/whatever/s1c1 to get the data.
I would have expected to retrieve the data ONLY by using the specified parameter, however I can use the ID or the date or any other parameter found in the json file to get the data.
for example I can do
http://localhost:3000/api/whatever/5d692b6b21d5fdac2... // the ID
or
http://localhost:3000/api/whatever/2019-08-30T13:58:03.035Z ... // the created_at date
and obtain the same result.
Why is that?
How can I make sure that if I use router.get('/:story_name' ... I can retrieve the data only if I use the 'story_name' parameter?
Thanks!
* UPDATE *
my loadStoryToRead() looks like this:
async function loadStoryToRead () {
const client = await mongodb.MongoClient.connect(
'mongodb+srv://...', {
useNewUrlParser: true
})
return client.db('read').collection('selectedStory')
}
I will try to reformulate my question.
I want to ensure that the data is retrieved only by adding the 'story_name' parameter in the URL and not by adding any other parameter within the file.
The reading that I have done suggested to add the parameter to the get request, but when I do it, it doesn't matter what parameter I enter, I can still retrieve the data.
The delete request, however, is very specific. If I use router.delete('/:id'... the only way to delete the file is by using the ID parameter.
I would like to obtain the same with a get request and not using the 'id' but by using 'story_name'.
you can use regular expression capabilities for pattern matching strings in queries.
Syntax is:
db.<collection>.find({<fieldName>: /<string>/})
for example, you can use
var re = new RegExp(req.params.story_name,"g");
db.users.find({$or:[{"name": re}, {"_id": re}, {..other fields}]});
You can use $and and $or according to your requirement.
To read more follow the link
https://docs.mongodb.com/manual/reference/operator/query/regex/

How to return a plain value from a Knex / Postgresql query?

I'm trying to return a simple, scalar string value from a Postgres DB using Knex. So far, everything I do returns a JSON object with a key (the column name) and the value, so I have to reach into the object to get the value. If I return multiple rows, then I get multiple JSON objects, each one repeating the key.
I could be returning multiple columns, in which case each row would at least need to be an array. I'm not looking for a special case where specifying a single column returns the value without the array -- I'm OK reaching into the array. I want to avoid the JSON object with the repetitive listing of column names as keys.
I've scoured the Knex docs but don't see how to control the output.
My table is a simple mapping table with two string columns:
CREATE TABLE public._suite
(
piv_id character(18) NOT NULL,
sf_id character(18) NOT NULL,
CONSTRAINT _suite_pkey PRIMARY KEY (piv_id)
)
When I build a query using Knex methods like
let myId = 'foo', table = '_suite';
return db(table).where('piv_id', myId).first(['sf_id'])
.then( function(id) { return(id); });
I get {"sf_id":"a4T8A0000009PsfUAE"} ; what I want is just "a4T8A0000009PsfUAE"
If I use a raw query, like
return db.raw(`select sf_id from ${table} where piv_id = '${myId}'`);
I get a much larger JSON object describing the result:
{"command":"SELECT","rowCount":1,"oid":null,"rows":[{"sf_id":"a4T8A0000009Q9HUAU"}],"fields":[{"name":"sf_id","tableID":33799,"columnID":2,"dataTypeID":1042,"dataTypeSize":-1,"dataTypeModifier":22,"format":"text"}],"_parsers":[null],"RowCtor":null,"rowAsArray":false}
What do I have to do to just get the value itself? (Again, I'm OK if it's in an array -- I just don't want the column names.)
Take a look at the pluck method.
db(table).where('piv_id', myId).pluck('sf_id'); // => will return you ["a4T8A0000009PsfUAE"]

How to query a JSON element

Let's say I have a Postgres database (9.3) and there is a table called Resources. In the Resources table I have the fields id which is an int and data which is a JSON type.
Let's say I have the following records in said table.
1, {'firstname':'Dave', 'lastname':'Gallant'}
2, {'firstname':'John', 'lastname':'Doe'}
What I want to do is write a query that would return all the records in which the data column has a json element with the lastname equal to "Doe"
I tried to write something like this:
records = db_session.query(Resource).filter(Resources.data->>'lastname' == "Doe").all()
Pycharm however is giving me a compile error on the "->>"
Does anyone know how I would write the filter clause to do what I need?
Try using astext
records = db_session.query(Resource).filter(
Resources.data["lastname"].astext == "Doe"
).all()
Please note that the column MUST have a type of a JSONB. The regular JSON column will not work.
Also you could explicitly cast string to JSON (see Postgres JSON type doc).
from sqlalchemy.dialects.postgres import JSON
from sqlalchemy.sql.expression import cast
db_session.query(Resource).filter(
Resources.data["lastname"] == cast("Doe", JSON)
).all()
If you are using JSON type (not JSONB) the following worked for me:
Note the '"object"'
query = db.session.query(ProductSchema).filter(
cast(ProductSchema.ProductJSON["type"], db.String) != '"object"'
)
I have some GeoJSON in a JSON (not JSONB) type column and none of the existing solutions worked, but as it turns out, in version 1.3.11 some new data casters were added, so now you can:
records = db_session.query(Resource).filter(Resources.data["lastname"].as_string() == "Doe").all()
Reference: https://docs.sqlalchemy.org/en/14/core/type_basics.html#sqlalchemy.types.JSON
Casting JSON Elements to Other Types
Index operations, i.e. those invoked by calling upon the expression
using the Python bracket operator as in some_column['some key'],
return an expression object whose type defaults to JSON by default, so
that further JSON-oriented instructions may be called upon the result
type. However, it is likely more common that an index operation is
expected to return a specific scalar element, such as a string or
integer. In order to provide access to these elements in a
backend-agnostic way, a series of data casters are provided:
Comparator.as_string() - return the element as a string
Comparator.as_boolean() - return the element as a boolean
Comparator.as_float() - return the element as a float
Comparator.as_integer() - return the element as an integer
These data casters are implemented by supporting dialects in order to
assure that comparisons to the above types will work as expected, such
as:
# integer comparison
data_table.c.data["some_integer_key"].as_integer() == 5
# boolean comparison
data_table.c.data["some_boolean"].as_boolean() == True
According sqlalchemy.types.JSON, you can do it like this
from sqlalchemy import JSON
from sqlalchemy import cast
records = db_session.query(Resource).filter(Resources.data["lastname"] == cast("Doe", JSON)).all()
According to this, pre version 1.3.11, the most robust way should be like this, as it works for multiple database types, e.g. SQLite, MySQL, Postgres:
from sqlalchemy import cast, JSON, type_coerce, String
db_session.query(Resource).filter(
cast(Resources.data["lastname"], String) == type_coerce("Doe", JSON)
).all()
From version 1.3.11 onward, type-specific casters is the new and neater way to handle this:
db_session.query(Resource).filter(
Resources.data["lastname"].as_string() == "Doe"
).all()