How can I use an hstore column type with Npgsql? - postgresql

I have a table with the following schema:
CREATE TABLE account
(
id serial primary key,
login varchar(40) not null,
password varchar(40) not null,
data hstore
);
I'd like to use an NpgsqlCommand object with parameters to retrieve and store the account data from my application. Which DbType do I have to use for the NpgsqlParameter? The enum NpgsqlDbType does not have a value for hstore. Can I use a Dictionary or HashTable as value of the NpgsqlParameter object?
When I use a JSON column I can create a parameter of type NpgsqlDbType.Text, use a library like JSON.Net to serialize an object to a JSON string and send an SQL statement like that:
INSERT INTO account (login, password, data) VALUES (:login, :password, :data::json)
Unfortunately this does not work with an hstore column. I get a syntax error when I try to do this:
INSERT INTO account (login, password, data) VALUES (:login, :password, :data::hstore)
The string I pass to the data parameter looks like this:
'key1 => "value1", key2 => "value2"'

Thank you, Francisco! I saw in the log that the single quotes (') at the beginning and the end of the string are escaped when they are passed to PostgreSQL. When I pass
key1 => "value1", key2 => "value2"
instead, I can insert the data into the hstore column.

Related

Slick Postgres: How to search in a list of strings using the like operator

There is a simple database entity:
case class Foo(id: Option[UUID], keywords: Seq[String])
I want to implement a search function which returns all entities of type Foo which have at least one keyword that contains the search string.
I'm using Slick and tried this:
def searchKeywords(txt: String): Future[Seq[Foo]] = {
val action = Foos.filter(p => p.keywords.any like s"%$txt%").result
db.run(action)
}
This piece of code compiles, but when executing, I get this SQL error:
PSQLException: ERROR: syntax error at or near "any"
The generated sql statement looks like:
select "id", "title", "tagline", "logo", "short_desc", "keywords", "initial_condition", "work_process", "end_result", "ts", "lm", "v" from "projects" where any("keywords") like '%foo%'
And it does not work with postgresql. (I'm using v12)
Schema for the table looks like this:
CREATE TABLE foos
(
id UUID NOT NULL PRIMARY KEY,
keywords varchar[] NOT NULL
);
How can I achieve to search in a list of strings using the like operator?
From a pure SQL point of view, you need a derived table to achieve that. I hope some expert corrects me if I'm wrong but you can't use SQL operator like on a array.
Supposing your table construction is :
CREATE TABLE foos
(
id UUID NOT NULL PRIMARY KEY,
keywords varchar[] NOT NULL
);
Then an SQL way of retrieving the results would be :
select * from (
select id, unnest(keywords) as keyw from foos
) myTable where keyw like '%foo%'
Otherwise, the syntax you're using for the like operator seems correct.
myProperty like s"%$myVariable"

PostgreSQL field type to store string, int, or json

I want to store a table for variables with table definition like below
variable
--------
id int
var_type int 0: number, 1: string, 2: json
var_name int
var_value ??? varchar or jsonb?
If I use varchar, how to store the json type variable and if, I am using jsonb how to store the int and string?
The example json value that is stored,
[{"name": "Andy", "email" : "andy#mail.id"},{"name": "Cindy", "email" : "cindy#mail.id"}]
TIA
Beny
When you have data and you don't know the structure, use a single jsonb column. JSON can handle strings, numbers, and more JSON.
{
"string": "basset hounds got long ears",
"number": 23.42,
"json": [1,2,3,4,5]
}
Don't try to cram them all into a single array. Put them in separate rows.
One row: {"name": "Andy", "email" : "andy#mail.id"}
Another row: {"name": "Cindy", "email" : "cindy#mail.id"}
However, your example feels like its avoiding designing a schema. JSONB is useful, but overusing it defeats the point of a relational database.
create table people (
id bigserial primary key,
// Columns for known keys which can have constraints.
name text not null,
email text not null,
// JSONB for extra keys you can't predict.
data jsonb
)
Use the JSON operators to query individual pairs.
select
name, email, data->>'favorite dog breed'
from some_table

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"]

Insert with defaults using subquery in knex

I have this query;
knex('metrics').insert(function() {
this.select('metric as name')
.from('stage.metrics as s')
.whereNotExists(function() {
this.select('*')
.from('metrics')
.where('metrics.name', knex.raw('s.metric'))
})
})
The table metrics has two columns; an id, which is incrementing, and name. I expected this to insert into the name column because the subquery has one column, labeled name, and default id. however, instead it complains that I am providing a column of type character varying for my integer column id. How do I make it explicit that I want the id to take the default value?
This can do the trick
knex('metrics').insert(function() {
this
.select([
knex.raw('null::bigint as id'), // or any other type you need (to force using default value you need to pass explicitly null value to insert query)
'metric as name'
])
.from('stage.metrics as s')
.whereNotExists(function() {
this.select('*')
.from('metrics')
.where('metrics.name', knex.raw('s.metric'))
})
})
I know, looks a bit hacky. Would be great to see something in knex API like (example below is a proposal and not a working example)
knex('table_name')
.insert(
['name', 'surname'],
function () {
this.select(['name', 'surname']).from('other_table'))
}
)
Which produces
insert into table_name (name, surname) select name, surname from other_table;
I'm not sure about this interface, but you got the point. Like explicitly write fields you want to insert.

Updating an array of objects fields in crate

I created a table with following syntax:
create table poll(poll_id string primary key,
poll_type_id integer,
poll_rating array(object as (rating_id integer,fk_user_id string, israted_image1 integer, israted_image2 integer, updatedDate timestamp, createdDate timestamp )),
poll_question string,
poll_image1 string,
poll_image2 string
)
And I inserted a record without "poll_rating" field which is actually an array of objects fields.
Now when I try to update a poll_rating with the following commands:
update poll set poll_rating = [{"rating_id":1,"fk_user_id":-1,"israted_image1":1,"israted_image2":0,"createddate":1400067339.0496}] where poll_id = "f748771d7c2e4616b1865f37b7913707";
I'm getting an error message like this:
"SQLParseException[line 1:31: no viable alternative at input '[']; nested: ParsingException[line 1:31: no viable alternative at input '[']; nested: NoViableAltException;"
Can anyone tell me why I get this error when I try to update the array of objects fields.
Defining arrays and objects directly in SQL statement is currently not supported by our SQL parser, please use parameter substitution using placeholders instead as described here:
https://crate.io/docs/current/sql/rest.html
Example using curl is as below:
curl -sSXPOST '127.0.0.1:4200/_sql?pretty' -d#- <<- EOF
{"stmt": "update poll set poll_rating = ? where poll_id = ?",
"args": [ [{"rating_id":1,"fk_user_id":-1,"israted_image1":1,"israted_image2":0,"createddate":1400067339.0496}], "f748771d7c2e4616b1865f37b7913707" ]
}
EOF