I have a function that generates a prepared statement for batch insert into postgres where I am trying to insert the string into type jsonb in postgres.
My struct looks like:
type struct1 struct {
id int
comment string
extra string
}
and my table schema looks like:
create table deal (
id bigserial,
comment varchar(75),
extra jsonb
)
and I want to dump []struct1 to Postgres DB "deal".
My function which generates the prepared statement looks like this:
func BulkInsert(str []struct1, ctx context.Context) string {
log.Debug("inserting records to DB")
query := fmt.Sprintf(`insert into deal (%s) values `, strings.Join(dbFields, ","))
var numFields = len(dbFields)
var values []interface{}
for i, database := range str {
values = append(values, database.Comment,`'`+database.Extra+`'`)
n := i * numFields
query += `(`
for j := 0; j < numFields; j++ {
query += `$` + strconv.Itoa(n+j+1) + `,`
}
query = query[:len(query)-1] + `),`
}
query = query[:len(query)-1]
return query
Expected results should be: I should be able to insert string to json or you can say cast string to json and dump it.
The actual result is :
could not save batch: pq: invalid input syntax for type json"
Function of json_build_array('exp1'::Text, 'exp2'::Text) may help you.
return json object: {'exp1', 'exp2'}
And extract the values just use operator ->><index> like ->>1 to get 'exp2'.
If you just want to insert into database, function of to_json('any element') should also works, which can convert any element to a json object.
And you can get more funtions about json(jsonb) in postgres document.
Related
I'm using the postgres crate which makes a query with postgres::Connection. I query a table based on a string value in an ilike '%search_string%' expression:
extern crate postgres;
use std::error::Error;
//DB Create queries
/*
CREATE TABLE TestTable (
Id SERIAL primary key,
_Text varchar(50000) NOT NULL
);
insert into TestTable (_Text) values ('test1');
insert into TestTable (_Text) values ('test1');
*/
fn main() -> Result<(), Box<dyn Error>> {
let conn = postgres::Connection::connect(
"postgres://postgres:postgres#localhost:5432/notes_server",
postgres::TlsMode::None,
)?;
let text = "test";
// //Does not work
// let query = &conn.query(
// "
// select * from TestTable where _text ilike '%$1%'
// ",
// &[&text],
// )?;
//Works fine
let query = &conn.query(
"
select * from TestTable where Id = $1
",
&[&1],
)?;
println!("Rows returned: {}", query.iter().count());
Ok(())
}
If I uncomment the //Does not work part of the code, I will get the following error:
thread 'main' panicked at 'expected 0 parameters but got 1'
It appears it doesn't recognize the $1 parameter that is contained in the ilike expression. I've tried escaping the single quotes and that doesn't change it.
The only dependencies are:
postgres = { version = "0.15.2", features = ["with-chrono"] }
To my surprise, here was the fix:
let text = "%test%";
let query = &conn.query(
"
select * from TestTable where _text like $1
",&[&text],
)?;
Apparently the postgres function knows to add single quotes around strings in this scenario.
I found out about this from here: https://www.reddit.com/r/rust/comments/8ltad7/horrible_quote_escaping_conundrum_any_ideas_on/
An example should do the magic.
At the top, I am using pg. So, import it. My code looks like
const title = req.params.title;
const posts = await db.query("SELECT * FROM postsTable INNER JOIN usersTable ON postsTable.author = usersTable.username WHERE title ILIKE $1 ORDER BY postsTable.created_on DESC LIMIT 5;", [`%${title}℅`])
I have a JPA/Hibernate entity which has a JSONB column (using https://github.com/vladmihalcea/hibernate-types ) for storing a list of strings. This works fine so far.
#TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
#Type(type = "jsonb")
#Column(name = "TAGS", columnDefinition = "jsonb")
private List<String> tags;
Now I want to check if another string is contained in the list of strings.
I can do this by writing a native query and use the #> operator from Postgres. Because of other reasons (the query is more complex) I do not want to go in that direction. My current approach is calling the jsonb_contains method in a Spring Data specification (since the operator is just alias to this function), e.g. jsonb_contains('["tag1", "tag2", "tag3"]','["tag1"]'). What I am struggling with is, getting the second parameter right.
My initial approach is to also use a List of Strings.
public static Specification<MyEntity> hasTag(String tag) {
return (root, query, cb) -> {
if (StringUtils.isEmpty(tag)) {
return criteriaBuilder.conjunction();
}
Expression<Boolean> expression = criteriaBuilder.function("jsonb_contains",
Boolean.class,
root.get("tags"),
criteriaBuilder.literal(List.of(tag)));
return criteriaBuilder.isTrue(expression);
};
}
This results in the following error.
Caused by: org.postgresql.util.PSQLException: ERROR: function jsonb_contains(jsonb, character varying) does not exist
Hinweis: No function matches the given name and argument types. You might need to add explicit type casts.
Position: 375
It does know that root.get("tags") is mapped to JSONB but for the second parameter it does not. How can I get this right? Is this actually possible?
jsonb_contains(jsob, jsonb) parameters must be jsonb type.
You can not pass a Java String as a parameter to the function.
You can not do casting in Postgresql via JPA Criteria.
Using JSONObject or whatever does not help because Postgresql sees it as
bytea type.
There are 2 possible solutions:
Solution 1
Create jsonb with jsonb_build_object(text[]) function and send it to jsonb_contains(jsonb, jsonb) function:
public static Specification<MyEntity> hasTag(String tag) {
// get List of key-value: [key1, value1, key2, value2...]
List<Object> tags = List.of(tag);
// create jsonb from array list
Expression<?> jsonb = criteriaBuilder.function(
"jsonb_build_object",
Object.class,
cb.literal(tags)
);
Expression<Boolean> expression = criteriaBuilder.function(
"jsonb_contains",
Boolean.class,
root.get("tags"),
jsonb
);
return criteriaBuilder.isTrue(expression);
}
Solution 2
Create custom function in your Postgresql and use it in Java:
SQL:
CREATE FUNCTION jsonb_contains_as_text(a jsonb, b text)
RETURNS BOOLEAN AS $$
SELECT CASE
WHEN a #> b::jsonb THEN TRUE
ELSE FALSE
END;$$
LANGUAGE SQL IMMUTABLE STRICT;
Java Code:
public static Specification<MyEntity> hasTag(String tag) {
Expression<Boolean> expression = criteriaBuilder.function(
"jsonb_contains_as_text",
Boolean.class,
root.get("tags"),
criteriaBuilder.literal(tag)
);
return criteriaBuilder.isTrue(expression);
}
I think that the reason is that you pass the varchar as the second param. jsonb_contains() requires two jsonb params.
To check a jsonb array contains all/any values from a string array you need to use another operators: ?& or ?|.
The methods bindings for them in PSQL 9.4 are: jsonb_exists_all and jsonb_exists_any correspondingly.
In your PSQL version, you could check it by the following command:
select * from pg_operator where oprname = '?&'
I have the following gorm.Model and I want to Query my Postgres database to return Confessions that have a specific category in their .Categories attribute, but I have no idea how to Query inside a pq.StringArray. Is there a work-around?
type Confession struct {
gorm.Model
User string `json:"User"`
Title string `json:"Title"`
Body string `json:"Body"`
Mood string `json:"Mood"`
Categories pq.StringArray `gorm:"type:varchar(64)[]" json:"Categories"`
}
And here is how I tried to query, but using the LIKE operator throws an error.
if categories != nil {
for _, cat := range categories {
tx = tx.Where("Categories LIKE ?", "%"+cat+"%")
}
}
Use <# for array contains. You can query using Query function of *sql.DB and get *sql.DB using tx.DB() in gorm
sel := "SELECT * FROM confessions WHERE $1 <# categories"
categories := []string{"cat1", "cat2"}
rows, err := tx.DB().Query(sel, pq.Array(categories))
Or try Gorm Raw SQL , but I won't sure it will work properly or not for array functions.
References:
PostgreSQL Array function here
ProtgreSQL Array use in golang here
The easiest solution to my problem was to use the .Where command as such
tx = tx.Where("categories && ?", pq.Array(categories))
This will return a gorm.DB so I can continue to chain actions. The && operator is to check for OVERLAPPING elements.
I am having postgres db table as
CREATE TABLE foo (
name varchar(50),
types varchar(50)[],
role varchar[10]
);
and corresponding struct in go:
type Foo struct {
Name string `db:"name"`
Types []string `db:"types"`
Role string `db:"role"`
}
I want to fetch db rows into my struct. Right now I am able to do this by using:
var foo Foo
query := `SELECT name, types, roles FROM foo LIMIT 1`
err = dbConn.QueryRow(query).Scan(&foo.Name, pq.Array(&foo.Types), &foo.Role)
But I want to achieve the same using direct mapping. Something like:
var foo []Foo
query := `SELECT name, types, roles FROM foo`
dbWrapper.err = dbConn.Select(&foo, query)
Above snippet gives me error because of Types being pq array. Is it possible to directly map pq array as a part of struct?
Thanks to https://stackoverflow.com/a/44385791/10138004, I am able to solve this pq driver for sqlx (https://godoc.org/github.com/lib/pq) itself by replacing []string with pq.StringArray.
So, updated struct looks like:
type Foo struct {
Name string `db:"name"`
Types pq.StringArray `db:"types"` //this is what changed.
Role string `db:"role"`
}
and direct mapping is working like a charm now
var foo []Foo
query := `SELECT name, types, roles FROM foo`
dbWrapper.err = dbConn.Select(&foo, query)
You can use pg-go lib for that. Please look at pg.Model(). It's possible to pass an entire struct to it.
I am trying to insert into SQL Server DateTime field. Trying simple scenario of one table having datetime column named start_date only.
Query I am trying is
INSERT INTO test (start_date) values (${start_date})
start_date is channelMap variable of Type java.util.Date , It was created using :
var start_date = DateUtil.getDate('yyyyMMddHHmmss', msg['date'].toString());
Here start_date is of java.util.Date, why mirth treats it as String when it tries to insert into database ??
You can handle the conversion even in SQL. Hope it helps
var start_date = msg['PID']['PID.7']['PID.7.1'].toString(); // 19831123 - YYYYMMDD format
try {
sql="INSERT INTO test (start_date) values (convert(datetime,'" + start_date + "',5))";
logger.info(sql);
rst = dbConn.executeUpdate(sql);
}
catch(err) {
logger.info('ERR: ' + err);
}
Out in DB will be below.
select * from test
start_date |
----------
1983-11-23 00:00:00.000
2nd Approach
If you still want to use util try below
var start_date = msg['PID']['PID.7']['PID.7.1'].toString(); // 19831123 - YYYYMMDD format
/*
Input is yyyyMMdd and
output is yyyyMMddHHmmss format
*/
var datestring = DateUtil.convertDate('yyyyMMdd', 'yyyyMMddHHmmss', start_date);
try {
sql="INSERT INTO test (start_date) values ('" + start_date + "')";
logger.info(sql);
rst = dbConn.executeUpdate(sql);
}
catch(err) {
logger.info('ERR: ' + err);
}
I believe your data is inserting in DB as 'Mon Feb 19 09:25:16 IST 1968' along with quotes.
I used formatDate function, but data inserted into DB will be like 1968-02-19 09:25:16
var pidDate = msg['PID']['PID.7']['PID.7.1'].toString();
var value = DateUtil.getDate("yyyyMMddHHmmss",pidDate);
var data = DateUtil.formatDate("yyyyMMddHHmmss", value)
channelMap.put("start_date",data);
Inserting to DB:
var dateValue = $('start_date')
dbConn = DatabaseConnectionFactory.createDatabaseConnection(dbDriver, dbAddress, userName,passwd);
result = dbConn.executeUpdate("INSERT INTO test (startdate) values ('"+dateValue+"')");
I'm sending date value as 19680219092516,inside DB value is 1968-02-19 09:25:16.Datatype of my DB is DateTime type.
the getDate function returns a java.util.Date object, but when I tried with getCurrentDate function, it returns a formatted String. I guess formatting the date object is one way of inserting data into DB.