oracle node executeMany with dynamic sql query - javascript

I am trying to do UPDATE query with executeMany statement. I have tried following with execute in async forEach loop like this:
export const asyncForEach = async (array, callback) => {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
}
then some dynamicDefinitions
export const buildDynamicDefs = (data,enumRef) => {
const defs = {...data};
Object.keys(defs).forEach(key => defs[key] = enums.typeDefs[enumRef][key])
return defs;
};
then finally execute call
...
await asyncForEach(dataArray, async (device) => {
response[device.deviceId] = await connection.execute(
queryPut(device),
{
bind1,
...device,
},
{
autoCommit: false,
bindDefs: {
bind1,
...buildDynamicDefs(device,'enumName'),
},
},
)
})
and query looks following :
export const queryPut = data => `
UPDATE status
SET MANDATORY_PROP1=:bind4
${data.bind2 && ',OPT_PROP1=:bind2' || ''}
${data.bind3 && ',OPT_PROP2=:bind3' || ''}
WHERE MANDATORY_PROP2=:bind1 AND MANDATORY_PROP3=:bind5`;
Problem is that when i use executeMany, i do not know how to tell query how should it looks like
.executeMany(query,arrayOfObject-binds,options)
There is only one query. And my array of objects may looks following:
[
{
bind1: 1,
bind4: 4,
bind5: 5,
},
{
bind1: 1,
bind2: 2,
bind4: 4,
bind5: 5,
},
]
Is there some possibility to optionally add or include that set OPT_PROP1=:bind2 in sql? is there some check for null value of binds?
With execute it is working, but i dont think it is efficient for big numbers of data.
Thanks,

After some investigation all i needed to do it was change SQL a bit:
UPDATE status
SET MANDATORY_PROP1=:bind4,
OPT_PROP1=COALESCE(:bind2,OPT_PROP1),
OPT_PROP2=COALESCE(:bind3,OPT_PROP2),
WHERE MANDATORY_PROP2=:bind1 AND MANDATORY_PROP3=:bind5`;

Related

async await not waiting for object to fill axios.get

I have an object called move_dict and am using the method get_learned_by_pokemon() to fill it. In getPokes() I call the get_learned_by_pokemon() and expect move_dict to be filled. However it is empty.
function move(){
let move_dict = {}
let db_data = JSON.parse(fs.readFileSync('pokes.json', 'utf8'));
async function get_learned_by_pokemon(curr_move, move_dict, current_poke){
response = await axios.get('https://pokeapi.co/api/v2/move/'.concat(curr_move))
let learned_by_pokemons = []
for (let k = 0; k < response.data.learned_by_pokemon.length; k++){
learned_by_pokemons.push(response.data.learned_by_pokemon[k].name)
}
// pass
if (learned_by_pokemons.includes(current_poke)){move_dict[current_poke].pass.push(curr_move)}
// fail
else{move_dict[current_poke].fail.push(curr_move)}
}
function getPokes(){
//iterate through all pokemon
for (let i = 0; i < db_data.length; i++){
let current_poke = db_data[i].name
let moves = db_data[i].moves
move_dict[current_poke] = {pass: [], fail: []}
//iterate through all moves of pokemon
for (let j = 0; j < moves.length; j++){
let curr_move = moves[j]
//get data on current move
get_learned_by_pokemon(curr_move, move_dict, current_poke)
}
}
}
getPokes()
}
I've also used an await before the Axios.get()
. I'd like to know why move_dict is not filling and I'd like to overcome this problem without using a setTimeout()
It looks like the OP begins with a json file describing pokemons. From the code, it looks like the file contains at least the following...
[
{ name: 'nameA', moves: [ 1, 2, 3, ... ] },
{ name: 'nameB', moves: [ 3, 4, 5, ... ] },
...
]
It looks like there's an api endpoint that takes a move ("id or name") and returns, among other things, a list of pokemons that have "learned" that move.
And it looks like the OP aims to produce a dictionary like this...
{
nameA: { pass: [1, 2, ...], fail: [3, 4, ...] },
nameB: { pass: [3, 4, ...], fail: [4, 5, ...] },
...
}
... where the pass and fail arrays moves found in the input file that either are or are not learned moves according to the api.
Since we don't want to call the api redundantly, and since there might be redundant moves in the input file, it 's worthwhile to create an intermediate structure that associates unique moves in the input the pokemons who have learned them, like this...
{ // key is a move id or name, value is an array of pokemon names
1 : [ 'nameA', 'nameB', ... ],
2 : [ 'nameC', 'nameD', ... ],
...
}
So here's how I'd describe the idea in code (not compiled or tested)...
async function get_learned_by_pokemon(move){
const response = await axios.get(`https://pokeapi.co/api/v2/move/${move}`);
return response.data.learned_by_pokemon.map(p => p.name);
}
async function move() {
let db_data = JSON.parse(fs.readFileSync('pokes.json', 'utf8'));
let allMoves = db_data.flat_map(p => p.moves);
let uniqueMoves = [...new Set(allMoves)];
// map move ids to pokemons who learned the moves { moveId: [ 'pokeA', 'pokeB' ...], ...}
let learnedBy = {}
for (let move in uniqueMoves) {
learnedBy[move] = await get_learned_by_pokemon(move);
}
// map pokemon names to a pair of arrays indicating if they have learned their moves (pass) or not (fail)
let move_dict = db_data.reduce((acc, p) => {
let name = p.name;
let pass = p.moves.filter(move => learnedBy[move].includes(name));
let fail = p.moves.filter(move => !learnedBy[move].includes(name));
acc[name] = { pass, fail };
return acc;
}, {});
return move_dict;
}
there are 2 wrong with your code.
1: where do you call getPokes() ?
2: get_learned_by_pokemon is async function so you have to use await if you want to wait for it done

Smarter way of using filter and map instead of filter and loop

I want to create a smarter way of coding of the following example. Important is that each loop (for activeFilters) needs to be fully done, before we want to return the filtersTest.
const createFilters = async () => {
const filtersTest = [] as any
// Only create active filters by checking count.
const activeFilters = getComponentFilter.value.filter(function(item) {
if (item.items) {
return item.items.some((obj) => obj.count)
}
});
// Loop through the active filters and push arrays into the object.
for(let i = 0 ; i < activeFilters.length; i++) {
const options = await createFilterOptions(activeFilters[i].id, activeFilters[i].items);
const array = {
defaultValue: null,
id: activeFilters[i].id,
value: 'nee',
label: activeFilters[i].label,
options: options,
}
filtersTest.push(array)
}
return filtersTest;
}
First of all, it should be clear that createFilters is not going to return the array, but a promise that will eventually resolve to that array.
With that in mind, you can reduce your code a bit, using Promise.all, the ?. operator, destructuring parameters, and shorthand property names in object literals:
const createFilters = () => Promise.all(
getComponentFilter.value.filter(({items}) =>
items?.some((obj) => obj.count)
).map(({id, label, items}) =>
createFilterOptions(id, items).then(options => ({
defaultValue: null,
id,
value: 'nee',
label,
options
}))
)
);

How to get the value of the key present inside an object in Javascript

Below is my sample data from which I want to extract the string present inside ResponseVariations array's object : CriterionExpression
Articles":[
"ResponseVariations":[
"CriterionExpression":"User: internal AND Country: CA"
]
]
Code Snippet:
function getChannel(agent){
const channelInfo = agent.Articles;
channelInfo.forEach((ResponseVariations) => {
if(channelInfo.values(CriterionExpression)!=="DEFAULT_SELECTION")
var iterator = channelInfo.values();
console.log(iterator.next().value);
});
But the above criteria is not fitting well to extract information of those criteria in which the String is not DEFAULT_SELECTION.
Any suggestions how to traverse this array object's value ?
The below code worked in order to fetch the key:
const _dir1 = path.resolve(__dirname, `../`);
const _config1 = JSON.parse(fs.readFileSync(path.resolve(_dir, "./export.4862052732962492282_2.json"), "utf-8"));
const filteredArr = {};
_config1.Articles.forEach(el => {
if (el.ResponseVariations && el.ResponseVariations.length > 1 ) {
el.ResponseVariations && el.ResponseVariations.forEach((rv) => {
if(rv.CriterionExpression !== 'DEFAULT_SELECTION') {
console.log('not default', rv);
filteredArr[rv.CriterionExpression]= rv.Text;
project['data']['supportedChannels'].push(filteredArr);
}
})

Pagination in TypeORM/NestJS

I have to introduce pagination in findAll() method. I really dont know how to do it. I tried but it is giving so many errors. I used findAndCount() method given by typeorm for that, But I am not sure how it will work.
As of now below method returning all the record. I need to return at a time 10 records. Please suggest what modification I need to do.
async findAll(queryCertificateDto: QueryCertificateDto,page=1): Promise<PaginatedResult> {
let { country, sponser } = queryCertificateDto;
const query = this.certificateRepository.createQueryBuilder('certificate');
if (sponser) {
sponser = sponser.toUpperCase();
query.andWhere('Upper(certificate.sponser)=:sponser', { sponser });
}
if (country) {
country = country.toUpperCase();
query.andWhere('Upper(certificate.country)=:country', { country });
}
const certificates = query.getMany();
return certificates;
}
this is PaginatedResult file.
export class PaginatedResult {
data: any[];
meta: {
total: number;
page: number;
last_page: number;
};
}
I tried changing code of findAll() but where clause is giving error. I am not sure how to handle query.getMany() in pagination.
const take = query.take || 10
const skip = query.skip || 0
const [result, total] = await this.certificateRepository.findAndCount(
{
where: query.getMany(), //this is giving error
take:take,
skip:skip
}
);
return result;
I need to introduce pagination in this method. Any help will be really helpful.
Typeorm has a really nice method specific to your usecase findAndCount
async findAll(queryCertificateDto: QueryCertificateDto): Promise<PaginatedResult> {
const take = queryCertificateDto.take || 10
const skip = queryCertificateDto.skip || 0
const country = queryCertificateDto.keyword || ''
const sponser = queryCertificateDto.sponser || ''
const query = this.certificateRepository.createQueryBuilder('certificate');
const [result, total] = await this.certificateRepository.findAndCount(
{
where: { country: Like('%' + country + '%') AND sponser: Like('%' + sponser + '%') }, order: { name: "DESC" },
take: take,
skip: skip
}
);
return {
data: result,
count: total
};
}
More documentation about Repository class can be found here
You don't need the .getMany() with your where in the last code, the result is an array of the data you need.
From your first code, you can do this:
async findAll(queryCertificateDto: QueryCertificateDto,page=1): Promise<PaginatedResult> {
// let's say limit and offset are passed here too
let { country, sponser, limit, offset } = queryCertificateDto;
const query = this.certificateRepository.createQueryBuilder('certificate');
if (sponser) {
sponser = sponser.toUpperCase();
query.andWhere('certificate.sponser = :sponser', { sponser });
}
if (country) {
country = country.toUpperCase();
query.andWhere('certificate.country = :country', { country });
}
// limit and take mean the same thing, while skip and offset mean the same thing
const certificates = await query
.orderBy("certificate.id", "ASC")
.limit(limit || 10)
.offset(offset || 0)
.getMany();
// if you want to count just replace the `.getMany()` with `.getManyandCount()`;
return certificates;
}```

JavaScript - Targeting an object value to create another variable

So I have an array which looks like this:
[
{ TransactionValues: '50.00' },
{ TransactionValues: '-77.43' },
{ TransactionValues: '-20.23' },
{ TransactionValues: '200.23' }
]
I am trying to find a way to target the monetary value and create a variable based on the sum of these. When I try to target the "50.00" for example I get "Undefined" and it's still an array.
I'm not exactly sure how I can target it specifically, is it possible? Any help would be appreciated
As per the comments here is the full code (be wary I'm still learning so it's not elegant):
var fs = require('fs');
var parse = require('csv-parse');
var transactionValues = []; //Need an array to hold transactions
var currentTrans = [];
var savingsTrans = [];
//constuctor for transactions
function addData (id, accountType, initiatorType, dateTime, transactions) {
var data = {
"AccountID" : id,
"AccountType" : accountType,
"InitiatorType" : initiatorType,
"DateTime" : dateTime,
"TransactionValues" : transactions
}
transactionValues.push(data); //should add a new line
}
function logTrans (accountType, transactions) {
if (accountType == "CURRENT") {
var cTrans = {
"TransactionValues" : transactions
}
currentTrans.push(cTrans);
}
else {
var sTrans = {
"TransactionValues" : transactions
}
savingsTrans.push(sTrans);
}
};
//parses the csv file, loops each row and adds it to the transactionValue array
var parser = parse({columns: true}, function (err, results) {
console.table(results);
for (const row of results) {
addData(row.AccountID, row.AccountType, row.InitiatorType, row.DateTime, row.TransactionValue );
logTrans(row.AccountType, row.TransactionValue);
}
console.log(transactionValues);
console.log(currentTrans);
console.log(savingsTrans);
});
fs.createReadStream(__dirname+'/testData/customer-1234567-ledger.csv').pipe(parser)
not completely following but at the end of the day you have an array like data below.
you can use filter to target the attribute you want.
you can use map to pull out just the values.
you can use reduce to sum them all up.
run the snippet below to see each step
const data = [
{ TransactionValues: '50.00', AccountType: 'CURRENT' },
{ TransactionValues: '-77.43', AccountType: null},
{ TransactionValues: '-20.23', AccountType: 'CURRENT' },
{ TransactionValues: '200.23', AccountType: null }
];
const CurrentTrans = data.filter((x) => x.AccountType === 'CURRENT');
const SavingTrans = data.filter((x) => x.AccountType !== 'CURRENT');
console.log('CurrentTrans');
console.log(CurrentTrans);
console.log('SavingTrans');
console.log(SavingTrans);
const CurrentTransValues = CurrentTrans.map((x) => parseFloat(x.TransactionValues));
const SavingTransValues = SavingTrans.map((x) => parseFloat(x.TransactionValues));
console.log('CurrentTransValues');
console.log(CurrentTransValues);
console.log('SavingTransValues');
console.log(SavingTransValues);
const TotalCurrentValues = CurrentTransValues.reduce((sum, x) => sum + x);
const TotalSavingValues = SavingTransValues.reduce((sum, x) => sum + x);
console.log('TotalCurrentValues');
console.log(TotalCurrentValues.toFixed(2));
console.log('TotalSavingValues');
console.log(TotalSavingValues.toFixed(2));
So I may have fixed it by using parseFloat in my addData and logTrans functions:
function addData (id, accountType, initiatorType, dateTime, transactions) {
var data = {
"AccountID" : id,
"AccountType" : accountType,
"InitiatorType" : initiatorType,
"DateTime" : dateTime,
"TransactionValues" : parseFloat(transactions)
}
transactionValues.push(data); //should add a new line
}
function logTrans (accountType, transactions) {
if (accountType == "CURRENT") {
var cTrans = parseFloat(transactions);
currentTrans.push(cTrans);
}
else {
var sTrans = parseFloat(transactions);
savingsTrans.push(sTrans);
}
};
Now that seems to of worked. So I can use the "Sum values of objects in array" as suggested before. Thank you everyone :)

Categories

Resources