Why does Mongoose always get an older snapshot of my database? - javascript

I have a database of football matches and have the following situation:
Promotion >= 6 points & Relegation < 4 points.
I am in Season 1, Division 8. I have 1 match in my database that is for season 1, it was a win so 3 points.
I then have [{"season": "1", "Score": "1-0"}, {"season": "1", "Score": "2-0"}, {"season": "2", "Score": "3-0"}]
The first two matches in the array are for season 1 so I know this is division 8.
For the third match I need to check the result of Season 1 to know what division Season 2 should be. My issue is that when I check this it is only checking based on the very first match and it is saying 3 points when it should be 9 points.
How do I force Mongoose to use the latest snapshot of my database and not one from the very start of the function?
Matches.js
const router = require('express').Router();
const Match = require('../../models/match.model');
const getSeasonData = require('./getSeasonData');
router.route('/getNewMatches').post(auth, async (req, res) => {
const matches = await Match.find();
const getDivisionBasedOnSeasonResult = async () => {
const seasonData = await getSeasonData(seasonOfLastGame);
console.log({ seasonData });
switch (seasonData[0].seasonResult) {
case "Promoted":
return seasonData[0].division - 1;
case "Remained":
return seasonData[0].division;
case "Relegated":
return seasonData[0].division + 1;
default:
console.log("result not one of the three values");
}
}
const eaMatches = [{"season": "1", "Score": "1-0"}, {"season": "1", "Score": "2-0"}, {"season": "2", "Score": "3-0"}]
let seasonOfLastGame = 1;
for (const match of eaMatches) {
if (seasonOfLastGame === season) {
division = 8;
} else {
division = await getDivisionBasedOnSeasonResult();
}
seasonOfLastGame = season;
const newMatch = new Match({
division,
});
newMatch.save()
.then(() => {
res.json('Match added!')
})
.catch(err => res.status(400).json('Error: ' + err));
};
});
module.exports = router;
getSeasonData.js
const Match = require('../../models/match.model');
const getSeasonData = async seasonOfLastGame => {
const stages = [
{ "$match": { season: seasonOfLastGame } }
{
"$group":
{
"_id": "$season",
"points": {
"$sum": {
"$add": [{"$sum": { $cond: [{ $eq: ['$result', "Win"] }, 1, 0] } }]
}
},
"teamPlayed": { $sum: 1 }
}
},
{ "$sort": { "_id": 1 } },
{
"$project": {
"seasonResult":
{
$switch:
{
branches: [
{
case: {$gte: ["$points", 6] },
then: "Promoted"
},
{
case: {$gte: ["$points", 4] },
then: "Remained"
},
{
case: {$lt: ["$points", 4] },
then: "Relegated"
}
],
default: "Result not available"
}
}
},
}
]
return Match.aggregate(stages);
}
module.exports = getSeasonData;

I have fixed this by adding this
const updatedMatches = await Match.find();
into the else statement just above
division = await getDivisionBasedOnSeasonResult();

Related

Adjust a 22-line JS function that now returns wrong counts with +1 / +2 differences for no reasons

My function further below seems fine yet the count is overstated in some instances, eg: should count '1' but shows '2'.
Data source for context:
{
"currency": "USD",
"services": [
{
"category": [
{"token": "token1"},
{"token": "token2"}
],
"price": 149
},
{
"category": [
{"token": "token3"},
{"token": "token4"}
],
"price": 149
}
]
},
{
"currency": "EUR",
"services": [
{
"category": [
{"token": "token1"},
{"token": "token2"}
],
"price": 149
},
{
"category": [
{"token": "token3"},
{"token": "token4"}
],
"price": 149
}
Goal: COUNT the frequency of category tokens per price, sorted by currency in their own objects.
Desired output schema (for illustration purposes, unrelated to above schema example):
{
"result": [
{
"currency": "USD",
"token": "Wellness",
"count": 1,
"price": 100
},
{
"currency": "USD",
"token": "Adventure",
"count": 1,
"price": 300
}
]
}
It appears that sometimes, the count is not right, by +1 or +2 difference for no apparent reasons.
My function, which outputs wrong counts:
const data = inputs.data;
const result = [];
let error = null;
try {
data.forEach(item => {
item.services.forEach(service => {
service.category.forEach(tokenObject => {
const token = tokenObject.token;
const existingToken = result.find(item => item.token === token && item.price === service.price && item.currency === item.currency);
if (existingToken) {
existingToken.count++;
} else {
result.push({currency: item.currency, token, count: 1, price: service.price});
}
});
});
});
} catch (e) {
error = "error";
}
return error ? [1, {error}] : [0, {result}]
Any way to make it "fool-proof" with some kind of "UNIQUE" safe guard?
Note: I'm beginner in JS.
I've used OpenAI's playground and it gave me the right function!
It suggested to "modify the existing code to use a Map data structure instead of an array to store the token count information. The Map will allow to keep track of each token, currency, and price combination as a key, and its count as the value"
const data = inputs.data;
const result = [];
let error = null;
try {
const tokenMap = new Map();
data.forEach(item => {
item.services.forEach(service => {
service.category.forEach(tokenObject => {
const token = tokenObject.token;
const key = `${token}-${service.price}-${item.currency}`;
if (tokenMap.has(key)) {
tokenMap.set(key, tokenMap.get(key) + 1);
} else {
tokenMap.set(key, 1);
}
});
});
});
tokenMap.forEach((count, key) => {
const [token, price, currency] = key.split('-');
result.push({currency, token, count, price: Number(price)});
});
} catch (e) {
error = "error";
}
return error ? [1, {error}] : [0, {result}]

How do I query the contents of my list in order?

I am trying to create DNS api. Since my records are asynchronous on node js, I cannot match record names and record values. How can I sync this build? or how can I add the record names next to the record values?
import dns from 'dns';
const rrtype="*";
const obj= ["A","MX","CNAME","NS","TXT"];
var myobj = [];
export const getAllRecords = (req,res) => {
const {domain} = req.params;
for(var i=0;i<obj.length;i++){
dns.resolve(domain, obj[i], (err, records) => myobj.push(records));
}
setTimeout(function(){
res.send(myobj);
}, 1000);
myobj = [];
}
You can use Promise.All like #Barmar suggested or if you want to do the dns lookups sequencially you can do something like this, notice the use of dns.promises.resolve which returns a promise and not a callback, and the use of async/await. I tested it as a script so i'm also putting the output of the call to getAllRecords({ params: { domain: "gmail.com" } });
import dns from "dns";
const rrtype = "*";
const obj = ["A", "MX", "CNAME", "NS", "TXT"];
export const getAllRecords = async (req, res) => {
const { domain } = req.params;
let myObj = [];
for (let i = 0; i < obj.length; i++) {
try {
const res = await dns.promises.resolve(domain, obj[i]);
myObj.push({ recordType: obj[i], recordValue: res });
} catch (err) {
console.log(err);
}
}
// Debug results
console.log(JSON.stringify(myObj, null, 4));
setTimeout(() => {
// res.send(myObj);
console.log(" === END ===");
}, 1000);
};
// Simple test
getAllRecords({ params: { domain: "gmail.com" } });
Output:
> node dnsTest.js
Error calling resolve Error: queryCname ENODATA gmail.com
at QueryReqWrap.onresolve [as oncomplete] (internal/dns/promises.js:170:17) {
errno: 'ENODATA',
code: 'ENODATA',
syscall: 'queryCname',
hostname: 'gmail.com'
}
[
{
"recordType": "A",
"recordValue": [
"142.250.184.5"
]
},
{
"recordType": "MX",
"recordValue": [
{
"exchange": "alt4.gmail-smtp-in.l.google.com",
"priority": 40
},
{
"exchange": "alt2.gmail-smtp-in.l.google.com",
"priority": 20
},
{
"exchange": "alt3.gmail-smtp-in.l.google.com",
"priority": 30
},
{
"exchange": "gmail-smtp-in.l.google.com",
"priority": 5
},
{
"exchange": "alt1.gmail-smtp-in.l.google.com",
"priority": 10
}
]
},
{
"recordType": "NS",
"recordValue": [
"ns1.google.com",
"ns3.google.com",
"ns2.google.com",
"ns4.google.com"
]
},
{
"recordType": "TXT",
"recordValue": [
[
"globalsign-smime-dv=CDYX+XFHUw2wml6/Gb8+59BsH31KzUr6c1l2BPvqKX8="
],
[
"v=spf1 redirect=_spf.google.com"
]
]
}
]
=== END ===

Get property of a grouped result from reduce in javascript

In grouped result I need the property of vendor and store in new array. but the give me some error. the error is Error: words is not defined.
how can I get vendor property from grouped list?
it is about to get result and store in new property.
const cart = {
"_id": 2,
"owner": 7,
"products": [{
"_id": {
"$oid": "5f06f9a4b8b878050fbc54b2"
},
"product": 1,
"vendor": 1,
"quantity": 2
}, {
"_id": {
"$oid": "5f06f9a4b8b878050fbc54b3"
},
"product": 2,
"vendor": 1,
"quantity": 1
}, {
"_id": {
"$oid": "5f06f9a4b8b878050fbc54b4"
},
"product": 4,
"vendor": 2,
"quantity": 1
}],
"createdAt": {
"$date": "2020-06-21T06:46:40.111Z"
},
"updatedAt": {
"$date": "2020-07-09T11:04:04.459Z"
},
"__v": 0,
"totalPrice": 265
}
const product = cart.products;
var grouped = product.reduce((dictionary, p) => {
dictionary[p.vendor] = dictionary[p.vendor] || [];
dictionary[p.vendor].push(p);
return dictionary;
}, {})
for (const p in grouped) {
console.log(grouped[p].vendor)
}
const cart = {
_id: 2,
owner: 7,
products: [
{
_id: {
$oid: "5f06f9a4b8b878050fbc54b2",
},
product: 1,
vendor: 1,
quantity: 2,
},
{
_id: {
$oid: "5f06f9a4b8b878050fbc54b3",
},
product: 2,
vendor: 1,
quantity: 1,
},
{
_id: {
$oid: "5f06f9a4b8b878050fbc54b4",
},
product: 4,
vendor: 2,
quantity: 1,
},
],
createdAt: {
$date: "2020-06-21T06:46:40.111Z",
},
updatedAt: {
$date: "2020-07-09T11:04:04.459Z",
},
__v: 0,
totalPrice: 265,
};
// const result = words.filter((word) => word.length > 6); // useless line, you do not have variable 'words'
const f = cart.products.filter((p) => p.vendor == 1);
const products = cart.products; //better variable naming
var grouped = products.reduce((dictionary, p) => {
dictionary[p.vendor] = dictionary[p.vendor] || [];
dictionary[p.vendor].push(p);
return dictionary;
}, {});
for (const p in grouped) {
console.log(grouped[p]); //is array
}
To fix this code just delete the line where you use variable words coz you didn't declare such.
To get vendor value:
grouped[p] is an array. It doesn't have a property vendor. But you can get it with:
for (const p in grouped) {
console.log(grouped[p][0].vendor);
}
or get an array of them:
let vendors = Object.getOwnPropertyNames(grouped);
Aside from the 2 lines of code which do nothing, I think you're trying to get the id of the vendor for each group - in which case this is just p in your code at the bottom which logs:
const cart = {"_id":2,"owner":7,"products":[{"_id":{"$oid":"5f06f9a4b8b878050fbc54b2"},"product":1,"vendor":1,"quantity":2},{"_id":{"$oid":"5f06f9a4b8b878050fbc54b3"},"product":2,"vendor":1,"quantity":1},{"_id":{"$oid":"5f06f9a4b8b878050fbc54b4"},"product":4,"vendor":2,"quantity":1}],"createdAt":{"$date":"2020-06-21T06:46:40.111Z"},"updatedAt":{"$date":"2020-07-09T11:04:04.459Z"},"__v":0,"totalPrice":265}
//const result = words.filter(word => word.length > 6);
//const f = cart.products.filter(p => p.vendor == 1);
const product = cart.products;
var grouped = product.reduce((dictionary, p) => {
dictionary[p.vendor] = dictionary[p.vendor] || [];
dictionary[p.vendor].push(p);
return dictionary;
}, {})
let vendor;
for (const p in grouped) {
console.log("vendor=", p, " count of items=", grouped[p].length)
}
I think this will give you the result you are looking for:
let f = cart.products.map( p => p.vendor);
let newArray = f.filter((vendor,index,arr)=>vendor!==arr[index+1]);
newArray.forEach(element => {
console.log(element);
});
You have some extraneous code in your script.
const result = words.filter(word => word.length > 6);
On line 36 you write const result = words.filter(word => word.length > 6); but words is not defined anywhere in your code and that is what generates the error.
For what concerns what you want to achieve, I am not entirely sure I understood it but, if I did, you can solve your issue like this:
const cart = {
"_id": 2,
"owner": 7,
"products": [{
"_id": {
"$oid": "5f06f9a4b8b878050fbc54b2"
},
"product": 1,
"vendor": 1,
"quantity": 2
}, {
"_id": {
"$oid": "5f06f9a4b8b878050fbc54b3"
},
"product": 2,
"vendor": 1,
"quantity": 1
}, {
"_id": {
"$oid": "5f06f9a4b8b878050fbc54b4"
},
"product": 4,
"vendor": 2,
"quantity": 1
}],
"createdAt": {
"$date": "2020-06-21T06:46:40.111Z"
},
"updatedAt": {
"$date": "2020-07-09T11:04:04.459Z"
},
"__v": 0,
"totalPrice": 265
}
const products = cart.products;
const vendors = products
.map(product => product.vendor)
.reduce((vendors, vendor) => {
if (vendors.indexOf(vendor) < 0) {
vendors.push(vendor);
}
return vendors;
}, []);
const productsByVendor = products.reduce((dictionary, p) => {
dictionary[p.vendor] = dictionary[p.vendor] || [];
dictionary[p.vendor].push(p);
return dictionary;
}, {});
console.log('Products grouped by vendor:\n', productsByVendor);
// in 'productsByVendor' the vendors are the keys of your object
console.log('Vendors:', Object.keys(productsByVendor));
/* if you want to retrieve the vendor of a specific product from 'productsByVendor'
* Assumptions:
* 1. cart.products[n].product is assumed to be a unique id (if that is not the case, you can use cart.products[n]._id instead)
* 2. I am assuming that each product can be sold by only one vendor; if a product can be sold by more than one vendor you'll have to adjust a bit the function
*/
getVendorIdFromGroupedProducts = (productId) => {
for (let key of Object.keys(productsByVendor)) {
for (let prod of productsByVendor[key]) {
if (prod.product === productId) {
return prod.vendor
}
}
}
return 'The product does not exist'
};
console.log('Vendor of product 1 is:', getVendorIdFromGroupedProducts(1));
console.log('Vendor of product 2 is:', getVendorIdFromGroupedProducts(2));
console.log('Vendor of product 3 is:', getVendorIdFromGroupedProducts(3));
console.log('Vendor of product 4 is:', getVendorIdFromGroupedProducts(4));

Pushing array but i needed no quotes

i have problem when im using array into my render apps..
here my code for getting list chat from sqlite..
return new Promise(resolve => {
const listchat = [];
this.initDB()
.then(db => {
db.transaction(tx => {
tx.executeSql(
'SELECT num, text, type, _id,user FROM chat where CAST(user as INT) order by num desc',
[],
).then(([tx, results]) => {
var len = results.rows.length;
for (let i = 0; i < len; i++) {
let row = results.rows.item(i);
var user = {id: 2, avatar: null};
const {num, text, type, _id} = row;
listchat.push({
num,
text,
type,
_id,
user,
});
}
resolve(listchat);
});
})
.then(result => {
this.closeDatabase(db);
})
.catch(err => {
console.log(err);
});
})
});
and i got results from query like this :
[
{
"_id": "6d357f55-5fb8-4fec-ac5f-26cc1df4b98d",
"num": 1,
"text": "here must number 111111111111111111",
"type": null,
"user": {
"avatar": null,
"_id": 2
}
}
]
and what i need is no quotes on "avatar and id"
[
{
"_id": "6d357f55-5fb8-4fec-ac5f-26cc1df4b98d",
"num": 1,
"text": "here must number 111111111111111111",
"type": null,
"user": {
avatar: null,
_id: 2
}
}
]
the problem is on quotes.. someone have solution for this ?

how do i use unique value in object overall

i have this API call, where i want to return the 3 highest and the 3 lowest "totalScore"'s in my object.
this is my function
app.get('/getTopThree', (req, res) => {
let store = Store.find({}, 'name _id', function (error, response) {
}).sort({_id:-1})
store.then(stores => {
let output = {}
function delay() {
return new Promise(function (resolve, reject) {
stores.map(async store => {
var j = 0
await Rate.find({
"storeId": store._id
},
'rate date', function(error, response) {
totalArray = [];
response.filter(function(el) {
totalArray.push(el.rate);
});
sumOfVotes = totalArray.reduce((a, b) => a + b, 0);
totalScore = Math.round(sumOfVotes / totalArray.length * 33.33);
var counts = {};
for (var i = 0; i < totalArray.length; i++) {
var num = totalArray[i];
counts[num] = counts[num] ? counts[num] + 1 : 1;
}
var finalStore = {
"totalScore": totalScore
}
output[j++] = finalStore;
})
})
setTimeout(function () {
resolve(store)
}, 1000)
})
}
delay().then(finalStore => {
console.log(finalStore)
console.log(output)
res.send({
"store": {
"name": finalStore,
"score": output
}
})
})
})
})
and this is my output
{
"store": {
"Klaregade": {
"name": "Klaregade",
"totalScore": 93
},
"Overgade": {
"name": "Overgade",
"totalScore": 67
}
}
}
So what i want is to loop though this object and return the 3 highest as
store: { "highest": output.highest, "lowest": output.lowest" }
can anyone help me with doing that, the problem is every returned value in my object has an unique name in the start ("klaregade" and "overgade")
how do i loop though them and take the higest and lowest value?
Thanks in advance.
Here is an example of working solution:
const output = {
"store": {
"Klaregade": {
"name": "Klaregade",
"totalScore": 93
},
"Overgade": {
"name": "Overgade",
"totalScore": 1
},
"Overgade2": {
"name": "Overgade2",
"totalScore": 412
},
"Overgade3": {
"name": "Overgade3",
"totalScore": 32
},
"Overgade4": {
"name": "Overgade4",
"totalScore": 67
}
}
}
const sorted = Object.values(output.store).sort((a, b) => b.totalScore - a.totalScore);
const store = {
highest: sorted.slice(0, 3),
lowest: sorted.slice(-3),
};
You have to keep in mind that you'll get trimmed results when array.length < 3
I found an solution for this.
what i did was instead of an object i made output to an array. and then pushed finalStore into the array.
and then in .delay.then() i made an for loop on my output
delay().then(finalStore => {
console.log(output)
for (var t = 0; t < output.length; t++) {
output.push({
"name": output[t].name,
"score": output[t].totalScore
})
}
res.send(output)
})
like this

Categories