sequelize bulkupdate error on column "0" of relation does not exist - node.js

With the following migration script:
const rules = [
{ type: 'read', target: 'table1', service: 'admin', scope: 'self', field: 'id' },
{ type: 'read', target: 'table2', service: 'admin', scope: 'self', field: 'id' }
]
module.exports = {
up: (queryInterface, Sequelize) => {
rules.map(({ type, target, service }) => { console.log( type, target, service) })
return queryInterface.bulkUpdate(
'rules',
rules,
{[Sequelize.Op.or]: rules.map(({ type, target, service }) => { console.log( type, target, service); return {type, target, service} })},
).then(ids => {
console.log(ids);
})
},
I am seeing the sql being executed:
Executing (default): UPDATE "rules" SET "0"=$1,"1"=$2 WHERE (("type" = $6 AND "target" = $7 AND "service" = $8) OR ("type" = $9 AND "target" = $10 AND "service" = $11) OR ("type" = $12 AND "target" = $13 AND "service" = $14) OR ("type" = $15 AND "target" = $16 AND "service" = $17) OR ("type" = $18 AND "target" = $19 AND "service" = $20))
Which gives this error:
ERROR: column "0" of relation "rules" does not exist
But how come it makes the index instead of the actual column name.

Related

Insert into JSON objects postgres using Node without forloop for every db.query

I have an array of objects like with the same setup as the one below. I wanna insert this array of objects into postgres table that looks like this:[![Table setup][1]][1]
I have tried to make a function below but it returns error when inserting UNGDOMSKOLE because this is a string so it doesn't understand the space so it crashes on the second input value. How can I make it understand it is a string?
{
'#type': 'SensorSystem',
id: 'SN47230',
name: 'ÅKRA UNGDOMSSKOLE',
shortName: 'Åkra ',
country: 'Norge',
countryCode: 'NO',
geometry: {
'#type': 'Point',
coordinates: [ 5.1963, 59.2555 ],
nearest: false
},
masl: 18,
validFrom: '2013-10-29T00:00:00.000Z',
county: 'ROGALAND',
countyId: 11,
municipality: 'KARMØY',
municipalityId: 1149,
stationHolders: [ 'KARMØY KOMMUNE' ],
externalIds: [ '506131077' ],
wigosId: '0-578-0-47230'
}
Error code:
error: syntax error at or near "UNGDOMSSKOLE"
What I have tried so far:
let sqlinsert= data.data.map((source)=>{
if (source.geometry) {
if(!source.masl){
source.masl=0
}
let Point = `POINT(${source.geometry.coordinates[0]} ${source.geometry.coordinates[1]})`;
return `(${source.id}, ${source.name}, ${source.shortName},${source.country},${source.countryCode},${source.masl},${source.geometry.coordinates[0]},${source.geometry.coordinates[1]},${Point},${source.validFrom},${source.county},${source.countyId},${source.municipality},${source.municipalityId})`
}
})
const result = await db.query("INSERT INTO sources(source_id,name,shortName,country,countryCode,masl,long,lat,geog,valid_from,county,countyId,municipality,municipalityId) values"+sqlinsert[0])
A second problem I have with this is that inserting
POINT(59.2555 5.1963)
Gives a syntax error at 5.1963
[1]: https://i.stack.imgur.com/4RSkq.png
The main problem with your query as written is that you are adding raw, unescaped values into your VALUES records. You can use escapeLiteral on your db client to ensure that these values are properly escaped which will solve the syntax errors you are getting:
const data = [
{
"#type": "SensorSystem",
id: "SN47230",
name: "ÅKRA UNGDOMSSKOLE",
shortName: "Åkra ",
country: "Norge",
countryCode: "NO",
geometry: {
"#type": "Point",
coordinates: [5.1963, 59.2555],
nearest: false,
},
masl: 18,
validFrom: "2013-10-29T00:00:00.000Z",
county: "ROGALAND",
countyId: 11,
municipality: "KARMØY",
municipalityId: 1149,
stationHolders: ["KARMØY KOMMUNE"],
externalIds: ["506131077"],
wigosId: "0-578-0-47230",
},
].map((source) => {
const {
id,
name,
shortName,
country,
countryCode,
masl,
geometry: {
// the coordinates in your source data appear to be in y,x instead of
// x,y. Treating them as x,y results in the point being located
// in the Indian Ocean while y,x is somewhere in Norway.
coordinates: [lat, long],
},
validFrom,
county,
countyId,
municipality,
municipalityId,
} = source;
return [
id,
name,
shortName,
country,
countryCode,
masl || 0,
long,
lat,
`POINT( ${long} ${lat} )`,
validFrom,
county,
countyId,
municipality,
municipalityId,
];
});
const headers = [
"source_id",
"name",
"shortname",
"country",
"countrycode",
"masl",
"long",
"lat",
"geog",
"valid_from",
"county",
"countyid",
"municipality",
"municipalityid",
];
const sourceValStr = data
.map((sourceRecords, rowIndex) => {
return sourceRecords
.map((value, colIndex) => {
if (typeof value === "string") {
// safely escape string values
return dbClient.escapeLiteral(value);
}
if (
typeof value === "number" ||
typeof value === "boolean" ||
typeof value === "bigint"
) {
return value;
}
if (value === undefined || value === null) {
return "null";
}
throw new Error(
`non-simple value: ${JSON.stringify(value)} for ${
headers[colIndex]
} at row ${rowIndex}`
);
})
.join(",");
})
.map((value) => `(${value})`)
.join(",");
const sourceInsert = `INSERT INTO sources(${headers.join(
","
)}) VALUES ${sourceValStr};`;
await dbClient.query(sourceInsert);
A much more efficient and scalable way to insert the rows is to use the pg-copy-streams library in conjunction with a CSV library like csv-stringify which will bulk insert using a COPY FROM stream:
import { from as copyFrom } from "pg-copy-streams";
import { stringify } from "csv-stringify";
// ...
const copyStmt = `COPY sources(${headers.join(
","
)}) FROM STDIN (FORMAT CSV)`;
await new Promise<void>((resolve, reject) => {
const copyStream = dbClient.query(copyFrom(copyStmt));
const stringifiedStream = stringify(data, {
header: false,
encoding: "utf-8",
delimiter: ",",
quote: "\"",
});
stringifiedStream
.on("error", (err) => {
reject(err);
})
.on("end", () => resolve());
stringifiedStream.pipe(copyStream);
});
On my low-end laptop, this approach takes about 39 seconds to insert a million rows with no database optimizations.

How to query type enum in nestjs

i try to run query graphql:
projects(input:{
nameTerm:""
projectTypeTerm:""
statusTerm:"Initiation Phase"
})
{
id
name
status
}
}
and after that it return message operator does not exist: project_status_enum ~~ unknown"
file resolver:
#Query(() => [Project], { name: 'projects' })
getProjects(#Args('input') input: GetProjectsInput, ) {
const query = this.mapper.map(input, GetProjectsQuery, GetProjectsInput);
return this.queryBus.execute(query);
}
file enum:
export enum statusProject {
Initiation = 'Initiation Phase',
Planning = 'Planning Phase',
Execution = 'Execution Phase',
ProjectClosure = 'Project Closure',
}
file handle query:
async execute(query: GetProjectsQuery): Promise<Project[]> {
return await this.ProjectRepository.find({
where: {
name: Like(`%${query.nameTerm}%`),
status: Like(`%${query.statusTerm}%`),
projectType: Like(`%${query.projectTypeTerm}%`)
},
});
}
I try to add {enum:statusProject} in #Query but not work
and hope result is:
"projects": \[
{
"id": "77b7134f-270a-4a41-a85a-377311fdbb91",
"name": "Time ABCDFG",
"status": "Initiation Phase"
},\]
I have fixed the problem by changing status: Like(`%${query.statusTerm}%`)
to status: {query.statusTerm}
Like cannot be used with enum.

Codeigniter 4 registration to multiple table

I have some questions that I do not know how to solve it. Currently I have 2 tables, users and user_profile. I have 2 fields in users_profile, membership and coins which will be added by a default value "yes/no" for membership and "0" for coins.
How do I add "created_by" column from the form username?
How do I add the value in the user_profile table when the user submit the registration form?
This is my code that will store the registration form fields in users table
public function register()
{
$data = [];
helper(['form']);
$validation = \Config\Services::validation();
// To add in is_unique
if($this->request->getMethod() == 'post'){
//validations
$rules = [
'username' => 'required|is_unique[users.username]',
'email' => 'required|valid_email|is_unique[users.email]',
'firstname' => 'required',
'lastname' => 'required',
'dob' => 'required',
'country' => 'required',
'contact' => 'required',
'password' => 'required'
];
$errors = [
'username' => [
'is_unique' => 'Username already exist!'
],
'email' => [
'is_unique' => 'Email already exist!'
]
];
if(!$this->validate($rules, $errors)){
$data['validation'] = $this->validator;
}else{
//store information into database
$model = new AccountModel();
$newData = [
'username' => $this->request->getVar('username'),
'email' => $this->request->getVar('email'),
'firstname' => $this->request->getVar('firstname'),
'lastname' => $this->request->getVar('lastname'),
'dob' => $this->request->getVar('dob'),
'country' => $this->request->getVar('country'),
'contact' => $this->request->getVar('contact'),
'password' => $this->request->getVar('password'),
'created_by' => $this->request->getVar('username')
];
$model->save($newData);
$user_id = $model->insertID();
$newAccount = $model->where('user_id',$user_id)->first();
$userProfileModel = new UserProfileModel();
$newProfile = $userProfileModel->save(['user_id' => $user_id, 'coins' => '0', 'membership' => 'no']);
}
}
echo view('templates/header', $data);
echo view('account/register');
echo view('templates/footer');
}
AccountModel
class AccountModel extends Model{
protected $table = 'users';
protected $allowedFields = [
'username',
'email',
'firstname',
'lastname',
'dob',
'country',
'contact',
'password',
'created_at',
'updated_at',
'created_by'
];
protected $beforeInsert = ['beforeInsert'];
protected $beforeUpdate = ['beforeUpdate'];
protected function beforeInsert(array $data) {
$data = $this->passwordHash($data);
return $data;
}
protected function beforeUpdate(array $data) {
$data = $this->passwordHash($data);
return $data;
}
protected function passwordHash(array $data){
if(isset($data['data']['password']))
$data['data']['password'] = password_hash($data['data']['password'], PASSWORD_DEFAULT);
return $data;
}
}
UserProfileModel
<?php namespace App\Models;
use CodeIgniter\Model;
class UserProfileModel extends Model{
protected $table = 'user_profile';
protected $allowedFields = [
'user_id',
'coins',
'membership'
];
protected $beforeInsert = ['beforeInsert'];
protected $beforeUpdate = ['beforeUpdate'];
protected function beforeInsert(array $data) {
}
protected function beforeUpdate(array $data) {
}
}
?>
How do I add "created_by" column from the form username?
use insertID() ci4 models method aftaer save method executed. It will return the lastest primary key which been saved from the table you want. in this case from your account table
$model->save($newData);
$userId= $model->insertID();
How do I add the value in the user_profile table when the user submit the registration form?
you should have a foreign key in user_profile table that refering to users table primary key
++++++++++++ ++++++++++++++++
+ users + + user_profile +
++++++++++++ ++++++++++++++++
+-id[p.k] + +-id[p.k] +
+-username + +-users_id[f.k]+
+-email + +-coins +
+-email + +-membership +
get lastest user data by $userId, i assume your primary key is 'id' and than save to the user_profile
$newAccount = $model->where('id',$user_id')->first();
$userProfileModel = new UserProfileModel();
$newProfile = $userProfileModel->save(['user_id' => $userId])

Convert a ColumnSet column into geometry with pg-promise

I'm creating a ColumnSet object with pg-promise, according to this:
const cs = new pgp.helpers.ColumnSet([
{name: 'Id',prop: 'Id'},
{name: 'Lat',prop: 'Lat'},
{name: 'Lng',prop: 'Lng'},
{name: 'CreationDateTime',prop: 'CreationDateTime'},
{name: 'Topic',prop: 'Topic'},
{name: 'UserId',prop: 'UserId'},
{name: 'shape',mod: ':raw',prop: 'shape',def: 'point'},
{name: 'UserName',prop: 'UserName'},
{name: 'appName',prop: 'appName'},
{name: 'appVersion',prop: 'appVersion'}
], {
table: 'Location'
});
def: 'point' point is method to converting into geometry-- This is a value or how can i run point method and do bind in this column (shape) ?
and write this method for bulk inserting :
async function insertMany(values) {
try {
let results = await db.none(pgp.helpers.insert(values, cs));
} catch (error) {
console.log(error);
}
}
for converting lat and lng i wrote this method :
const point = (lat, lng) => ({
toPostgres: () => pgp.as.format('ST_SetSRID(ST_MakePoint($1, $2), 4326)', [Lag, Lng]),
rawType: true
});
But I got this error:
TypeError: Values null/undefined cannot be used as raw text
According this page:
Raw-text variables end with :raw or symbol ^, and prevent escaping the text. Such variables are not allowed to be null or undefined, or the method will throw TypeError = Values null/undefined cannot be used as raw text.
When point method is not executed, of course that shape filed is null.
First, you are misusing option prop, which is documented as to be used when the destination property name differs from the column name, which is not your case.
And def, as documented also, represents the value when the property is missing. When the property is there set to null or undefined, the value of def isn't used.
You are trying to override the resulting value, that means you need to use property init.
Another issue - your variables inside point implementation switch cases.
In all, your code should look something like this:
const getPoint = col => {
const p = col.value;
// we assume that when not null, the property is an object of {lat, lng},
// otherwise we will insert NULL.
return p ? pgp.as.format('ST_SetSRID(ST_MakePoint(${lat}, ${lng}), 4326)', p) : 'NULL';
};
const cs = new pgp.helpers.ColumnSet([
'Id',
'Lat',
'Lng',
'CreationDateTime',
'Topic',
'UserId',
{name: 'shape', mod: ':raw', init: getPoint},
'UserName',
'appName',
'appVersion',
], {
table: 'Location'
});
And version that uses Custom Type Formatting would look like this:
const getPoint = col => {
const p = col.value;
if(p) {
return {
toPostgres: () => pgp.as.format('ST_SetSRID(ST_MakePoint(${lat}, ${lng}), 4326)', p),
rawType: true
};
}
// otherwise, we return nothing, which will result into NULL automatically
};
const cs = new pgp.helpers.ColumnSet([
'Id',
'Lat',
'Lng',
'CreationDateTime',
'Topic',
'UserId',
{name: 'shape', init: getPoint},
'UserName',
'appName',
'appVersion',
], {
table: 'Location'
});

Multiple insertion with addition data with pg-promise

I have a large dataset that I want to insert into a postgres db, I can achieve this using pg-promise like this
function batchUpload (req, res, next) {
var data = req.body.data;
var cs = pgp.helpers.ColumnSet(['firstname', 'lastname', 'email'], { table: 'customer' });
var query = pgp.helpers.insert(data, cs);
db.none(query)
.then(data => {
// success;
})
.catch(error => {
// error;
return next(error);
});
}
The dataset is an array of objects like this:
[
{
firstname : 'Lola',
lastname : 'Solo',
email: 'mail#solo.com',
},
{
firstname : 'hello',
lastname : 'world',
email: 'mail#example.com',
},
{
firstname : 'mami',
lastname : 'water',
email: 'mami#example.com',
}
]
The challenge is I have a column added_at which isn't included in the dataset and cannot be null. How do I add a timestamp for each record insertion to the query.
As per the ColumnConfig syntax:
const col = {
name: 'added_at',
def: () => new Date() // default to the current Date/Time
};
const cs = pgp.helpers.ColumnSet(['firstname', 'lastname', 'email', col], { table: 'customer' });
Alternatively, you can define it in a number of other ways, as ColumnConfig is very flexible.
Example:
const col = {
name: 'added_at',
mod: ':raw', // use raw-text modifier, to inject the string directly
def: 'now()' // use now() for the column
};
or you can use property init to set the value dynamically:
const col = {
name: 'added_at',
mod: ':raw', // use raw-text modifier, to inject the string directly
init: () => {
return 'now()';
}
};
See the ColumnConfig syntax for details.
P.S. I'm the author of pg-promise.

Resources