Get existing and inserted IDs in upsert operation (db.Clauses clause.OnConflict) - postgresql

I have a scenario where I need to insert into a table some array of data, In which case if the combination of name and version already exists (composite unique constrain), I need to get those IDs, else get inserted IDs, if both case exist get inserted and existing ids
Models and code I tried are given below:
Model Dependency
type Dependency struct {
gorm.Model
ID string `gorm:"primaryKey; not null"`
Name string `gorm:"not null; UniqueIndex:idx_name_version"`
Version string `gorm:"not null; UniqueIndex:idx_name_version"`
}
go code with gorm query to insert dependencies
var saveDependencyData []models.Dependency
// Dependecies are read form api input
// [
// {
// "name": "node",
// "version": "16.0.0"
// },
// {
// "name": "node",
// "version": "18.0.0"
// }
// ]
for _, dep := range Dependecies {
saveDependencyData = append(saveDependencyData, models.Dependency{
ID: nanoid.New(),
Name: dep.Name,
Version: dep.Version,
})
}
res := db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "name"}, {Name: "version"}},
DoUpdates: clause.AssignmentColumns([]string{"name"}),
}).Create(saveDependencyData)
gorm query output
INSERT INTO "dependencies" ("id","created_at","updated_at","deleted_at","name","version") VALUES ('QanL-nfNFrOGdxG2iXdoQ','2022-10-06 19:21:13.079','2022-10-06 19:21:13.079',NULL,'react','16.0.0'),('Yw1YyQ-aBqrQtwZ72GNtB','2022-10-06 19:21:13.079','2022-10-06 19:21:13.079',NULL,'react','18.0.0') ON CONFLICT ("name","version") DO UPDATE SET "name"="excluded"."name" RETURNING "id"
This query returns the list of ids I needed, but could not find a way to retrieve that.
using Scan() gets all the datas in that table.
Either you can help with a way to retrieve the returning IDs form the above GORM db.Clauses(), Or any other optimized method to get those (inserted & existing) ids with a upsert query.

As indicated in the comments: Several functions of GORM expect a pointer as argument and will update the variable with information.
That's obviously the case for all functions whose main purpose is to retrieve information (First, Find, ..., cf. https://gorm.io/docs/query.html).
But it's also the case for functions that modify data like
Create (https://gorm.io/docs/create.html),
Update (https://gorm.io/docs/update.html#Returning-Data-From-Modified-Rows) or
Delete (https://gorm.io/docs/delete.html#Returning-Data-From-Deleted-Rows)
So, the solution in this case is to pass Create(&saveDependencyData) instead of Create(saveDependencyData).
The up-to-date information corresponding to the database will then be available in the saveDependencyData after the call.

Related

GORM unable to update data in one to many relationship

I have two tables users and documents. They are related in such a way that each document must belong to a user using a one to many relationship. When I try updating a document I get the following error
ERROR: insert or update on table "documents" violates foreign key
constraint "fk_users_documents" (SQLSTATE 23503)
Here are my structs definition and update function
type User struct {
gorm.Model
Name string
Email string
Password string
Documents []Document
}
type Document struct {
gorm.Model
Name string
UserID uint
}
//Update document by id
func (h handler)UpdateDocument(w http.ResponseWriter, r *http.Request) {
// once again, we will need to parse the path parameters
var updatedDoc Document
reqBody, _ := ioutil.ReadAll(r.Body)
json.Unmarshal(reqBody, &updatedDoc)
var document Document
vars := mux.Vars(r)
id := vars["id"]
if result := Db.First(&updatedDoc, id); result.Error != nil {
fmt.Println(result.Error)
}
document.Name=updatedDoc.Name
Db.Save(&document)
json.NewEncoder(w).Encode(&updatedDoc)
}
You are calling Db.Save(&document) but document has only its Name field populated. This means that the UserID is set to 0. I'm guessing you don't have any user with ID 0 present in the User table, therefore this violates the foreign key constraint.
The UserID field shall always be set to an existing user when updating a document otherwise the query will fail.
Regardless of this, I'd suggest you to study a bit of database and golang basics because the code you posted is quite messy.

In Firebase, How to update an element in an object array within a document using Dart

Here's my Firebase database
Within the "users" collection, I have a document "xyz#gmai.com. The document has an array "status". I'm trying to update one of the elements in the "status" array.
I've written the following but I would like to understand if I need to write a separate query to delete first or would this actually update the existing record or add a new record in the "status" array with the same id (1)
final conn = FirebaseFirestore.instance.collection("users")
.doc(email);
conn.update({
"status": FieldValue.arrayUnion([{
"date":"10/15/2022",
"id":1
}]),
});
In general, instead of update you can use set with option merge: true to change only the fields you specify and leave the others alone:
conn.set({'field': 'newValue'}, SetOptions(merge: true));
But in your case the code that you have written will always add a new element to the array status with the new data even if the id already exists.
One solution is to go through the existing elements in status array and update if you find the id or add a new element.
The other solution is to create a subcollection status and use set with the id. In this case Firestore will do the job and either create a new document or update the existing:
conn.collection('status').doc(1).set({'date': '10/15/2022'});
In both cases please remember that these operations are async so you might want to use await.
I had to delete and add the new entry. It's very inefficient compared to how we do things in a traditional database i.e SQL UPDATE. Network calls are expensive and should be avoided. I like Peter Koltai's approach but that would force me to refactor my schema which I can't.
Future<void> updateReqStatus(email, id, oldDate, newDate) async {
final conn = FirebaseFirestore.instance.collection("users")
.doc(email);
conn.update({
"status": FieldValue.arrayRemove([{
"date":oldDate,
"id":id
}]),
}).then((value) => print("value"));
conn.update({
"status": FieldValue.arrayUnion([{
"date":newDate,
"id":id
}]),
}).then((value) => print("value"));
}

How can I create a relation in Strapi if I don't know the id of the field?

I am creating a collection of judges and courthouses. Every judge will be assigned to one courthouse. I have set up my relation to be that courthouse has many judges
I am attempting to do this programmatically when the app loads. I have a function that is able to populate all the fields in judge except the relation to courthouse. My function uses the Strapi API like this
const judge = await strapi.query('judge').create({
name: data[i].name,
},
{
courthouse: data[i].courthouse_name // here is where I think the relation is created
}
)
I am passing in a string that has the name of courthouse, because I don't know the ID of the courthouse in the Courthouse collection.
My question is it possible to create a relation to another collection by anything other than an ID? How can I create a relation to a courthouse by its name?
I couldn't find a way around building a relationship between two models without the ID, so I created a custom solution using the Strapi lifecycle hooks
Essentially what I did I utilized the beforeCreate lifecycle hook to query and find the courthouse that matches the name like this:
// judges.js
async beforeCreate(result, data) {
const courthouse = await strapi.query('courthouse').find(
{courthouse_name:data.courthouse}
); // returns the courthouse that matches the name
result['courthouse'] = courthouse[0].id; // populates the relational field with the
// ID of the courthouse
}
The response object contained the courthouse's ID and I manipulated the data that is being sent to the create command like this:
const judge = await strapi.query('judge').create({
name: data[i].name,
courthouse: data[i].courthouse_name
})
The result is an object that looks like this:
{name: 'Garfield Lucas, courthouse: 7463987}

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.?

Add a record to an Algolia Index only if it does not exists

I was wondering how can i POST in a single request (without fetching results for the given attribute) a pretty simple record to an Algolia Index without creating repeated instances.
e.g:
category: {
name: String // This should be unique
}
There isn't such "addObject if not exists" feature based on the record content but if you use the category name as the objectID of your record; the second time you'll add the object, it will just replace the previous instance.
{
objectID: "mycategoryname",
moreattributes: "if needed",
[...]
}
Would that work?