Sequelize - query other table inside where clause - mysql

I am trying to do something and do not know if this is possible with sequelize. Basically I have this code snippet running on graphQl and basically what this does is to find kits on the kits table and then verify if the same id exists on the "users" table. If not, it returns them, if yes it does not. However now we need to scale this to have pagination and the current snippet is not so scalable. That is why I had the idea to just include the for loop in the where clause or somehow to check there, but really do not know any command on mySql that allows to do this.
Do you have any tip?
async findKitsWithResultNoReg2(_, {search}) {
try {
let promises = []
const a1 = await db.kits.findAll({
where: {
[Op.and]: [
{ result: { [Op.or]: [1, 2, 3] } },
{ cp: 0 },
{[Op.or]: [
{ kitID: { [Op.like]: '%' + search + '%' } }]}
]
}
})
for (let i = 0; i < a1.length; i++) {
const a2 = await db.users.findByPk(a1[i].dataValues.kitID)
if (a2 === null) {
const a3 = {
kitID: a1[i].dataValues.kitID,
result: a1[i].dataValues.result,
date: a1[i].dataValues.resultDate
}
promises.push(a3)
}
}
return Promise.all(promises)
} catch (error) {
console.log(error)
}
},

async findKitsWithResultNoReg() {
try {
const a0 = await sequelize.query(`SELECT kitID, result, resultDate from kits where result in (1,2,3) and cp = 0 and archived = 0 and not Exists(select kitID from users where kits.kitID = users.kitID) order by resultDate desc`, { type: QueryTypes.SELECT })
const a1 = JSON.stringify(a0)
return a1
} catch (error) {
console.log(error)
}
},

Related

RowDataPacket returns empty object but it is not empty [React/Next]

I've been stressing around trying to fix this and I've burnt myself out. I'm calling my serverless mysql trying to get kanbans from teams. I've used this method multiple times and all were working fine but that is most likely because of they only return single item whilst this returns multiple items.
This is my code which returns empty object.
async function getKanbans(team_id){
let kanbans = [];
await sql_query(`SELECT id, sName FROM table WHERE iTeam = ?`, [team_id])
.then(result => {
result.forEach(kanban => {
// console.log(kanban);
kanbans.push({
id: kanban.id,
name: kanban.sName
});
});
})
.catch(err => {
console.log(err);
});
console.log(kanbans);
return kanbans;
}
As you can see.. I am trying to print kanbans and I do get:
[
{ id: 1, name: 'Kanban_1' },
{ id: 2, name: 'Kanban_2' }
]
of out it. Then I'm trying to return it to the item that called this function and this is how that looks like:
teams.push({
id : team.id,
sName : team.sName,
sColor : team.sColor,
aKanbans : result[0]['selectedTeam'] == team.id ? getKanbans(team.id) : null,
});
(a small snippet of something bigger)
Okay, so now when I try and look at the data response (from the frontend) I get this:
{
"success": true,
"message": "Found teams",
"teams": [
{
"id": 1,
"sName": "Team1",
"sColor": "#fcba03",
"aKanbans": {}
},
{
"id": 2,
"sName": "Team2",
"sColor": "#2200ff",
"aKanbans": null
}
]
}
aKanbans from Team1 is empty, empty object. What the **** do I do? I tried mapping it and still got an empty object. React/javascript is not my main language, I just like to learn. Any suggestions?
You are mixing async / await function with normal Promises handling.
Try to change your getKanbans code like this:
async function getKanbans(team_id) {
let kanbans = [];
try {
const result = await sql_query(
`SELECT id, sName FROM table WHERE iTeam = ?`,
[team_id]
);
result.forEach((kanban) => {
kanbans.push({
id: kanban.id,
name: kanban.sName,
});
});
} catch (err) {
console.log(err);
}
return kanbans;
}
And then populate the teams using (declare the parent async):
teams.push({
id : team.id,
sName : team.sName,
sColor : team.sColor,
aKanbans : result[0]['selectedTeam'] == team.id ? getKanbans(team.id) : null,
});

Unable to access inner JSON value in JSON array - Typescript(Using Angular 8)

I am trying to use the group by function on a JSON array using the inner JSON value as a key as shown below. But unable to read the inner JSON value. Here is my JSON array.
NotificationData = [
{
"eventId":"90989",
"eventTime":"2019-12-11T11:20:53+04:00",
"eventType":"yyyy",
"event":{
"ServiceOrder":{
"externalId":"2434",
"priority":"1"
}
}
},
{
"eventId":"6576",
"eventTime":"2019-12-11T11:20:53+04:00",
"eventType":"yyyy",
"event":{
"ServiceOrder":{
"externalId":"78657",
"priority":"1"
}
}
}
]
GroupBy Logic:
const groupBy = (array, key) => {
return array.reduce((result, currentValue) => {
(result[currentValue[key]] = result[currentValue[key]] || []).push(
currentValue
);
return result;
}, {});
};
const serviceOrdersGroupedByExternalId = groupBy(this.NotificationData, 'event.ServiceOrder.externalId');
//this line of code is not working as
// it is unable to locate the external id value.
Desired output
{ "2434":[{
"eventId":"90989",
"eventTime":"2019-12-11T11:20:53+04:00",
"eventType":"yyyy",
"event":{
"ServiceOrder":{ "priority":"1" }
}
}],
"78657":[{
"eventId":"6576",
"eventTime":"2019-12-11T11:20:53+04:00",
"eventType":"yyyy",
"event":{
"ServiceOrder":{ "priority":"1" }
}
}]
}
Does this solves your purpose?
let group = NotificationData.reduce((r, a) => {
let d = r[a.event.ServiceOrder.externalId] = [...r[a.event.ServiceOrder.externalId] || [], a];
return r;
}, {});
console.log(group);
Try like this:
result = {};
constructor() {
let externalIds = this.NotificationData.flatMap(item => item.event.ServiceOrder.externalId);
externalIds.forEach(id => {
var eventData = this.NotificationData.filter(
x => x.event.ServiceOrder.externalId == id
).map(function(item) {
delete item.event.ServiceOrder.externalId;
return item;
});
this.result[id] = eventData;
});
}
Working Demo

Update JSON variable based on Mongoose results

I have a JSON variable outside of mongoDB collection as below
var outputJson = [
{
'Product' : 'TV',
'isSelected': 0
},
{
'Product' : 'Radio',
'isSelected': 0
},
{
'Product' : 'Book',
'isSelected': 0
},
{
'Product' : 'Watch',
'isSelected': 0
}
]
Now I want to update the isSelected key if the product exits in MongoDB; I want something like below
var outputJson = [
{
'Product' : 'TV',
'isSelected': 0
},
{
'Product' : 'Radio',
'isSelected': 1
},
{
'Product' : 'Book',
'isSelected': 0
},
{
'Product' : 'Watch',
'isSelected': 1
}
]
Here is the code that I am trying, but I am not getting the above result
outputJson.forEach(function(key,value){
wishlistData.find({userID:req.user.id}, function(err,data{
data.forEach(function(k,i){
if (data[i].product=== outputJson[value].Product){
outputJson[value].isSelected = 1
}
})
});
})
Any help is appreciated
Firstly, the callback function to forEach is called with the item in the array as the first argument, so data[i] and outputJson[value] are redundant.
You should make use of mongoose's findOne method to see if there's at least one match, and you can pass in Product as follows
outputJson.forEach(function(item) {
wishlistData.findOne({userID: req.user.id, product: item.Product}, function(err, data) {
if (data !== null) { // if it actually found a match
item.isSelected = 1;
}
});
});
But keep in mind that mongoDB queries are asynchronous, so outputJson would still be the same right after the forEach loop. You might want to use promises and Promise.all to ensure that you do stuff with outputJson after all the queries have been processed:
Promise.all(outputJson.map(function(item) {
return wishlistData.findOne({userID: req.user.id, product: item.Product}).then(function(err, data) {
if (data !== null) { // if it actually found a match
item.isSelected = 1;
}
});
})).then(function() {
// do stuff with outputJson here
});
Replace this
if (data[i].product=== outputJson[value].Product){
outputJson[value].isSelected = 1
}
by
if (data[k].product=== outputJson[key].Product){
outputJson[key].isSelected = 1
}
each callback function have first argument as index
First thing, why are you doing the same mongo query repeatedly for each object in the Array, as it will give you the same result. Also use the lodash library 'lodash'. This is how you can achieve this:
var _ = require('lodash')
wishlistData.find({userID:req.user.id}, function (err, data) {
var dataMap = _.indexBy(data, "product");
outputJson.forEach(function(key,value){
if(!_.isEmpty(_.get(dataMap, key.Product))) {
key.isSelected = 1;
}
})
}
Thank you #Ambyjkl, you guidence worked Promise made the trick, I made minor changes to your script and it started working
Promise.all(outputJson.map(function(i,k) {
return wishlistData.findOne({userID: req.user.id, product: i.Product}).then(function(data, err) {
if (data !== null) { // if it actually found a match
i.isSelected = 1;
}
});
})).then(function() {
console.log(outputJson);
});

Transform Request to Autoquery friendly

We are working with a 3rd party grid (telerik kendo) that has paging/sorting/filtering built in. It will send the requests in a certain way when making the GET call and I'm trying to determine if there is a way to translate these requests to AutoQuery friendly requests.
Query string params
Sort Pattern:
sort[{0}][field] and sort[{0}][dir]
Filtering:
filter[filters][{0}][field]
filter[filters][{0}][operator]
filter[filters][{0}][value]
So this which is populated in the querystring:
filter[filters][0][field]
filter[filters][0][operator]
filter[filters][0][value]
would need to be translated to.
FieldName=1 // filter[filters][0][field]+filter[filters][0][operator]+filter[filters][0][value] in a nutshell (not exactly true)
Should I manipulate the querystring object in a plugin by removing the filters (or just adding the ones I need) ? Is there a better option here?
I'm not sure there is a clean way to do this on the kendo side either.
I will explain the two routes I'm going down, I hope to see a better answer.
First, I tried to modify the querystring in a request filter, but could not. I ended up having to run the autoqueries manually by getting the params and modifying them before calling AutoQuery.Execute. Something like this:
var requestparams = Request.ToAutoQueryParams();
var q = AutoQueryDb.CreateQuery(requestobject, requestparams);
AutoQueryDb.Execute(requestobject, q);
I wish there was a more global way to do this. The extension method just loops over all the querystring params and adds the ones that I need.
After doing the above work, I wasn't very happy with the result so I investigated doing it differently and ended up with the following:
Register the Kendo grid filter operations to their equivalent Service Stack auto query ones:
var aq = new AutoQueryFeature { MaxLimit = 100, EnableAutoQueryViewer=true };
aq.ImplicitConventions.Add("%neq", aq.ImplicitConventions["%NotEqualTo"]);
aq.ImplicitConventions.Add("%eq", "{Field} = {Value}");
Next, on the grid's read operation, we need to reformat the the querystring:
read: {
url: "/api/stuff?format=json&isGrid=true",
data: function (options) {
if (options.sort && options.sort.length > 0) {
options.OrderBy = (options.sort[0].dir == "desc" ? "-" : "") + options.sort[0].field;
}
if (options.filter && options.filter.filters.length > 0) {
for (var i = 0; i < options.filter.filters.length; i++) {
var f = options.filter.filters[i];
console.log(f);
options[f.field + f.operator] = f.value;
}
}
}
Now, the grid will send the operations in a Autoquery friendly manner.
I created an AutoQueryDataSource ts class that you may or may not find useful.
It's usage is along the lines of:
this.gridDataSource = AutoQueryKendoDataSource.getDefaultInstance<dtos.QueryDbSubclass, dtos.ListDefinition>('/api/autoQueryRoute', { orderByDesc: 'createdOn' });
export default class AutoQueryKendoDataSource<queryT extends dtos.QueryDb_1<T>, T> extends kendo.data.DataSource {
private constructor(options: kendo.data.DataSourceOptions = {}, public route?: string, public request?: queryT) {
super(options)
}
defer: ng.IDeferred<any>;
static exportToExcel(columns: kendo.ui.GridColumn[], dataSource: kendo.data.DataSource, filename: string) {
let rows = [{ cells: columns.map(d => { return { value: d.field }; }) }];
dataSource.fetch(function () {
var data = this.data();
for (var i = 0; i < data.length; i++) {
//push single row for every record
rows.push({
cells: _.map(columns, d => { return { value: data[i][d.field] } })
})
}
var workbook = new kendo.ooxml.Workbook({
sheets: [
{
columns: _.map(columns, d => { return { autoWidth: true } }),
// Title of the sheet
title: filename,
// Rows of the sheet
rows: rows
}
]
});
//save the file as Excel file with extension xlsx
kendo.saveAs({ dataURI: workbook.toDataURL(), fileName: filename });
})
}
static getDefaultInstance<queryT extends dtos.QueryDb_1<T>, T>(route: string, request: queryT, $q?: ng.IQService, model?: any) {
let sortInfo: {
orderBy?: string,
orderByDesc?: string,
skip?: number
} = {
};
let opts = {
transport: {
read: {
url: route,
dataType: 'json',
data: request
},
parameterMap: (data, type) => {
if (type == 'read') {
if (data.sort) {
data.sort.forEach((s: any) => {
if (s.field.indexOf('.') > -1) {
var arr = _.split(s.field, '.')
s.field = arr[arr.length - 1];
}
})
}//for autoquery to work, need only field names not entity names.
sortInfo = {
orderByDesc: _.join(_.map(_.filter(data.sort, (s: any) => s.dir == 'desc'), 'field'), ','),
orderBy: _.join(_.map(_.filter(data.sort, (s: any) => s.dir == 'asc'), 'field'), ','),
skip: 0
}
if (data.page)
sortInfo.skip = (data.page - 1) * data.pageSize,
_.extend(data, request);
//override sorting if done via grid
if (sortInfo.orderByDesc) {
(<any>data).orderByDesc = sortInfo.orderByDesc;
(<any>data).orderBy = null;
}
if (sortInfo.orderBy) {
(<any>data).orderBy = sortInfo.orderBy;
(<any>data).orderByDesc = null;
}
(<any>data).skip = sortInfo.skip;
return data;
}
return data;
},
},
requestStart: (e: kendo.data.DataSourceRequestStartEvent) => {
let ds = <AutoQueryKendoDataSource<queryT, T>>e.sender;
if ($q)
ds.defer = $q.defer();
},
requestEnd: (e: kendo.data.DataSourceRequestEndEvent) => {
new DatesToStringsService().convert(e.response);
let ds = <AutoQueryKendoDataSource<queryT, T>>e.sender;
if (ds.defer)
ds.defer.resolve();
},
schema: {
data: (response: dtos.QueryResponse<T>) => {
return response.results;
},
type: 'json',
total: 'total',
model: model
},
pageSize: request.take || 40,
page: 1,
serverPaging: true,
serverSorting: true
}
let ds = new AutoQueryKendoDataSource<queryT, T>(opts, route, request);
return ds;
}
}

It is possible an synchronous mysql query in nodejs?

I try find solutions like "Promices" or modules like "synchronize" or "sync", but I donsen't fine a properly solution ):
The way its I have like 10 tables 'user', 'data', 'game'. And functions like:
getUsers(){} // UserTable
getData(){} // DataModel
getGames(){} // GameTable
getUserByGames(){} // UserModel
And some funtions have needs to return me a model or table... and in some cases I need that model or that 'answer' to make another query and another stuff.
So, i need to do synchronous querys to make that in the best way, no use '.then' or stuff like that :/
Did you know how I can doit in nodejs?
(Maybe a solution can be put a flag in true each async function and in the callback change the flag to false. With a while for dosent end the original function?)
You can mix callbacks, sequential and parallel execution, loops, recursion with SynJS. Here is an example to illustrate:
var SynJS = require('synjs');
var mysql = require('mysql');
var connection = mysql.createConnection({
host : 'localhost',
user : 'tracker',
password : 'tracker123',
database : 'tracker'
});
function runSQLQuery(modules,connection,context,query,queryParams) {
var res = {done: false};
connection.query(query,queryParams, function(err, rows, fields) {
res.err = err;
res.rows = rows;
res.done = true;
//console.log('got rows:',rows);
modules.SynJS.resume(context);
});
return res;
}
function myFunc(modules,connection) {
for(var i=0; i<3; i++) {
console.log('Iteration:',i);
// sequential execution
var res1 = modules.runSQLQuery(modules,connection,_synjsContext,"SELECT 100+? as id",[i]);
SynJS.wait(res1.done);
console.log("res1=", res1.rows);
var res2 = modules.runSQLQuery(modules,connection,_synjsContext,"SELECT 200+? as id",[i]);
SynJS.wait(res2.done);
console.log("res2=", res2.rows);
// parallel execution
var res3 = modules.runSQLQuery(modules,connection,_synjsContext,"SELECT 300+? as id",[i]);
var res4 = modules.runSQLQuery(modules,connection,_synjsContext,"SELECT 400+? as id",[i]);
SynJS.wait(res3.done && res4.done);
console.log("res3,4=", res3.rows, res4.rows);
}
};
var modules = {
SynJS: SynJS,
mysql: mysql,
runSQLQuery: runSQLQuery,
};
SynJS.run(myFunc,null,modules,connection,function () {
console.log('done');
connection.end();
});
It produces following output:
Iteration: 0
res1= [ { id: 100 } ]
res2= [ { id: 200 } ]
res3,4= [ { id: 300 } ] [ { id: 400 } ]
Iteration: 1
res1= [ { id: 101 } ]
res2= [ { id: 201 } ]
res3,4= [ { id: 301 } ] [ { id: 401 } ]
Iteration: 2
res1= [ { id: 102 } ]
res2= [ { id: 202 } ]
res3,4= [ { id: 302 } ] [ { id: 402 } ]
done