Mention in Discord - sqlite

I ran into a strange problem.
When I take the user id from my database and try to mention the user by it, the id digit is rounded, therefore the mention will not work.
async Run(guild_id) {
return new Promise((resolve, reject) => {
this.participantRepository.GetRandomParticipant(guild_id).then(participant => {
if (participant === null) {
reject("u cant do this");
return;
}
this.gamesRepository.SaveGameInformation(guild_id, participant.id);
this.participantRepository.ScoreParticipant(participant.id);
resolve(Misc.GetRandomElement(resultPhrases) + "<#" + participant.discord_user_id+ ">"); //here is problem!
});
});
}
How it looks like in discord
In database everything is fine
see? in database last three numbers is 784, but in discord they become 800
I'm at a loss and can't figure out what can affect this. Help pls
UPD:
GetRandomParticipant() takes 1 random user from a sqlite table
async GetRandomParticipant(guild_id) {
return this.dbAdapter.get(
"SELECT id, discord_user_id, discord_user_name FROM participants WHERE discord_guild_id = ?1 ORDER BY random() LIMIT 1",
{
1: guild_id
}
).then(result => {
if (result === undefined) {
return null;
}
return result;
});
}
UPD2:
I found one interesting detail, if I put the real type in db, then it also starts to output 800 in the database table. Could it be that I have somehow converted to the real type during program execution?
***800.0

I ABSOLUTELY don't understand how it worked, but when I just went through the options, set
varchar(50) data type for the field discord_user_id - everything worked :/

Related

Firestore query "onSnapshot" called at the same time does not work (

I created an app with Ionic and Firestore that features live chat and I'm having a problem with it.
The conversation is loaded with the method:
refUneConversationMyUserCol.ref.orderBy('date', 'desc').limit(20).get()
To this is added an "onSnapshot" request to retrieve the last message sent live
this.unsubscribeDataUneConversation = refUneConversationMyUserCol.ref.orderBy('date', 'desc').limit(1).onSnapshot(result => {
console.log(result.docs[0].data());
if (this.isCalledBySnapshot === false) {
this.isCalledBySnapshot = true;
} else if (result.docs[0].data().expediteur !== this.authentificationService.uidUserActif) {
const data = result.docs[0].data();
const id = result.docs[0].id;
this.dataUneConversation.push({ id, ...data } as UneConversation);
}
});
It will work perfectly however, when I send a message at the same time (with 2 different accounts talking to each other), I encounter a problem, the onSnapshot is triggered only once and I only receive one message.
I specify that the two messages are sent well in the database, they are only not displayed both during the live session
Do you have any idea why?
Thank you
(Here is the whole method)
async getDataUneConversation(idI: string) {
if (this.loadedDataUneConversation !== idI) {
/* ANCHOR Msg en direct */
this.isCalledBySnapshot = false;
if (this.unsubscribeDataUneConversation) {
await this.unsubscribeDataUneConversation();
}
const refUneConversationMyUserCol = this.afs.collection<User>('users').doc<User>(this.authentificationService.uidUserActif).collection<Conversations>('conversations');
const result = await refUneConversationMyUserCol.ref.orderBy('date', 'desc').limit(20).get();
/* ANCHOR Msg en direct */
this.unsubscribeDataUneConversation = refUneConversationMyUserCol.ref.orderBy('date', 'desc').limit(1).onSnapshot(result => {
console.log(result.docs[0].data());
if (this.isCalledBySnapshot === false) {
this.isCalledBySnapshot = true;
} else if (result.docs[0].data().expediteur !== this.authentificationService.uidUserActif) {
const data = result.docs[0].data();
const id = result.docs[0].id;
this.dataUneConversation.push({ id, ...data } as UneConversation);
}
});
/* ANCHOR Msg en brut */
if (result.docs.length < 20) {
this.infiniteLastUneConversationMax = true;
} else {
this.infiniteLastUneConversationMax = false;
}
this.infiniteLastUneConversation = result.docs[result.docs.length - 1];
this.dataUneConversation = result.docs.map(doc => {
const data = doc.data();
const id = doc.id;
return { id, ...data } as UneConversation;
});
this.dataUneConversation.reverse();
this.loadedDataUneConversation = idI;
}
}
EDIT for working :
this.unsubscribeDataUneConversation = refUneConversationMyUserCol.ref.orderBy('date', 'asc').startAfter(this.dataUneConversation[this.dataUneConversation.length
- 1].date).onSnapshot(result => {
result.docs.forEach(element => {
const data = element.data();
const id = element.id;
if (!this.dataUneConversation.some(e => e.id === element.id)) {
this.dataUneConversation.push({ id, ...data } as UneConversation);
}
});
});
You're limiting live messages to only one last message. In a chat app, you want to listen to all new messages. So the issue is probably in your .limit(1) clause.
But if you do that, I understand that you'll get the whole conversation, with all messages, since the conversation started.
My approach would be like this:
Get the date of the last message from your refUneConversationMyUserCol... conversation loader.
When you do the onSnapshot() to get the last message, do not limit to 1 message, instead, start at a date after the date of the last loaded message.
Since you're ordering by date anyway, this will be an easy fix. Look into "Adding a cursor to your query".
Basically, you'll be saying to Firestore: give me LIVE new messages but start at NOW - and even if there are many messages posted at the same time, you'll get them all, since you're not limiting to 1.
Feel free to ask if this is not clear enough.

Async Await with four nested loops

I am currently trying to return a JSON object array that requires me to do one asynchronous function and then four nested asynchronous map functions in order to populate an array of entities. Basically, each user has an array of orders, each order has an array of items, each item has an array of options and each option has an array of values. I am using loopback4 framework and therefore cannot do res.send once all things have been populated. The function seems to return on the first await, but any await after that, it does not wait on it and instead runs to the end of the function. I have tried using Promises and .thens(), but cannot seem to figure out how to populate each entity fully nested, and then return the array of populated entities. I keep getting an empty array. Below is only one nest of maps, but I cannot get it to even populate up to the first nest and return this, so I decided not to go any further. This is the code:
async getUserOrders2(#param.path.number('id') id: number): Promise<any> {
if ( !this.user) {
throw new HttpErrors.Unauthorized(AuthErrorKeys.ClientInvalid);
}
else if (this.user.id != id) {
throw new HttpErrors.Unauthorized(AuthErrorKeys.ClientInvalid);
}
else {
let restaurantId = this.user.restaurantId
let orderFrameArray = new Array<OrderFrame>()
return this.restaurantRepository.orders(restaurantId as string).find()
.then(async orders => {
orders.map(async (val, key)=> {
let orderFrame = new OrderFrame(val)
orderFrame.itemArray = await this.orderRepository.orderItems(val.id).find()
orderFrameArray.push(orderFrame)
})
orderFrameArray = await Promise.all(orderFrameArray)
return orderFrameArray
})
}
}
The function is returning before the orderFrameArray has been populated. I need four nested map loops and this first one is not working, so I am not sure how to do the rest. Any help would be extremely appreciated.
Based on #Tomalaks solution I tried the following, but its still only returning the top level array and nothing is nested:
async getUserOrders2(#param.path.number('id') id: number): Promise<any> {
if ( !this.user) {
throw new HttpErrors.Unauthorized(AuthErrorKeys.ClientInvalid);
}
else if (this.user.id != id) {
throw new HttpErrors.Unauthorized(AuthErrorKeys.ClientInvalid);
}
else {
let restaurantId = this.user.restaurantId
let orderFrameArray = new Array<OrderFrame>()
return this.restaurantRepository.orders(restaurantId as string).find()
.then(orders => {Promise.all(orders.map(
order => {
let orderFrame = new OrderFrame(order)
orderFrame.itemArray = new Array<Item>()
this.orderRepository.orderItems(order.id).find()
.then(orderItems => Promise.all(orderItems.map(
orderItem => {
let itemFrame = new Item(orderItem)
itemFrame.options = new Array<Option>()
this.orderItemRepository.orderItemOptions(orderItem.id).find()
.then(orderItemOptions => Promise.all(orderItemOptions.map(
orderItemOption => {
let optionFrame = new Option(orderItemOption)
optionFrame.values = new Array<Value>()
this.orderItemOptionRepository.orderItemOptionValues(orderItemOption.id).find()
.then(orderItemOptionValues => Promise.all(orderItemOptionValues.map(
orderItemOptionValue => {
let valueFrame = new Value(orderItemOptionValue)
optionFrame.values.push(valueFrame)})))
itemFrame.options.push(optionFrame)})))
orderFrame.itemArray.push(itemFrame)})))
orderFrameArray.push(orderFrame)}))
return orderFrameArray})
}
}
I apologize for the formatting I wasn't sure how best to format it. Is there something else I'm doing wrong?
Thanks to everyone for their response. The answer that was posted by #Tomalak was correct. I just had to surround the entire function in brackets, and put a .then to return the populated entity I had made
You only need to use async when you are using await in the same function. If there's await in a nested function, the parent function does not need async.
However, in your case, there is no function that should be made async in the first place.
There is no benefit in awaiting any results in your function, because no code inside depends on any intermediary result. Just return the promises as you get them.
There's no need for intermediary result variables like orderFrameArray, you're making things harder than they are with your approach of awaiting individual orders and pushing them to a top-level variable.
Using await in a loop like you do inside your .map() call is bad for performance. You are basically serializing database access this way – the next query will only be sent after the current one has returned. This kind of daisy-chaining nullifies the database's ability to process multiple concurrent requests.
getUserOrders2 is not Promise<any>, it's Promise<Array<OrderFrame>>.
throw terminates the function anyway, you can do multiple checks for error conditions without using else if. This reduces nesting.
So a fully asynchronous function would look like this:
getUserOrders2(#param.path.number('id') id: number): Promise<Array<OrderFrame>> {
if (!this.user) throw new HttpErrors.Unauthorized(AuthErrorKeys.ClientInvalid);
if (this.user.id != id) throw new HttpErrors.Unauthorized(AuthErrorKeys.ClientInvalid);
return this.restaurantRepository
.orders(this.user.restaurantId).find().then(
orders => Promise.all(orders.map(
order => this.orderRepository.orderItems(order.id).find().then(
order => new OrderFrame(order)
)
))
);
}
The async/await equivalent of this function would be more complex.
You then would await the result in the calling code, as you would have to do anyway:
async test() {
const orders = await foo.getUserOrders2(someUserId);
// ...
}
// or
test() {
foo.getUserOrders2(someUserId).then(orders => {
// ...
});
}

Firestore get value of Field.increment after update without reading the document data

Is there a way to retrieve the updated value of a document field updated using firestore.FieldValue.increment without asking for the document?
var countersRef = db.collection('system').doc('counters');
await countersRef.update({
nextOrderCode: firebase.firestore.FieldValue.increment(1)
});
// Get the updated nextOrderCode without asking for the document data?
This is not cost related, but for reliability. For example if I want to create a code that increases for each order, there is no guaranty that if >= 2 orders happen at the same time, will have different codes if I read the incremental value right after the doc update resolves, because if >= 2 writes happen before the first read, then at least 2 docs will have the same code even if the nextOrderCode will have proper advance increment.
Update
Possible now, check other answer.
It's not possible. You will have to read the document after the update if you want to know the value.
If you need to control the value of the number to prevent it from being invalid, you will have to use a transaction instead to make sure that the increment will not write an invalid value. FieldValue.increment() would not be a good choice for this case.
We can do it by using Firestore Transactions, like incremental worked before Field.increment feature:
try {
const orderCodesRef = admin.firestore().doc('system/counters/order/codes');
let orderCode = null;
await admin.firestore().runTransaction(async transaction => {
const orderCodesDoc = await transaction.get(orderCodesRef);
if(!orderCodesDoc.exists) {
throw { reason: 'no-order-codes-doc' };
}
let { next } = orderCodesDoc.data();
orderCode = next++;
transaction.update(orderCodesRef, { next });
});
if(orderCode !== null) {
newOrder.code = orderCode;
const orderRef = await admin.firestore().collection('orders').add(newOrder);
return success({ orderId: orderRef.id });
} else {
return fail('no-order-code-result');
}
} catch(error) {
console.error('commitOrder::ERROR', error);
throw errors.CantWriteDatabase({ error });
}
Had the same question and looks like Firestore Python client
doc_ref.update() returns WriteResult that has transform_results attribute with the updated field value

Firebase denormalization data consistency issue

I'm currently using Ionic CLI 3.19 with Cordova CLI 7.1.0 (#ionic-app-script 3.1.4)
The problem that I’m currently facing with is, I should update friends node values simultaneously every time the related data get changed from elsewhere. I’d like to clarify my objective with some screenshots to make it more clear.
As you can see from the image below, each child node consists of a user array that has a user id as a key of friends node. The reason why I store as an array is because each user could have many friends.
In this example, Jeff Kim has one friend which is John Doe vice versa.
When data in users node gets changed for some reason, I want the related data in friends node also want them to be updated too.
For example, when Jeff Kim changed his profile photo or statusMessage all the same uid that reside in friends node which matches with Jeff Kim’s uid need to be updated based on what user has changed.
user-service.ts
constructor(private afAuth: AngularFireAuth, private afDB: AngularFireDatabase,){
this.afAuth.authState.do(user => {
this.authState = user;
if (user) {
this.updateOnConnect();
this.updateOnDisconnect();
}
}).subscribe();
}
sendFriendRequest(recipient: string, sender: User) {
let senderInfo = {
uid: sender.uid,
displayName: sender.displayName,
photoURL: sender.photoURL,
statusMessage: sender.statusMessage,
currentActiveStatus: sender.currentActiveStatus,
username: sender.username,
email: sender.email,
timestamp: Date.now(),
message: 'wants to be friend with you.'
}
return new Promise((resolve, reject) => {
this.afDB.list(`friend-requests/${recipient}`).push(senderInfo).then(() => {
resolve({'status': true, 'message': 'Friend request has sent.'});
}, error => reject({'status': false, 'message': error}));
});
}
fetchFriendRequest() {
return this.afDB.list(`friend-requests/${this.currentUserId}`).valueChanges();
}
acceptFriendRequest(sender: User, user: User) {
let acceptedUserInfo = {
uid: sender.uid,
displayName: sender.displayName,
photoURL: sender.photoURL,
statusMessage: sender.statusMessage,
currentActiveStatus: sender.currentActiveStatus,
username: sender.username,
email: sender.email
}
this.afDB.list(`friends/${sender.uid}`).push(user);
this.afDB.list(`friends/${this.currentUserId}`).push(acceptedUserI
this.removeCompletedFriendRequest(sender.uid);
}
According to this clip that I've just watched, it looks like I did something called Denormalization and the solution might be using Multi-path updates to change data with consistency. Data consistency with Multi-path updates. However, it's kinda tricky to fully understand and start writing some code.
I've done some sort of practice to make sure update data in multiple locations without calling .update method twice.
// I have changed updateUsername method from the code A to code B
// Code A
updateUsername(username: string) {
let data = {};
data[username] = this.currentUserId;
this.afDB.object(`users/${this.currentUserId}`).update({'username': username});
this.afDB.object(`usernames`).update(data);
}
// Code B
updateUsername(username: string) {
const ref = firebase.database().ref();
let updateUsername = {};
updateUsername[`usernames/${username}`] = this.currentUserId;
updateUsername[`users/${this.currentUserId}/username`] = username;
ref.update(updateUsername);
}
I'm not trying to say this is a perfect code. But I've tried to figure this out on my own and here's what I've done so far.
Assume that I'm currently signed in as Jeff.
When I run this code all the associated data with Jeff in friends node gets changed, as well as Jeff's data in users node gets updated simultaneously.
The code needs to be improved by other firebase experts and also should be tested on a real test code.
According to the following thread, once('value' (which is, in general, a bad idea for optimal performance with Firebase). I should find out why this is bad.
friend.ts
getFriendList() {
const subscription = this.userService.getMyFriendList().subscribe((users: any) => {
users.map(u => {
this.userService.testMultiPathStatusMessageUpdate({uid: u.uid, statusMessage: 'Learning Firebase:)'});
});
this.friends = users;
console.log("FRIEND LIST#", users);
});
this.subscription.add(subscription);
}
user-service.ts
testMultiPathStatusMessageUpdate({uid, statusMessage}) {
if (uid === null || uid === undefined)
return;
const rootRef = firebase.database().ref();
const query = rootRef.child(`friends/${uid}`).orderByChild('uid').equalTo(this.currentUserId);
return query.once('value').then(snapshot => {
let key = Object.keys(snapshot.val());
let updates = {};
console.log("key:", key);
key.forEach(key => {
console.log("checking..", key);
updates[`friends/${uid}/${key}/statusMessage`] = statusMessage;
});
updates[`users/${this.currentUserId}/statusMessage`] = statusMessage;
return rootRef.update(updates);
});
}
The code below works fine when updating status to online but not offline.
I don't think it's the correct approach.
updateOnConnect() {
return this.afDB.object('.info/connected').valueChanges()
.do(connected => {
let status = connected ? 'online' : 'offline'
this.updateCurrentUserActiveStatusTo(status)
this.testMultiPathStatusUpdate(status)
})
.subscribe()
}
updateOnDisconnect() {
firebase.database().ref().child(`users/${this.currentUserId}`)
.onDisconnect()
.update({currentActiveStatus: 'offline'});
this.testMultiPathStatusUpdate('offline');
}
private statusUpdate(uid, status) {
if (uid === null || uid === undefined)
return;
let rootRef = firebase.database().ref();
let query = rootRef.child(`friends/${uid}`).orderByChild('uid').equalTo(this.currentUserId);
return query.once('value').then(snapshot => {
let key = Object.keys(snapshot.val());
let updates = {};
key.forEach(key => {
console.log("checking..", key);
console.log("STATUS:", status);
updates[`friends/${uid}/${key}/currentActiveStatus`] = status;
});
return rootRef.update(updates);
});
}
testMultiPathStatusUpdate(status: string) {
this.afDB.list(`friends/${this.currentUserId}`).valueChanges()
.subscribe((users: any) => {
users.map(u => {
console.log("service U", u.uid);
this.statusUpdate(u.uid, status);
})
})
}
It does show offline in the console, but the changes do not appear in Firebase database.
Is there anyone who could help me? :(
I think you are right doing this denormalization, and your multi-path updates is in the right direction. But assuming several users can have several friends, I miss a loop in friends' table.
You should have tables users, friends and a userFriend. The last table is like a shortcut to find user inside friends, whitout it you need to iterate every friend to find which the user that needs to be updated.
I did a different approach in my first_app_example [angular 4 + firebase]. I removed the process from client and added it into server via onUpdate() in Cloud functions.
In the code bellow when user changes his name cloud function executes and update name in every review that the user already wrote. In my case client-side does not know about denormalization.
//Executed when user.name changes
exports.changeUserNameEvent = functions.database.ref('/users/{userID}/name').onUpdate(event =>{
let eventSnapshot = event.data;
let userID = event.params.userID;
let newValue = eventSnapshot.val();
let previousValue = eventSnapshot.previous.exists() ? eventSnapshot.previous.val() : '';
console.log(`[changeUserNameEvent] ${userID} |from: ${previousValue} to: ${newValue}`);
let userReviews = eventSnapshot.ref.root.child(`/users/${userID}/reviews/`);
let updateTask = userReviews.once('value', snap => {
let reviewIDs = Object.keys(snap.val());
let updates = {};
reviewIDs.forEach(key => { // <---- note that I loop in review. You should loop in your userFriend table
updates[`/reviews/${key}/ownerName`] = newValue;
});
return eventSnapshot.ref.root.update(updates);
});
return updateTask;
});
EDIT
Q: I structured friends node correctly or not
I prefer to replicate (denormalize) only the information that I need more often. Following this idea, you should just replicate 'userName' and 'photoURL' for example. You can aways access all friends' information in two steps:
let friends: string[];
for each friend in usrService.getFriend(userID)
friends.push(usrService.getUser(friend))
Q: you mean I should create a Lookup table?
The clip mentioned in your question, David East gave us an example how to denormalize. Originaly he has users and events. And in denormalization he creates eventAttendees that is like a vlookup (like you sad).
Q: Could you please give me an example?
Sure. I removed some user's information and add an extra field friendshipTypes
users
xxsxaxacdadID1
currentActiveStatus: online
email: zinzzkak#gmail.com
gender: Male
displayName: Jeff Kim
photoURL: https://firebase....
...
trteretteteeID2
currentActiveStatus: online
email: hahehahaheha#gmail.com
gender: Male
displayName: Joeh Doe
photoURL: https://firebase....
...
friends
xxsxaxacdadID1
trteretteteeID2
friendshipTypes: bestFriend //<--- extra information
displayName: Jeff Kim
photoURL: https://firebase....
trteretteteeID2
xxsxaxacdadID1
friendshipTypes: justAfriend //<--- extra information
displayName: John Doe
photoURL: https://firebase....
userfriends
xxsxaxacdadID1
trteretteteeID2: true
hgjkhgkhgjhgID3: true
trteretteteeID2
trteretteteeID2: true

Convert multiple FirebaseObjectObservable into a FirebaseListObservable

The snippet is part of a bigger code. Generally I have an object on firebase database called users (it's not a list). I need to get some of them and then convert into Array or FirebaseListObservable.
Observable.merge(...[
this.db.object('users/user1'),
this.db.object('users/user2'),
this.db.object('users/user3'),
this.db.object('users/user4'),
this.db.object('users/user5')
]).subscribe(user => {
console.log(user);
});
This return me user by user, however I need to get all users together. I need to do it in sync. Any ideas?
I have a similar problem and this is how I'm solving it for the moment:
getUsers(): Observable<any> {
let observables = [];
for (let user of users) {
observables.push(this.db.object(user))
}
return Observable.combineLatest(...observables, (...results) => { return results });
}
What I did not manage to do is to return it as FirebaseListObservable.

Resources