How to process the return from sqlite Select statement in ionic 2? - android-sqlite

I'm working on a Ionic 2 (2.0.0-rc.2) app with a sqlite storage and i've ran into some problems, hence i'm seeking for help here.
When starting the app for the first time i'm creating the database(tables) insert a few values. So far everything goes alright. However when I try to retrieve data and read it from the database i'm getting an error.
The class were i'm talking about:
import {Component} from '#angular/core';
import {Platform} from 'ionic-angular';
import {StatusBar, Splashscreen, SQLite} from 'ionic-native';
import {HomePage} from '../pages/home/home';
#Component({
template: `<ion-nav [root]="rootPage"></ion-nav>`
})
export class MyApp {
users = [];
rootPage = HomePage;
constructor(platform: Platform) {
platform.ready().then(() => {
StatusBar.styleDefault();
let db = new SQLite();
console.log("open database app.compontent")
db.openDatabase({
name: "data.db",
location: "default"
}).then(() => {
db.executeSql("CREATE TABLE IF NOT EXISTS user (iduser INTEGER PRIMARY KEY AUTOINCREMENT, firstname TEXT, lastname TEXT, castleType TEXT, castleHealth INTEGER); ", {}).then((data) => {
console.log("USER TABLE CREATED: ", data);
}, (error) => {
console.error("USER Unable to execute sql", error);
});
db.executeSql("CREATE TABLE IF NOT EXISTS melding (idmelding INTEGER PRIMARY KEY AUTOINCREMENT, meldingType TEXT, meldingTitel TEXT, meldingOmschrijving TEXT, ruimte INTEGER, user_iduser INTEGER, FOREIGN KEY (user_iduser) REFERENCES user(iduser)); ", {}).then((data) => {
console.log("MELDING TABLE CREATED: ", data);
}, (error) => {
console.error("MELDING Unable to execute sql", error);
});
db.executeSql("CREATE TABLE IF NOT EXISTS kasteelOnderdelen (idkasteelOnderdelen INTEGER PRIMARY KEY AUTOINCREMENT, onderdeelNaam TEXT, onderdeelShort TEXT, onderdeelHealth TEXT); ", {}).then((data) => {
console.log("KASTEELONDERDELEN TABLE CREATED: ", data);
}, (error) => {
console.error("KASTEELONDERDELEN Unable to execute sql", error);
});
db.executeSql("CREATE TABLE IF NOT EXISTS user_has_kasteelOnderdelen (id INTEGER PRIMARY KEY AUTOINCREMENT, user_iduser INTEGER, kasteelOnderdelen_idkasteelOnderdelen INTEGER, FOREIGN KEY (user_iduser) REFERENCES user(iduser), FOREIGN KEY (kasteelOnderdelen_idkasteelOnderdelen) REFERENCES kasteelOnderdelen(idkasteelOnderdelen));", {}).then((data) => {
console.log("USER_HAS_KASTEELONDERDELEN TABLES CREATED: ", data);
}, (error) => {
console.error("USER_HAS_KASTEELONDERDELEN Unable to execute sql", error);
});
console.log('Creating database complete!');
db.executeSql("INSERT INTO user (firstname, lastname, castletype, castleHealth) VALUES (?, ?, ?, ?)", ['Jan', 'Klaassen', 'mannelijk', '10']).then((data) => {
console.log("Inserted row 1: ", data);
}, (error) => {
console.error("Unable to insert row 1: ", error);
});
db.executeSql("INSERT INTO user (firstname, lastname, castletype, castleHealth) VALUES (?, ?, ?, ?)", ['Klaas', 'Janssen', 'mannelijk', '5']).then((data) => {
console.log("Inserted row 2: ", data);
}, (error) => {
console.error("Unable to insert row 2: ", error);
});
db.executeSql('SELECT * FROM user', []).then((resultSet) => {
if(resultSet.res.rows.length > 0) {
this.users = [];
for(let i = 0; i < resultSet.res.rows.length; i++) {
this.users.push({
"iduser": resultSet.res.rows.item(i).iduser,
"firstname": resultSet.res.rows.item(i).firstname,
"lastname": resultSet.res.rows.item(i).lastname,
});
}
}
}, (error) => {
console.log(error);
});
}, (error) => {
console.error("Unable to open database", error);
});
Splashscreen.hide();
});
}
}
error (when I run it on a device (Nexus 5):
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 12 : Error: Uncaught (in promise): TypeError: Cannot read property 'rows' of undefined
at s (file:///android_asset/www/build/polyfills.js:3:8568)
at file:///android_asset/www/build/polyfills.js:3:8940
at t.invokeTask (file:///android_asset/www/build/polyfills.js:3:14051)
at Object.inner.inner.fork.onInvokeTask (file:///android_asset/www/build/main.js:3:22982)
at t.invokeTask (file:///android_asset/www/build/polyfills.js:3:13987)
at e.runTask (file:///android_asset/www/build/polyfills.js:3:11411)
at i (file:///android_asset/www/build/polyfills.js:3:8028)
at u (file:///android_asset/www/build/polyfills.js:2:16275)
at file:///android_asset/www/build/polyfills.js:2:16399
at MutationObserver.r (file:///android_asset/www/build/polyfills.js:2:14841)
Additional findings/notes:
If i write console.log(resultSet);
The return will be [object object]
For now it's all in the same class and on the longrun it will be in it's own StorageProvider/Service but i've moved it all here to debug it. (without success so far)
Does someone have a clue what causes the error?
Thanks in advance.
Poerkie
UPDATE 20-12 (after implementing tips and suggestions):
current code:
import {Injectable} from '#angular/core';
import 'rxjs/add/operator/map';
import {SQLite} from 'ionic-native';
import {Platform} from 'ionic-angular';
#Injectable()
export class StorageService {
public database: SQLite;
public users;
constructor(private platform: Platform) {
console.log(' StorageService Initialised');
this.platform.ready().then(() => {
this.database = new SQLite();
this.database.openDatabase({name: "data.db", location: "default"}).then(() => {
console.log("SUCCES Opening DB storageService");
let s1 = this.createUserTable()
let s2 = this.database.executeSql("CREATE TABLE IF NOT EXISTS melding (idmelding INTEGER PRIMARY KEY AUTOINCREMENT, meldingType TEXT, meldingTitel TEXT, meldingOmschrijving TEXT, ruimte INTEGER, user_iduser INTEGER, FOREIGN KEY (user_iduser) REFERENCES user(iduser)); ", {});
return Promise.all([s1, s2]).then(() => {
console.log('creating database complete!');
let i1 = this.insertUser();
let i2 = this.database.executeSql("INSERT INTO user (firstname, lastname, castletype, castleHealth) VALUES (?, ?, ?, ?)", ['Klaas', 'Janssen', 'mannelijk', '5']);
return Promise.all([i1, i2]).then((res) => {
console.log('all insert querys executed', res);
}).then(() => {
console.log('executing sql statement');
return this.database.executeSql('SELECT * FROM user', []);
}).then((resultSet) => {
console.log('after then((resultset): ' + resultSet);
console.log(JSON.stringify(resultSet, undefined, 2));
if (resultSet.res.rows.length > 0) {
console.log('if statement true');
this.users = [];
for (let i = 0; i < resultSet.res.rows.length; i++) {
console.log('forloop');
this.users.push({
"iduser": resultSet.res.rows.item(i).iduser,
"firstname": resultSet.res.rows.item(i).firstname,
"lastname": resultSet.res.rows.item(i).lastname,
});
}
console.log('after forloop');
}
}).catch((error) => {
console.error("db error", error);
})
})
}, (error) => {
console.log("ERROR oepening DB storageService: ", error);
});
});
}
public createUserTable() {
this.database.executeSql("CREATE TABLE IF NOT EXISTS user (iduser INTEGER PRIMARY KEY AUTOINCREMENT, firstname TEXT, lastname TEXT, castleType TEXT, castleHealth INTEGER); ", {}).then((data) => {
console.log("USER TABLE CREATED: ", data);
}, (error) => {
console.error("USER Unable to execute sql", error);
});
}
public insertUser() {
this.database.executeSql("INSERT INTO user (firstname, lastname, castletype, castleHealth) VALUES (?, ?, ?, ?)", ['Jan', 'Klaassen', 'mannelijk', '10']).then((data) => {
console.log('Insert Succesful');
}, (error) => {
console.error('user not inserted: ' + error);
});
}
}
nexus 5 outputlog:
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 6 : StorageService Initialised
I/chromium: [INFO:CONSOLE(6)] " StorageService Initialised", source: file:///android_asset/www/build/main.js (6)
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 6 : CastleService initialized
I/chromium: [INFO:CONSOLE(6)] "CastleService initialized", source: file:///android_asset/www/build/main.js (6)
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 5 : SlimLabsService initialized
I/chromium: [INFO:CONSOLE(5)] "SlimLabsService initialized", source: file:///android_asset/www/build/main.js (5)
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 5 : Logging in!
I/chromium: [INFO:CONSOLE(5)] "Logging in!", source: file:///android_asset/www/build/main.js (5)
12-20 07:48:04.042 796-18483/? W/InputMethodManagerService: Window already focused, ignoring focus gain of: com.android.internal.view.IInputMethodClient$Stub$Proxy#fd827af attribute=null, token = android.os.BinderProxy#7d616de
D/CordovaWebViewImpl: onPageFinished(file:///android_asset/www/index.html)
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 19 : DEVICE READY FIRED AFTER
I/chromium: [INFO:CONSOLE(19)] "DEVICE READY FIRED AFTER", source: file:///android_asset/www/build/main.js (19)
I/App: WARNING: Back Button Default Behavior will be overridden. The backbutton event will be fired!
D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-sqlite-storage/www/SQLitePlugin.js: Line 175 : OPEN database: data.db
I/chromium: [INFO:CONSOLE(175)] "OPEN database: data.db", source: file:///android_asset/www/plugins/cordova-sqlite-storage/www/SQLitePlugin.js (175)
V/SQLitePlugin: Android db implementation: built-in android.database.sqlite package
V/info: Open sqlite db: /data/user/0/com.ionicframework.cwistlev1105374/databases/data.db
D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-sqlite-storage/www/SQLitePlugin.js: Line 179 : OPEN database: data.db - OK
I/chromium: [INFO:CONSOLE(179)] "OPEN database: data.db - OK", source: file:///android_asset/www/plugins/cordova-sqlite-storage/www/SQLitePlugin.js (179)
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 6 : SUCCES Opening DB storageService
I/chromium: [INFO:CONSOLE(6)] "SUCCES Opening DB storageService", source: file:///android_asset/www/build/main.js (6)
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 6 : USER TABLE CREATED:
I/chromium: [INFO:CONSOLE(6)] "USER TABLE CREATED: ", source: file:///android_asset/www/build/main.js (6)
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 6 : creating database complete!
I/chromium: [INFO:CONSOLE(6)] "creating database complete!", source: file:///android_asset/www/build/main.js (6)
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 6 : Insert Succesful
I/chromium: [INFO:CONSOLE(6)] "Insert Succesful", source: file:///android_asset/www/build/main.js (6)
12-20 07:48:04.629 1596-1596/? I/Keyboard.Facilitator: onFinishInput()
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 6 : all insert querys executed
I/chromium: [INFO:CONSOLE(6)] "all insert querys executed", source: file:///android_asset/www/build/main.js (6)
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 6 : executing sql statement
I/chromium: [INFO:CONSOLE(6)] "executing sql statement", source: file:///android_asset/www/build/main.js (6)
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 6 : after then((resultset): [object Object]
I/chromium: [INFO:CONSOLE(6)] "after then((resultset): [object Object]", source: file:///android_asset/www/build/main.js (6)
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 6 : {
"rows": {
"length": 42
},
"rowsAffected": 0
}
I/chromium: [INFO:CONSOLE(6)] "{
"rows": {
"length": 42
},
"rowsAffected": 0
}", source: file:///android_asset/www/build/main.js (6)
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 6 : db error
I/chromium: [INFO:CONSOLE(6)] "db error", source: file:///android_asset/www/build/main.js (6)
additional information:
There isn't more information about the error or I don't know how to retrieve that. (not getting a stack trace or anything);
Why doesn't it want to pass on the results! into the users[]?

I agree with Suraj Rao about the async result. I would add that you could do all the create tables in a Promise.all(). And when in between 'then' statement, place a 'return' on the query which result is expected in the next "then". Eg:
query.then( (res) => { return query2 }
).then( (res2) => { do something }
).catch( (err) => {...});
In your case:
let db = new SQLite();
console.log("open database app.compontent")
db.openDatabase({
name: "data.db",
location: "default"
}).then(() => {
let s1 = db.executeSql("CREATE TABLE IF NOT EXISTS user (iduser INTEGER PRIMARY KEY AUTOINCREMENT, firstname TEXT, lastname TEXT, castleType TEXT, castleHealth INTEGER); ", {});
let s2 = db.executeSql("CREATE TABLE IF NOT EXISTS melding (idmelding INTEGER PRIMARY KEY AUTOINCREMENT, meldingType TEXT, meldingTitel TEXT, meldingOmschrijving TEXT, ruimte INTEGER, user_iduser INTEGER, FOREIGN KEY (user_iduser) REFERENCES user(iduser)); ", {})
let sN = db.executeSql("CREATE TABLE ....);
return Promise.all([s1,s2,....,sN]);
).then(
() => {
console.log('Creating database complete!');
let i1 = db.executeSql("INSERT INTO user (firstname, lastname, castletype, castleHealth) VALUES (?, ?, ?, ?)", ['Jan', 'Klaassen', 'mannelijk', '10']);
let i2 = db.executeSql("INSERT INTO user (firstname, lastname, castletype, castleHealth) VALUES (?, ?, ?, ?)", ['Klaas', 'Janssen', 'mannelijk', '5']);
let iN = db.executeSql("INSERT INTO ....");
return Promise.all([i1,i2,....,iN]);
).then(
(res) => {
console.log('all insert over ' , res)
}
).then(
() => {return db.executeSql('SELECT * FROM user', []);}
).then(
(resultSet) => {
if(resultSet.res.rows.length > 0) {
this.users = [];
for(let i = 0; i < resultSet.res.rows.length; i++) {
this.users.push({
"iduser": resultSet.res.rows.item(i).iduser,
"firstname": resultSet.res.rows.item(i).firstname,
"lastname": resultSet.res.rows.item(i).lastname,
});
}
}
}
).catch(
(error) => {
console.error("db error", error);
}
);

The db related functions you are using return a promise.
The then() is asynchronous meaning all your db functions from create,insert,select may execute before one or more of the then method is called.
Suppose you have only user table,You will need to
db.executeSql(`
CREATE TABLE IF NOT EXISTS user (
iduser INTEGER PRIMARY KEY AUTOINCREMENT,
firstname TEXT,
lastname TEXT,
castleType TEXT,
castleHealth INTEGER
); `, {})
.then((data) => {
console.log("USER TABLE CREATED: ", data);
db.executeSql(`
INSERT INTO user (
firstname,
lastname,
castletype,
castleHealth
) VALUES (?, ?, ?, ?)`,
['Jan', 'Klaassen', 'mannelijk', '10'])
.then((data) => {
//select here
console.log("Inserted row 1: ", data);
}, (error) => {
console.error("Unable to insert row 1: ", error);
});
}, (error) => {
console.error("USER Unable to execute sql", error);
});
and so on.
Your resultset.res is probably null because your table is not created yet.
Also to print objects do : console.log(JSON.stringify(resultSet,undefined,2));

Hope it will work for you, refer the below simple implementation.
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import { SQLite, SQLiteObject } from '#ionic-native/sqlite';
#Component({
selector: 'page-about',
templateUrl: 'about.html',
providers:[SQLite]
})
export class AboutPage {
rows: any;
data: any;
public people: Array<Object>;
getdata: string;
constructor(public navCtrl: NavController, private sqlite: SQLite) {
this.sqlite.create({
name: 'data.db',
location: 'default'
}).then((db: SQLiteObject) => {
db.executeSql('CREATE TABLE IF NOT EXISTS supriya(name VARCHAR(32))', {})
.then(() => console.log('Executed SQL'))
.catch(e => console.log(e));
db.executeSql('INSERT INTO supriya VALUES (?)', ['Supriya']).then(() => console.log('value inserted in SQL'))
.catch(e => console.log(e));
db.executeSql('select * from supriya', []).then(data => {
console.log("select statment executed"+ data);
this.people = [];
if(data.rows.length > 0) {
for(var i = 0; i < data.rows.length; i++) {
this.people.push({name: data.rows.item(i).name});
console.log(data.rows.item(i).name);
}
}
}, (error) => {
console.log("ERROR: " + JSON.stringify(error));
})
})
}
}
<ion-list>
<ion-item *ngFor="let person of people">
{{person.name}}
</ion-item>
</ion-list>

Related

Insert with relationships in AdonisJS with transaction

I'm new to this JS Word, I read the Adonis and Knex doc, but I really don't understand what I'm doing wrong here.
Adonis: https://preview.adonisjs.com/guides/database/transactions and https://adonisjs.com/docs/4.1/lucid#_transactions
knex: http://knexjs.org/#Transactions
In the case when nothing goes wrong all the data are stored with no problem, but when I do a error on propose just to test the transaction I can notice that the "empresas" table had a record and the other tables are empty, I supose that when occurs an error all the tables need to be empty, by the transaction's rollback() function. Can someone enlighten me here?
Using: Adonis with postgres
this are my migrations:
table empresas:
'use strict'
/** #type {import('#adonisjs/lucid/src/Schema')} */
const Schema = use('Schema')
class EmpresaSchema extends Schema {
up () {
this.create('empresas', (table) => {
table.increments()
table.text('codigo').notNullable()
table.text('tipo').notNullable()
table.text('origem').notNullable()
table.bigInteger('grupo_id').notNullable()
table.text('nome_fantasia')
table.text('razao_social')
table.timestamps()
})
}
down () {
this.drop('empresas')
}
}
module.exports = EmpresaSchema
table contatos:
'use strict'
/** #type {import('#adonisjs/lucid/src/Schema')} */
const Schema = use('Schema')
class ContatosSchema extends Schema {
up () {
this.create('contatos', (table) => {
table.increments()
table
.integer('tipo_id')
.unsigned()
.notNullable()
.references('id')
.inTable('contato_tipos')
table.text('nome').notNullable()
table.text('dado').notNullable()
table.timestamps()
})
}
down () {
this.drop('contatos')
}
}
module.exports = ContatosSchema
table empresa_contatos:
'use strict'
/** #type {import('#adonisjs/lucid/src/Schema')} */
const Schema = use('Schema')
class EmpresaContatosSchema extends Schema {
up () {
this.create('empresa_contatos', (table) => {
table.increments()
table
.integer('empresa_id')
.unsigned()
.references('id')
.inTable('empresas')
.onUpdate('CASCADE')
.onDelete('CASCADE')
.index()
table
.integer('contato_id')
.unsigned()
.references('id')
.inTable('contatos')
.onUpdate('CASCADE')
.onDelete('CASCADE')
.index()
})
}
down () {
this.drop('empresa_contatos')
}
}
module.exports = EmpresaContatosSchema
My models:
Empresa model:
'use strict'
/** #type {typeof import('#adonisjs/lucid/src/Lucid/Model')} */
const Model = use('Model')
class Empresa extends Model {
contatos () {
return this.belongsToMany('App/Models/Contato').pivotTable('empresa_contatos')
}
}
module.exports = Empresa
Contato model:
'use strict'
/** #type {typeof import('#adonisjs/lucid/src/Lucid/Model')} */
const Model = use('Model')
class Contato extends Model {
static get table () {
return 'contatos'
}
empresa () {
return this.belongsToMany('App/Models/Empresa').pivotTable('empresa_contatos')
}
}
module.exports = Contato
And this is a part of my EmpresaController:
'use strict'
const Database = use('Database')
const Empresa = use('App/Models/Empresa')
const Contato = use('App/Models/Contato')
const Sequence = use('App/Controllers/Http/SequenceController')
class EmpresaController {
async customCreate ({ request, response, auth }) {
const trx = await Database.beginTransaction()
try {
const { enderecos, contatos, ...data } = request.all()
if (data.endereco.logradouro !== '') {
enderecos.push(data.endereco)
}
if (data.contato.nome !== '') {
contatos.push(data.contato)
}
const codigo = await Sequence.gerarNovoCodigoCliente(auth.user.grupo_id, trx)
let empresa = null
await Empresa.create({
codigo: codigo,
grupo_id: 1,
tipo: data.tipo,
origem: data.origem,
nome_fantasia: data.nome_fantasia,
razao_social: data.razao_social
}, trx)
.then(response => {
empresa = response
})
.catch(error => {
console.log('error empresa:')
console.log(error)
})
contatos.forEach(async contato => {
let novosContato = null
await Contato.create({
nome: contato.nome,
dado: contato.dado,
tipo_id: contato.tipo_contato.id
}, trx)
.then(response => {
novosContato = response
console.log('contato ok')
})
.catch(error => {
console.log('error contato:')
console.log(error)
})
await empresa.contatos().attach(novosContato.id, null, trx)
.then(response => {
console.log('attach ok')
})
.catch(error => {
console.log('error attach:')
console.log(error)
})
})
await trx.commit()
return response.ok(empresa)
} catch (error) {
await trx.rollback()
return response.badRequest(error.message)
}
}
}
module.exports = EmpresaController
I already tried put each async function on a const and resolve all with promise.all approach, and get the same problem, I really don't know but I guess that "const empresa = await Empresa.create(.......)" is commiting the transaction and running the everything else after it.
(edit)log:
error contato:
Error: Transaction query already complete, run with DEBUG=knex:tx for more info
at completedError (/app/node_modules/knex/lib/transaction.js:261:9)
at /app/node_modules/knex/lib/transaction.js:231:22
From previous event:
at Client_PG.trxClient.query (/app/node_modules/knex/lib/transaction.js:229:33)
at Runner.<anonymous> (/app/node_modules/knex/lib/runner.js:138:36)
From previous event:
at /app/node_modules/knex/lib/runner.js:47:21
at processImmediate (internal/timers.js:461:21)
From previous event:
at Runner.run (/app/node_modules/knex/lib/runner.js:33:30)
at Builder.Target.then (/app/node_modules/knex/lib/interface.js:23:43)
warning:
WARNING: Adonis has detected an unhandled promise rejection, which may
cause undesired behavior in production.
To stop this warning, use catch() on promises or wrap await
calls inside try/catch.
TypeError: Cannot read property 'id' of null
at /app/app/Controllers/Http/EmpresaController.js:165:54
You need to pass transaction reference to create also...
Just add trx here:
const empresa = await Empresa.create({
codigo: codigo,
grupo_id: 1,
tipo: data.tipo,
origem: data.origem,
nome_fantasia: data.nome_fantasia,
razao_social: data.razao_social
}, trx) <-- adding trx to create!
More info here: https://adonisjs.com/docs/4.1/lucid#_transactions

Error: TypeError: user.insertOne is not a function using mongoose

I'm having difficulty creating the routes to send to MongoDB.
When I return user, it returns the full database. This goes for using User or 'user'.
User is a model
let User = require('../models/user.model');
User.findById(req.params.id)
.then(user => {
if (!user)
res.status(404).send("data is not found");
else
for(var key in req.body.proposal) {
//res.send(user.proposal)
//res.send(user)
//res.send(User.username)
user.proposal.insertOne(
{
"uid" : req.body.proposal[key].uid,
"clientEmail" : req.body.proposal[key].clientEmail,
"summary" :req.body.proposal[key].summary,
"terms" :req.body.proposal[key].terms,
"form" :req.body.proposal[key].form
} //update
)
}
user.save()
.then(user => res.json(user))
.catch(err => res.status(400).json('Error: ' + err));
})
.catch(err => res.status(400).json('Error: ' + err));
});
Thank you in advanced!
It should be something like this :
let proposalArr = [];
for (const key in req.body.proposal) {
proposalArr.push({
uid: req.body.proposal[key].uid,
clientEmail: req.body.proposal[key].clientEmail,
summary: req.body.proposal[key].summary,
terms: req.body.proposal[key].terms,
form: req.body.proposal[key].form
});
}
user.proposal = proposalArr;
user.save().............
You can't use .insertOne on result of database query, it's a function of mongoose model to insert new document to collection but not to insert new fields to objects. You need to do just like adding new fields to json object using .js code, but mongoose will keep track of object's changes & when you use .save() it can update the document in collection with all those changes.
Instead of two DB calls, you can do that in one call, Check : .findByIdAndUpdate() & try below sample code :
let proposalArr = [];
for (const key in req.body.proposal) {
proposalArr.push({
uid: req.body.proposal[key].uid,
clientEmail: req.body.proposal[key].clientEmail,
summary: req.body.proposal[key].summary,
terms: req.body.proposal[key].terms,
form: req.body.proposal[key].form
});
}
User.findByIdAndUpdate(
req.params.id,
{
proposal: proposalArr
},
{ new: true }
)
.then(user => {
if (!user) res.status(404).send("data is not found");
res.json(user);
})
.catch(err => res.status(400).json("Error: " + err));

How to search on a single OR multiple columns with TSVECTOR and TSQUERY

I used some boilerplate code (below) that creates a normalized tsvector _search column of all columns I specify (in searchObjects) that I'd like full-text search on.
For the most part, this is fine. I'm using this in conjunction with Sequelize, so my query looks like:
const articles = await Article.findAndCountAll({
where: {
[Sequelize.Op.and]: Sequelize.fn(
'article._search ## plainto_tsquery',
'english',
Sequelize.literal(':query')
),
[Sequelize.Op.and]: { status: STATUS_TYPE_ACTIVE }
},
replacements: { query: q }
});
Search index setup:
const vectorName = '_search';
const searchObjects = {
articles: ['headline', 'cleaned_body', 'summary'],
brands: ['name', 'cleaned_about'],
products: ['name', 'cleaned_description']
};
module.exports = {
up: async queryInterface =>
await queryInterface.sequelize.transaction(t =>
Promise.all(
Object.keys(searchObjects).map(table =>
queryInterface.sequelize
.query(
`
ALTER TABLE ${table} ADD COLUMN ${vectorName} TSVECTOR;
`,
{ transaction: t }
)
.then(() =>
queryInterface.sequelize.query(
`
UPDATE ${table} SET ${vectorName} = to_tsvector('english', ${searchObjects[
table
].join(" || ' ' || ")});
`,
{ transaction: t }
)
)
.then(() =>
queryInterface.sequelize.query(
`
CREATE INDEX ${table}_search ON ${table} USING gin(${vectorName});
`,
{ transaction: t }
)
)
.then(() =>
queryInterface.sequelize.query(
`
CREATE TRIGGER ${table}_vector_update
BEFORE INSERT OR UPDATE ON ${table}
FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger(${vectorName}, 'pg_catalog.english', ${searchObjects[
table
].join(', ')});
`,
{ transaction: t }
)
)
.error(console.log)
)
)
),
down: async queryInterface =>
await queryInterface.sequelize.transaction(t =>
Promise.all(
Object.keys(searchObjects).map(table =>
queryInterface.sequelize
.query(
`
DROP TRIGGER ${table}_vector_update ON ${table};
`,
{ transaction: t }
)
.then(() =>
queryInterface.sequelize.query(
`
DROP INDEX ${table}_search;
`,
{ transaction: t }
)
)
.then(() =>
queryInterface.sequelize.query(
`
ALTER TABLE ${table} DROP COLUMN ${vectorName};
`,
{ transaction: t }
)
)
)
)
)
};
The problem is that because the code concats both columns within each array of searchObjects, what is getting stored is a combined index of all columns in each array.
For example on the articles table: 'headline', 'cleaned_body', 'summary' are all part of that single generated _search vector.
Because of this, I can't really search by ONLY headline or ONLY cleaned_body, etc. I'd like to be able to search each column separately and also together.
The use case is in my search typeahead I only want to search on headline. But on my search results page, I want to search on all columns specified in searchObjects.
Can someone give me a hint on what I need to change? Should I create a new tsvector for each column?
If anyone is curious, here's how you can create a tsvector for each column:
try {
for (const table in searchObjects) {
for (const col of searchObjects[table]) {
await queryInterface.sequelize.query(
`ALTER TABLE ${table} ADD COLUMN ${col + vectorName} TSVECTOR;`,
{ transaction }
);
await queryInterface.sequelize.query(
`UPDATE ${table} SET ${col + vectorName} = to_tsvector('english', ${col});`,
{ transaction }
);
await queryInterface.sequelize.query(
`CREATE INDEX ${table}_${col}_search ON ${table} USING gin(${col +
vectorName});`,
{ transaction }
);
await queryInterface.sequelize.query(
`CREATE TRIGGER ${table}_${col}_vector_update
BEFORE INSERT OR UPDATE ON ${table}
FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger(${col +
vectorName}, 'pg_catalog.english', ${col});`,
{ transaction }
);
}
}
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}

ionic run code to create databse only once

I created an app using ionic, and in the app I want to create and populate a database, but I want the code to run once, not everytime I open the app. Because once the databace is create for first time, no need to run recreate it again.
I googled it, I did not find an answer,
I appreciate any one has a solution.
private dboptions: any = {
name: 'ba.db',
createFromLocation: 1,
location: 'default'
}
constructor(
private sqlite: SQLite) {
this.connectDB();
}
private connectDB(): any {
this.sqlite.create(this.dboptions).then(
(db: SQLiteObject) => {
// create the table
let sql = `CREATE TABLE ba (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR (200) NOT NULL UNIQUE,
title VARCHAR (200)`;
db.executeSql(sql, []).then(success => {
console.log('table created');
}).catch(err => {
console.log(err);
})
}
)
.catch(err => console.log(err));
}
User LocalStorage to check if DB is already created like this. Storage Here is the link on how to add local storage to your project.
private dboptions: any = {
name: 'ba.db',
createFromLocation: 1,
location: 'default'
}
constructor(
private sqlite: SQLite, , public storage: Storage) {
this.storage.get("isDbPresent")
.then((data) => {
console.log(data);
if (data === null) {
this.connectDB();
this.storage.get(Constants.storagekey)
}
})
}
private connectDB(): any {
this.sqlite.create(this.dboptions).then(
(db: SQLiteObject) => {
// create the table
let sql = `CREATE TABLE ba (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR (200) NOT NULL UNIQUE,
title VARCHAR (200)`;
db.executeSql(sql, []).then(success => {
console.log('table created');
}).catch(err => {
console.log(err);
})
}
)
.catch(err => console.log(err));
}
add ionic Storage and save a flag when done.
in app.module.ts
import { IonicStorageModule } from '#ionic/storage';
...
imports: [
...
IonicStorageModule.forRoot()
],
...
in your component/provider
import { Storage } from '#ionic/storage';
storage.ready().then(() => {
storage.get('notFirstTime').then((notFirstTime:boolean) => {
if (!notFirstTime){
// Do what you want to do once
}
storage.set('notFirstTime', true);
})
});
...
ionic storage is persistent (when you install cordova-sqlite-storageplugin)

How to return the serial primary key from a db insert to use for another db insert

I'm using the express generated template with postgresql and I have 2 rest route methods for creating consignments and tracking.
However i want tracking to be updated on each consignment insert, but i require the serial primary key to do it. So from the createCon function i require it to return the id after the insert, to use for the cid field in the createConTracking.
routes/index.js file
var db = require('../queries');
router.post('/api/cons', db.createCon);
router.post('/api/cons/:id/tracking', db.createConTracking);
queries.js
var promise = require('bluebird');
var options = {
promiseLib: promise
};
var pgp = require('pg-promise')(options);
var db = pgp(connectionString);
function createCon(req, res, next) {
var conid = parseInt(req.body.conid);
db.none('insert into consignments(conid, payterm,........)'+
'values($1, $2, ......)',
[conid, req.body.payterm,........])
.then(function () {
res.status(200)
.json({
status: 'success',
message: 'Inserted one con'
});
})
.catch(function (err) {
return next(err);
});
}
function createConTracking(req, res, next) {
var cid = parseInt(req.params.id);
var userid = req.user.email;
var conid = parseInt(req.body.conid);
db.none('insert into tracking(status, remarks, depot, userid, date, cid, conid)'+
'values($1, $2, $3, $4,$5, $6, $7)',
[req.body.status, req.body.remarks, req.body.depot, userid, req.body.date, cid, conid])
.then(function (data) {
res.status(200)
.json({
data: data,
status: 'success',
message: 'Updated Tracking'
});
})
.catch(function (err) {
return next(err);
});
}
DB
CREATE TABLE consignments (
ID SERIAL PRIMARY KEY,
conId INTEGER,
payTerm VARCHAR,
CREATE TABLE tracking (
ID SERIAL PRIMARY KEY,
status VARCHAR,
remarks VARCHAR,
cid INTEGER
);
I'm the author of pg-promise.
You should execute multiple queries within a task (method task) when not changing data, or transaction (method tx) when changing data. And in case of making two changes to the database, like in your example, it should be a transaction.
You would append RETURNING id to your first insert query and then use method one to indicate that you expect one row back.
function myRequestHandler(req, res, next) {
db.tx(async t => {
const id = await t.one('INSERT INTO consignments(...) VALUES(...) RETURNING id', [param1, etc], c => +c.id);
return t.none('INSERT INTO tracking(...) VALUES(...)', [id, etc]);
})
.then(() => {
res.status(200)
.json({
status: 'success',
message: 'Inserted a consignment + tracking'
});
})
.catch(error => {
return next(error);
});
}
In the example above we execute the two queries inside a transaction. And for the first query we use the third parameter for an easy return value transformation, plus conversion (in case it is a 64-bit like BIGSERIAL).
Simply add a RETURNING clause to your INSERT statement. This clause allows you to return data concerning the actual values in the inserted record.
insert into consignments(conid, payterm,........)
values($1, $2, ......)
returning id;