Deleting records on a database - node.js

I have a logging command which works perfectly, the only issue is I can not finish the off section of the command. I need it to delete both records (guildid, channel) if the guildid matches.
This is what I have tried.
if (args[0] === 'off') {
message.channel.send('Logging turned off!');
const del = db.prepare('DELETE FROM logging WHERE guildid = ?;');
del.run({
guildid: `${message.guild.id}`
});
return;
Looking at the photo, when the args off is ran, I need it to delete the guildid contents (495602...) and channel contents (<#5290...) if the guildid contents matches with the guild the command was ran in.

Your current answer is the incorrect way to use prepared statement. If you use the way you pose, you open yourself up to SQL injection because you aren't interpreting the value you want to use in the statement as a value, you use it as part of the overall statement and then run the statement without parameters. That means that I could potentially provide a value that might not do exactly what you think it will.
For example the following won't do anything,
const $rowid = "3 OR rowid = 4";
const deleteStatement = db.prepare("DELETE FROM lorem WHERE rowid = $rowid");
deleteStatement.run({$rowid});
deleteStatement.finalize();
But this will delete elements with rowid 3 or 4:
const $rowid = "3 OR rowid = 4";
const deleteStatement = db.prepare(`DELETE FROM lorem WHERE rowid = ${$rowid}`);
deleteStatement.run();
deleteStatement.finalize();
Instead, take a look at the sqlite3 documentation here.
You need to actually paramaterize your prepared statement like the following:
const sqlite3 = require("sqlite3").verbose();
const db = new sqlite3.Database(":memory:");
db.serialize(function() {
// Make the table
db.run("CREATE TABLE lorem (info TEXT)");
// Create some dummy data
const insertStatement = db.prepare("INSERT INTO lorem VALUES (?)");
for (let i = 0; i < 5; i++) {
insertStatement.run(`My Data ${i}`);
}
insertStatement.finalize();
// Delete some data
const deleteStatement = db.prepare("DELETE FROM lorem WHERE rowid = $rowid");
deleteStatement.run({
$rowid: 3
});
deleteStatement.finalize();
// Print elements
db.each("SELECT rowid AS id, info FROM lorem", (err, {id, info}) => console.log(`${id}: ${info}`));
});
db.close();

For anyone in the future looking how to do this, this was the answer.
EDIT: Can't mark as an answer until 2 days lol
if (args[0] === 'off') {
message.channel.send('Logging turned off!');
db.prepare(`DELETE FROM logging WHERE guildid = '${message.guild.id}'`).run();
return;

Late to the better-sqlite3 party:
const del = db.prepare('DELETE FROM logging WHERE guildid = ?');
del.run(message.guild.id)

Related

How to get a list of all documents that contain the search string in their document id in Firestore's collection? [duplicate]

Is it possible to query a firestore collection to get all document that starts with a specific string?
I have gone through the documentation but do not find any suitable query for this.
You can but it's tricky. You need to search for documents greater than or equal to the string you want and less than a successor key.
For example, to find documents containing a field 'foo' staring with 'bar' you would query:
db.collection(c)
.where('foo', '>=', 'bar')
.where('foo', '<', 'bas');
This is actually a technique we use in the client implementation for scanning collections of documents matching a path. Our successor key computation is called by a scanner which is looking for all keys starting with the current user id.
same as answered by Gil Gilbert.
Just an enhancement and some sample code.
use String.fromCharCode and String.charCodeAt
var strSearch = "start with text here";
var strlength = strSearch.length;
var strFrontCode = strSearch.slice(0, strlength-1);
var strEndCode = strSearch.slice(strlength-1, strSearch.length);
var startcode = strSearch;
var endcode= strFrontCode + String.fromCharCode(strEndCode.charCodeAt(0) + 1);
then filter code like below.
db.collection(c)
.where('foo', '>=', startcode)
.where('foo', '<', endcode);
Works on any Language and any Unicode.
Warning: all search criteria in firestore is CASE SENSITIVE.
Extending the previous answers with a shorter version:
const text = 'start with text here';
const end = text.replace(/.$/, c => String.fromCharCode(c.charCodeAt(0) + 1));
query
.where('stringField', '>=', text)
.where('stringField', '<', end);
IRL example
async function search(startsWith = '') {
let query = firestore.collection(COLLECTION.CLIENTS);
if (startsWith) {
const end = startsWith.replace(
/.$/, c => String.fromCharCode(c.charCodeAt(0) + 1),
);
query = query
.where('firstName', '>=', startsWith)
.where('firstName', '<', end);
}
const result = await query
.orderBy('firstName')
.get();
return result;
}
If you got here looking for a Dart/Flutter version
Credit to the java answer by Kyo
final strFrontCode = term.substring(0, term.length - 1);
final strEndCode = term.characters.last;
final limit =
strFrontCode + String.fromCharCode(strEndCode.codeUnitAt(0) + 1);
final snap = await FirebaseFirestore.instance
.collection('someCollection')
.where('someField', isGreaterThanOrEqualTo: term)
.where('someField', isLessThan: limit)
.get();
I found this, which works perfectly for startsWith
const q = query(
collection(firebaseApp.db, 'capturedPhotos'),
where('name', '>=', name),
where('name', '<=', name + '\uf8ff')
)
The above are correct! Just wanted to give an updated answer!
var end = s[s.length-1]
val newEnding = ++end
var newString = s
newString.dropLast(1)
newString += newEnding
query
.whereGreaterThanOrEqualTo(key, s)
.whereLessThan(key, newString)
.get()

Best way to organize firebase writes on update trigger

There may be more than one correct answer to this question, but here's my issue: I have a user document in firebase with many fields that can be updated and which interact in different ways. If one field is updated, it may require a change to another field on the backend. Is it better to have a whole bunch of if statements each with their own write action if the condition is met or, or do single write at the end of the function for all the fields that might change. If a field does not change, I would have to write its original value back to itself. That seems clunky, but so does the other option. Am I missing a third option? What is the best practice here?
Here's an example of what I'm talking about. Updating fields one at a time is what I have now, which looks like this:
export const userUpdate = functions.firestore
.document("users/{userID}")
.onUpdate(async (change) => {
const beforeData = change.before.data();
const afterData = change.after.data();
// user levels up and gets more HP
if(beforeData.userLevel != afterData.userLevel){
const newMaxHP = 15 + 5 * afterData.userLevel;
change.after.ref.update({
maxHp: newMaxHP
})
}
//update user rating
if (beforeData.numberOfRatings != afterData.numberOfRatings) {
const newRating = placerRating(beforeData.userRating, beforeData.numberOfRatings, afterData.latestRating);
change.after.ref.update({
userRating: newRating
})
}
//replenish user funds from zero
if (afterData.money == 0){
change.after.ref.update({
money: 20
})
}
If I did it all in a single write, the if statements would assign a value to a variable, but not update the firestore document. Each if statement would include an else statement assigning the variable to the field's original value. There would be a single write at the end like this:
change.after.ref.update({
maxHp: newMaxHP,
userRating: newRating,
money: 20
})
I hope that helps.
[edit to add follow-up question about updating a map value]
#Dharmaraj's answer works great, but I'm struggling to apply it when updating a map value. BTW - I'm using Typescript.
Before using #Dharmaraj's solution, I was doing this:
admin.firestore().collection("users").doc(lastPlayerAttacker).update({
"equipped.weapon.usesLeft": admin.firestore.FieldValue.increment(-1)
});
Using the update object, I'm trying it like this, but I get the error "Object is of type 'unknown'"
const lastPlayerUpdates:{[key:string]:unknown} = {};
lastPlayerUpdates.equipped.weapon.usesLeft = admin.firestore.FieldValue.increment(-1);
admin.firestore().collection("users").doc(lastPlayerAttacker).update(lastPlayerUpdates);
Any advice on how to fix it?
Every time you call update(), you are being charged for 1 write operation. It'll be best to accumulate all updated fields in an object and then update the document only once as it'll be more efficient too. Try refactoring the code as shown below:
export const userUpdate = functions.firestore
.document("users/{userID}")
.onUpdate(async (change) => {
const beforeData = change.before.data();
const afterData = change.after.data();
const updatedData = {};
// user levels up and gets more HP
if (beforeData.userLevel != afterData.userLevel) {
const newMaxHP = 15 + 5 * afterData.userLevel;
updatedData.maxHp = newMaxHP;
}
//update user rating
if (beforeData.numberOfRatings != afterData.numberOfRatings) {
const newRating = placerRating(beforeData.userRating, beforeData.numberOfRatings, afterData.latestRating);
updatedData.userRating = newRating;
}
//replenish user funds from zero
if (afterData.money == 0) {
updatedData.money = 20;
}
await change.after.ref.update(updatedData);
console.log("Data updated");
return null;
})

Queries being run twice on sql.js

I'm using SQLite with sql.js on my project and I have been having some trouble with my implementation. Seems like the queries are being run on the database twice because for the for the INSERT statements I get 2 records in the DB.
The way I do it, I create the SQL and then pass it on to this method (the opts variable contains all of the data being put into the database):
prepareStatementAndCompileResults(db, sql, opts){
const stmt = db.prepare(sql);
const result = stmt.getAsObject(opts);
var rows = [];
if(!this.isEmpty(result)){ // isEmpty is a simple method that checks for empty objects
rows.push(result);
}
while(stmt.step()) {
var row = stmt.getAsObject();
rows.push(row);
}
this.saveToFile(db);
stmt.free();
return rows;
},
Here is a sample SQL INSERT that is being run twice
INSERT OR IGNORE INTO tag_event (tag_id, event_id, unique_string)
VALUES (:tag_id,:event_id, :unique);
Here is what the opts variable would look like for this query:
var opts = {
[':tag_id']: 1,
[':event_id']:1,
[':unique']: '1-1'
}
Because you're pushing it into row 2 time.
// if not empty will add to row
if(!this.isEmpty(result)){ // isEmpty is a simple method that checks for empty objects
rows.push(result);
}
// not sure what step() does but I'm assuming this will also run
while(stmt.step()) {
var row = stmt.getAsObject();
rows.push(row);
}
Verify by using a debugger or just console.log(rows) after the while loop before the save
So, what it turns out I needed to do was bind the variables to the prepared statement before getting the rather than binding them through getAsObject. This is much more efficient. My API response time on a local test went from 785ms to 14.5ms
prepareStatementAndCompileResults(db, sql, opts){
const rows = [];
const stmt = db.prepare(sql);
stmt.bind(opts);
while(stmt.step()) {
var row = stmt.getAsObject();
rows.push(row);
}
this.saveToFile(db);
stmt.free();
return rows;
},

JSX/Photoshop: Selecting Initial History Snapshot - Looking for a shorter way

I have a piece of code that selects the initial state (snapshot) of the active document.
I wonder if there exist a short way to achieve the same goal.
Here is the actual script:
var myDoc = app.activeDocument.name;
var doc = app.activeDocument.historyStates.length;
alert("History States : " + doc);
function firstStep(enabled, withDialog) {
if (enabled != undefined && !enabled)
return;
var dialogMode = (withDialog ? DialogModes.ALL : DialogModes.NO);
var desc1 = new ActionDescriptor();
var ref1 = new ActionReference();
ref1.putName(cTID('SnpS'), myDoc);
desc1.putReference(cTID('null'), ref1);
executeAction(cTID('slct'), desc1, dialogMode);
alert("Selected Initial State");
};
firstStep();
Thanks in advance
Something along these lines should work for you:
docRef.activeHistoryState = docRef.historyStates.getByName('Snapshot 0');

sorting 2d array in node js

From the following code I am getting the appropriate output, but I want the data to come in sorted form i.e the most recent data must come on top. The data must be sorted in descending order according to the difference of current date and 'win_date'.I am new to node.js..earlier in PHP I used to do this by using function array_multisort. Can someone suggest the alternative to do the same in nodejs.
var post=[];
var sql2 ="SELECT `post_id`, `user_id`, `post`, `votes_count`, `win_date` FROM `tb_winning_posts` WHERE `group_id`=?";
connection.query(sql2,[groupId],function(err, result) {
});
for(var i=0;i<result.length;i++)
{
var timeDiff = getTimeDifference(result[i].win_date); //returns diff of cur date & given date in day,hour,min
post.push({"post_id":result[i].post_id,"post":result[i].post,"votes":result[i].votes_count,"date":timeDiff});
}
console.log(post); //getting the data in order as stored in database
Based on the answer https://stackoverflow.com/a/8837511/1903116
post.sort(function(a, b){
var keyA = a.date, keyB = b.date;
if(keyA < keyB) return 1;
if(keyA > keyB) return -1;
return 0;
});
console.log (post);

Resources