Enmap (better-sqlite map) doesnt update until application is restarted - javascript

This is a code Snippet from a discord bot im working on
It needs to load up data from an Enmap, adds information to that Array and then pushes it back in.
It successfully pulls that data, does all the transformations and additions, etc
and successfully pushes that data
however, it cant pull the new data until the program is re-started. It gets saved but
let locationDataArray = await data.get(locationDataKey);
doesnt seem to do pull this new version of the map unless the program is restarted
I am fairly stumped
const Commando = require('discord.js-commando');
const levenshtein = require('fast-levenshtein');
const Enmap = require("enmap");
const data = new Enmap({
name:"locationdata"
});
const settingsMap = new Enmap({
name:"locationSettings"
})
module.exports = class addConnection extends Commando.Command{
constructor(client){
super(client, {
name:"addconnection",
group:"management",
aliases:["connect"],
memberName:"addconnection",
userPermissions:['ADMINISTRATOR'],
description:"adds a connection between two zones",
examples: ['$addconnection <zone1>,<zone2>'],
throttling: {
usages: 1,
duration: 5
},
args:[{
key:'destinations',
prompt:'what are the two areas you want to connect (seperate them with a comma , )',
type:'string'
}]
});
}
async run(msg,args){
//get our guilds Information
let guildID = msg.guild.id;
let locationDataKey = guildID+"_mapData";
let locationDataArray = await data.get(locationDataKey);
//Our Guild Settings
let settingMapKey = guildID+"_setting";
let settings = await settingsMap.get(settingMapKey);
//chiefly out npcRoleID
let npcRole = await msg.guild.roles.get(settings.npcRoleID);
let connectionArray = await args.destinations.toLowerCase().split(",");
for(var i = 0; i < connectionArray.length; i++){
//make sure the item is valid
var distance = levenshtein.get(connectionArray[i], locationDataArray[0].name);
var closestDistance = distance;
var closestWord = locationDataArray[0].name;
for(var j = 0; j < locationDataArray.length; j++){
distance = levenshtein.get(connectionArray[i], locationDataArray[j].name);
if (distance < closestDistance){
closestDistance = distance;
closestWord = locationDataArray[j].name;
}
}
//make sure all the areas are valid and good
if(closestDistance < (closestWord.length/2)){
connectionArray[i] = closestWord;
}
else{
msg.channel.send("those channels don't seem to exist").then( msg => {
msg.delete(10000);
});
return;
}
}
//our array of connections now only contains valid options
//loop over our location data array
for(var i = 0; i< connectionArray.length; i++){
for(var j = 0; j < locationDataArray.length; j++){
//when we hit one of out LDA that has the same name as something in the connectionArray
//stop add the rest of the connection array to its connections
if(locationDataArray[j].name === connectionArray[i]){
for(var k = 0; k < connectionArray.length; k++){
if(locationDataArray[j].name == connectionArray[k]){
}
else{
if(!locationDataArray[j].connections.includes(connectionArray[k])){
await locationDataArray[j].connections.push(connectionArray[k]);
}
//get the role for the connection and the current channel
let role = await msg.guild.roles.find(role => role.name === connectionArray[k]);
let currentChannel = await msg.guild.channels.find(channel => channel.name === locationDataArray[j].channelName);
//the connection can read but not type in its connection
currentChannel.overwritePermissions(
role,
{
'SEND_MESSAGES':false,
'VIEW_CHANNEL':true,
'READ_MESSAGE_HISTORY':true
}
)
data.set(locationDataKey, locationDataArray);
msg.channel.send("Connected "+locationDataArray[j].name+" and "+connectionArray[k]);
}
}
}
}
}
}
}

I know this question is old and I'm hoping you've resolved this issue a long time ago, but for posterity's sake, and because people might find this question later, I'm going to answer it anyways.
The reason this happens is that you're creating a new instance of Enmap in this file, and presumably you have it declared in other files too. This causes a new "client" for Enmap to be created, with its own individual cache, and it's not possible for Enmap to update those instances.
The solution is to either attach your Enmap to a variable you carry around (such as, for example, the Client), or to create a separate node module that you import wherever you need it. Both of those options are described in more details in the Enmap documentation: https://enmap.evie.dev/usage/using-from-multiple-files
Disclaimer: I am the author of Enmap

Related

Alter a object variable inside cloud functions

I am trying to alter a object variable after query in cloud functions, but before sending it to my app, but not sure how to do so! Here is what I am trying to do:
Parse.Cloud.define("getUserData", async(request) => {
// Getting the users data
const userQuery = new Parse.Query("UserData");
userQuery.equalTo("userId", request.params.userId);
const userData = await userQuery.first();
// Getting the groups
const groupQuery = new Parse.Query("GroupMembers");
groupQuery.equalTo("userId", request.params.userId);
groupQuery.include('pointerObject'); // including the pointer object
const groups = await groupQuery.find();
const allGroups = [];
for (let i = 0; i < groups.length; ++i) {
var thisGroup = groups[i].get("pointerObject");
thisGroup.isFavorite = true;
allGroups.push(thisGroup);
}
var returnObject = {
"playerData": userData,
"playerGroups": allGroups
}
const jsonString = JSON.stringify(returnObject);
return jsonString;
});
It is "thisGroup.isFavorite" i am trying to set to true, but when receiving the jsonString, it is still set to false? How do I alter a variable in cloud functions?
Any help is appreciated and thanks in advance :-)
Try with:
thisGroup.set('isFavorite', true);
await thisGroup.save();

TypeORM: most efficient way to create / update / remove multiple ManyToOne relations?

I have a Project entity and a ProjectMember entity, and one project could have many members.
I would like to have one function (using type-graphql) to handle create / update / remove of members. I came up with 2 solutions and wondering which one would be more efficient:
do the save on the many side:
#Mutation((returns) => Boolean)
async modifyProjectMembers(
#Arg("project") projectId: number,
#Arg("members", (type) => [ProjectMemberInput])
members: ProjectMemberInput[]
): Promise<Boolean> {
const project = await Project.findOneOrFail(projectId);
for (let i = 0; i < members.length; i++) {
const { id, remove, ...info } = members[i];
if (!!id) {
// updating existing
const oldMember = await ProjectMember.findOneOrFail(id);
if (remove) {
await oldMember.remove();
continue;
}
Object.assign(oldMember, info);
await oldMember.save();
} else {
// creating new
const newMember = ProjectMember.create(info);
newMember.project = project;
await newMember.save();
}
}
return true;
}
do the save on the one side:
#Mutation((returns) => Boolean)
async modifyProjectMembers(
#Arg("project") projectId: number,
#Arg("members", (type) => [ProjectMemberInput])
members: ProjectMemberInput[]
): Promise<Boolean> {
const project = await Project.findOneOrFail(projectId);
let newMembers = [];
for (let i = 0; i < members.length; i++) {
const { id, remove, ...info } = members[i];
if (!!id) {
if (remove) {
continue;
}
// updating existing
const oldMember = await ProjectMember.findOneOrFail(id);
Object.assign(oldMember, info);
newMembers.push(oldMember);
} else {
// creating new
const newMember = ProjectMember.create(info);
newMembers.push(newMember);
}
}
project.members = newMembers;
await project.save();
return true;
}
For solution 1, as ProjectMembers are saved independently, too many DB access are involved (I think; please point out if I'm wrong);
And for solution 2, I have to provide all existing ProjectMembers which makes the input members big.
So, which one is preferred?
Because every save is associated with:
connecting (if is not connected)
sending query to server
parsing query
inserting row
inserting indexes
closing (options - you will keep connection)
Generally if you use statement with many insert at the same time you should use INSERT with many VALUES. This is faster, sometimes many time faster, because you sending data once, you parse query once, you indexing once etc... so second approach is better
But you can improve speed of first approach by using transactions

ForEach only looping through first item on DataSnapshot

I´m trying to loop through the content of a DataSnapshot and then depending on a condition do some work FOR EACH one of the elements but currently, the ForEach is only doing the work in the first item. The "serverStatus" sometimes is waiting and sometimes in "onCall". When the first item is "onCall" does not go through the rest of the items as I think is supposed to do. Below a snapchot of where I get the information from:
And here is my function:
exports.manageCallRequests = functions.database.ref('/resquests/{userId}').onCreate((snap, context) => {
const event = snap.val();
console.log("function manageCallRequests is being called")
var rootPath = admin.database().ref();
var userOnCall = context.params.userId;
var serversRef = rootPath.child('servers');
var callRequest = event;
var userTime = callRequest["time"];
var waiting= "waiting";
//We first get all the servers in ascending order depending on the last time they were used
var serversSorted = serversRef.orderByChild('lastTimeUsed')
//Gets the children on the "serversSorted" Query
return serversSorted.once("value").then(allServers =>{
//Checks if there is any child
if(allServers.hasChildren()){
allServers.forEach(async function(server) {
//we extract the value from the server variable, this contains all the information
//about each one of the servers we have
var serverInfo = server.val();
var serverKey = server.key;
var serverNumber = serverInfo["serverNumber"];
var serverStatus = serverInfo["serverStatus"];
console.log("server status "+serverStatus)
if(serverStatus === waiting){
const setCallRequest = await serversRef.child(serverKey).child("current").child("callRequest").set(callRequest);
const removeUserOnCall = await rootPath.child("resquests").child(userOnCall).remove();
const setServerStatus = await serversRef.child(serverKey).child("serverStatus").set("onCall");
}
});
}else{
console.log("No servers available")
}
});
});
I had the same behavior because my cloud function was exited before that all iterations were executed in the forEach loop.I get rid of it using this snippet of code:
for (const doc of querySnapshot.docs) {
// Do wathever you want
// for instance:
await doc.ref.update(newData);
}
I found 2 ways of getting this done. The first one is useful if we have a DataSnapshot without any OrderBy* call, in this case, would be:
var allServers = await serversRef.once("value");
for (let serverKey of Object.keys(allServers.val())){
var server = allServers[serverKey];
//Do some work
}
We need to first get the keys of the object to then be able to extract it from within the for loop, as explained here otherwise we´ll get a "TypeError: 'x' is not iterable"
Now the problem with this particular case is that a have a DataSnapshot that was previously sorted at var serversSorted = serversRef.orderByChild('lastTimeUsed') so when we call Object.keys(allServers.val()) the value returned is no longer sorted and that´s where forEach() comes in handy. It guarantees the children of a DataSnapshot will be iterated in their query order as explained here however for some reasons when doing some async work within the forEach loop this seems not to work, that´s why I had to do this:
var serversSorted = serversRef.orderByChild('lastTimeUsed')
var allServers = await serversSorted.once("value");
//Checks if there is any children
if (allServers.hasChildren()) {
//if there is iterate through the event that was passed in containing all
// the servers
var alreadyOnCall = false;
var arrayOfServers = []
var arrayOfKeys = []
allServers.forEach(function(individualServer){
arrayOfKeys.push(individualServer.key)
arrayOfServers.push(individualServer)
})
for (var serveIndex = 0; serveIndex < arrayOfServers.length;serveIndex++){
var serverObj = arrayOfServers[serveIndex]
var serverObject = serverObj.val()
var serverKey = arrayOfKeys[serveIndex]
var serverStatus = serverObject["serverStatus"];
var serverNumber = serverObject["serverNumber"];
console.log("server info "+serverStatus+" "+serverKey);
if (serverStatus === waiting && alreadyOnCall === false) {
const setCallRequest = await serversRef.child(serverKey).child("current").child("callRequest").set(callRequest);
const removeUserOnCall = await rootPath.child("resquests").child(userOnCall).remove();
const setServerStatus = await serversRef.child(serverKey).child("serverStatus").set("onCall");
alreadyOnCall= true
console.log("Call properly set");
}
}
}

How to get only data without any metadata from cloud firestore?

Is it possible to fetch only data from Cloud Firestore?
Right now I am fetching all documents from a collection, then I use loop to get the only data I saved already to Firestore as below:
const data = {};
const snap = await db.collection("rooms").where("ppl","array-contains", myUsername).get({source:"cache"});
for (let i = snap.size -1; i >= 0; i--){
const {room} = snap.docs[i].data();
const snapshot = await db.collection(`rooms/${room}/msgs`).orderBy("dt","desc").limit(50).get({source:"cache"});
const tempMessages = [];
if (len2 > 0) {
for (let k = 0; k < len; k++) {
tempMessages = [...tempMessages, snapshot.docs[k].data()];
}
}
data = { ...data, [room]: tempMessages};
}
setRoomsMessages(data);
Now what I want ???
I want to fetch only and only array of snapshot.docs[k].data() (needed data).
Why ? because I want to fetch needed data and then pass it to state directly. There is, I dont need for two nested loop I only loop the rooms and not for every room messages.

Sequelize transaction stops executing during loop

I am trying to do a transaction that loops through multiple items and inserts them into the database. If i just have 1 item in each array, the code executes fine, it inserts the deployment, the crew and the equipment. However if I have 2 or more items in either equipment or crew, the application just freezes, no error or anything.
The console output looks like this:
Executing (e337b7de-e95f-4d18-a2e9-1216cb8b7d61): START TRANSACTION;
----------------------CREATE DEPLOYMENT---------------
Executing (e337b7de-e95f-4d18-a2e9-1216cb8b7d61): INSERT INTO `deployments` (`id
`,`dateDeployed`,`dateReturned`,`city`,`province`,`country`,`fireName`,`fireNumb
er`,`fireCenter`,`unitNumber`,`comments`,`finalSold`,`status`,`createdAt`,`updat
edAt`,`contractId`,`deploymentTypeId`,`productId`) VALUES (DEFAULT,'2018-03-01',
'Invalid date','1','BC','CAN','1','1','1','1','test','','active','2018-03-08 22:
36:44','2018-03-08 22:36:44','5','1','1');
----------------------CREATE EQUIPEMENT---------------
----------------------CREATE EQUIPEMENT---------------
Executing (default): INSERT INTO `deploymentEquipments` (`createdAt`,`updatedAt`
,`deploymentId`,`equipmentId`) VALUES ('2018-03-08 18:09:31','2018-03-08 22:36:4
4',17,1);
Executing (default): INSERT INTO `deploymentEquipments` (`createdAt`,`updatedAt`
,`deploymentId`,`equipmentId`) VALUES ('2018-03-08 18:09:39','2018-03-08 22:36:4
4',17,2);
My code is like this:
app.post('/deployment', function(req,res,next){
var deployment = req.body;
var crew = req.body.deploymentCrew;
var equipment = req.body.deploymentEquipment;
var deploymentId = "";
//insert new deployment - start transaction, add deployment, get ID, loop through crew, loop through equipment
models.sequelize.transaction(t =>
{
var equipPromises = [];
console.log('----------------------CREATE DEPLOYMENT---------------');
return models.deployment.create(req.body, {transaction: t})
.then(function(newDeployment) {
deploymentId = newDeployment.dataValues.id;
for (var i = 0; i < equipment.length; i++) {
console.log('----------------------CREATE EQUIPEMENT---------------');
var equip = equipment[i];
equip.deploymentId = deploymentId;
equip.equipmentId = equipment[i].id;
var equipPromise = models.deploymentEquipment.create(equip, equipPromises.push(equipPromise));
}
return Promise.all(equipPromises);
})
.then(function() {
console.log('----------------------CREATE STAFF---------------');
var crewPromises = [];
for (var i = 0; i < crew.length; i++) {
var staff = crew[i];
staff.deploymentId = deploymentId;
staff.staffId = crew[i].staff.id;
var crewPromise = models.deploymentCrew.create(staff, crewPromises.push(crewPromise));
}
return Promise.all(crewPromises);
});
}).then(result => {
console.log('deployment added');
res.send(result);
}).catch(err => {
console.log('deployment creation failed');
res.status(401).send({'message':err, 'redirect': '/'});
});
});
Any thought's or ideas why this might be happening would be appreciated.
Thanks
It was actually quite simple, I wasn't adding the transaction into the looped create statements. So now it is like:
//this is the old statement
//var equipPromise = models.deploymentEquipment.create(equip, equipPromises.push(equipPromise));
//this is the new correct way to do it
equipPromises.push(models.deploymentEquipment.create(equip, {transaction:t}));

Categories

Resources