Issue with concurrent (iterated) invocations of a function that calls Promise.all() - node.js

This example is a simplification, but hopefully demonstrates my problem. I suspect I am missing something obvious re: closure here.
The issue is that results being returned from my aggregation function below include the final set of personParams i.e. those resulting from the final iteration of the forEach , not the set of personParams that the function was called with.
let promiseArray = []; // top level array of promises
let globalParams = { date: "2018-05-20" };
let people = [
{name: "Tom",
location: "Philadelphia"},
{name: "Bob",
location: "Austin"},
{name: "John",
location: "Philadelphia"}
];
people.forEach( person => {
// Create indivdiualized parameter set
let personParams = globalParams;
personParams.name = person.name;
personParams.location = person.location;
// Push promise to array
promiseArray.push(assembleResults(personParams));
});
Promise.all(promiseArray).then( results => {
console.log("Final Output:", results);
})
// individual data aggregation function
function assembleResults(iterationParams) {
let promise1 = Promise.resolve({data: "Type A data about " + iterationParams.name});
let promise2 = Promise.resolve({data: "Type B data about " + iterationParams.name});
let promise3 = Promise.resolve({data: "Type C data about " + iterationParams.name});
return Promise.all([promise1, promise2, promise3, iterationParams])
}
Updated with executable Fiddle # JSFiddle https://jsfiddle.net/shaunhurley/9r5k6j0c/
My expectation is that each iteration of assembleResults() is going to return an array that looks like:
[{data: "Type A data about <person's name>"},
{data: "Type B data about <person's name>"},
{data: "Type C data about <person's name>"},
{date: "2018-05-20", name: "<person's name>", location: "<location>"}]
Where each array will have name set to "Tom", "Bob" & "John" respectively. The final output would be an array or arrays per below.
[{data: "Type A data about Tom"},
{data: "Type B data about Tom"},
{data: "Type C data about Tom"},
{date: "2018-05-20", name: "Tom", location: "Philadelphia"}]
[{data: "Type A data about Bob"},
{data: "Type B data about Bob"},
{data: "Type C data about Bob"},
{date: "2018-05-20", name: "Bob", location: "Austin"}]
[{data: "Type A data about John"},
{data: "Type B data about John"},
{data: "Type C data about John"},
{date: "2018-05-20", name: "John", location: "Philadelphia"}]
The actual result is that, while the correct data sets are being returned, the parameter set included in each array is showing the last/final parameter set generated i.e. name == "John" and location == "Philadelphia"
[{data: "Type A data about Tom"},
{data: "Type B data about Tom"},
{data: "Type C data about Tom"},
{date: "2018-05-20", name: "John", location: "Philadelphia"}]
[{data: "Type A data about Bob"},
{data: "Type B data about Bob"},
{data: "Type C data about Bob"},
{date: "2018-05-20", name: "John", location: "Philadelphia"}]
[{data: "Type A data about John"},
{data: "Type B data about John"},
{data: "Type C data about John"},
{date: "2018-05-20", name: "John", location: "Philadelphia"}]
Any thoughts would be greatly appreciated...
Thanks!
Shaun

people.forEach(person => {
let personParams = globalParams;
personParams.name = person.name;
promiseArray.push(assembleResults(personParams));
});
In the above code block you called person.name where I can see each person is a String. So you are getting undefined instead of the person's name. I think it should be personParams.name = person.
And getResultsA getResultsB and getResultsC of assembleResults() function should not return any data unless you have some person called undefined.
I guess it's a dummy code you have posted. Posting the real code would have helped me answering more precisely.
Modification
Bellow code would do your job:
people.forEach((person) => {
// Create indivdiualized parameter set
const personParams = {
date: globalParams.date,
name: person.name,
location: person.location
}
// Push promise to array
promiseArray.push(assembleResults(personParams));
});
Explaining how did it work would take too long and I guess you already know why did it behave like that. Please google 'JavaScript object creation'.

Related

Sequelize: How to match atleast 1 value from array stored in db with array in the request

I have a table in psql db like below.
[{
name="ABC",
email="xyz",
animals=["cats","dogs","elephants","giraffes"]
},
{
name="BCD",
email="fgh",
animals=["giraffes"]
}]
....
and an array that I'll get in the request like below
hasAnimals=["lion","cheetah","dog","cat"]
How do I use sequelize where clause to get the records of all the people in the above table who have at least 1 of the animals from the hasAnimals array.
For eg. the stated example should fetch me the user with name ABC as ABC has cats & dogs and so does the array that I got in the request
The solution turned out to be really simple.
const { Op } = require("sequelize");
let arr = []
for(let animal of animals) {
arr.push({
[Op.contains]: [animal],
});
}
abc.findAll({
where: {
animals: {
[Op.or]: arr
}
},
})
sql solution could be something like :
SELECT *
FROM your_table
WHERE your_json_column :: jsonb #? '$[*].animals[*] ? (# <# HasAnimals)'

NodeJS MongoDB Where Array contains any element of Array

I have a collection of content objects. Each document of this collection contains an array with tags:
{
_id: ....,
title: 'Title 1',
details: { ..., tags: ['politic', 'usa'] }
},
{
_id: ....,
title: 'Title 2',
details: { ..., tags: ['art', 'modern'] }
}
The user should be able to filter for tags. For individuals and several.
Is there any way to query for that?
Example:
User search for content with one of the following tags:
['politic', 'french'] => Title1
['music', 'autumn'] => no result
['usa', 'art'] => Title1 & Title2
['modern'] => Title2
What I tried:
const aggregate = [{ $match: { "details.tags": 'music' } }];
mongodb.collection("content").aggregate(aggregate).toArray();
This works fine for searching by one tag.
If I change 'music' to an array like ['music', 'usa'] I don't get any result.
#EDIT1
I added an Index to the collection:
db.content.createIndex( { "details.tags": 1 });
Unfortunately, the aggregation query still returns an empty result. That's why I tried also a find:
db.content.find({"details.tags": ['music', 'usa']})
But also without success.
In order to find multiple values in an array, you should use the $in-operator:
db.collection.find({
"details.tags": {
$in: [
"usa",
"art"
]
}
})
See this example on mongoplayground: https://mongoplayground.net/p/vzeHNdLhq0j

Source object access from ColumnSet

I try to split a sub-object in my recordset when importing data with initCB properties of a Column in ColumnSet.
But when I use two different init functions for two different destination names but one source I get same result.
const cs = new pgp.helpers.ColumnSet([
'id',
{ name: 'source_id', prop: 'source', init: function(obj) { return obj.value.id; } },
{ name: 'source_name', prop: 'source', init: function(obj) { return obj.value.name; } },
], { table: 'test_table' });
const data = [
{ id: 1, source: { id: 1, name: 'source1' } },
{ id: 2, source: { id: 1, name: 'source1' } },
{ id: 3, source: { id: 2, name: 'source2' } },
];
const insert = pgp.helpers.insert(data, cs);
The result is:
INSERT INTO "test_table"("id","source_id","source_name") VALUES
(1,'source1','source1'),
(2,'source1','source1'),
(3,'source2','source2')
instead of expected:
INSERT INTO "test_table"("id","source_id","source_name") VALUES
(1,1,'source1'),
(2,1,'source1'),
(3,2,'source2')
It seems like second invocation of callback function for THE SAME source field overriding result of previous invocation of ANOTHER callback function on THIS source field.
How I can avoid this?
Or there is another way of splitting a sub-object during import?
Option prop doesn't quite work that way. It is there to remap the value to a different property name, but it does not supply the direct object reference.
Instead, use property source of the column descriptor, to reference the source object. Ironically, you called the property in your data source as well, which means you will have to use source twice in your reference:
const cs = new pgp.helpers.ColumnSet([
'id',
{name: 'source_id', init: c => c.source.source.id},
{name: 'source_name', init: c => c.source.source.name}
], {table: 'test_table'});
The first source is what pg-promise API supports, while the second is your data column name :)
Also, as per documentation, the API sets source and this to the same, so if you prefer the ES5 function syntax (looks cleaner for your example), then you can do this instead:
const cs = new pgp.helpers.ColumnSet([
'id',
{ name: 'source_id', init: function() {return this.source.id;}},
{ name: 'source_name', init: function() {return this.source.name;}},
], { table: 'test_table' });
Above we have this point at the source data object.

Access a specific property in Object.keys adonis.js (Node.js)

I have a query that returns an array, with Object.key (array) .foreach I am iterating, I want to know the value of a property in specific array.
Example:
Object.keys(arreglo).forEach(function(key) {
console.log(arreglo[key]);
});
The output is:
name: "Pepito",
Surname: "Perez"
I want to know how to get only the value of the surname
I know it will not work but it would be something like:
console.log(arreglo[key].surname);
You can use Array.forEach on the original array as shown below. You can even extract the fields you are interested using Array.map.
// let's assume the arrary you got from your query is in this format
const arreglo = [
{ firstname: "fn1", surname: "ln1"},
{ firstname: "fn2", surname: "ln2"},
{ firstname: "fn3", surname: "ln3"}
];
// you can log `surname` for each entry in the array
arreglo.forEach(v => console.log(v.surname));
// you can use map to transform the array to just have `surname` using array.map()
const surnames = arreglo.map(v => v.surname);
console.log(surnames);
Is this what you are looking for
const object1 = {
a: {firstname:"sali",lastname:"mali"},
b: {firstname:"sali",lastname:"mali"},
c: {firstname:"sali",lastname:"mali"}
};
Object.keys(object1).forEach(function(key){console.log(object1[key].lastname)});

Access Object property using key name within map

I have below array of object
const reports = [{id:3, name:'three', description:'three d', other: 'other 3'}, {id:2, name:'two', description:'two d', other: 'other 2'}];
and I want to filter out only 2 property of each object and below is my desired output
[{id:3, name:'three'}, {id:2, name:'two'}];
so tried this way
const reportList = reports.map((report) => {id,name} );
console.log(reportList);
throw error
ReferenceError: id is not defined
even I can achieve this by using this approach
this.reportList = reports.map((report) => ({
id: report.id,
name: report.name,
description: report.description
}));
but here I need to write extra code, I want to use object accessor using key, can I achieve anyway?
You must wrap the returning object literal into parentheses. Otherwise curly braces will be considered to denote the function’s body. The following works:
const reports = [{
id: 3,
name: 'three',
description: 'three d',
other: 'other 3'
}, {
id: 2,
name: 'two',
description: 'two d',
other: 'other 2'
}];
const reportList = reports.map(({id, name}) => ({
id,
name
}));
console.log(reportList);
Reference: Returning object literals by MDN

Resources