I am creating chat functionality in my web app. I am using mysql to store data in a table with a userId and fromId columns. The userId is the message recepient while fromId is the sender.
So, what comes from the GET /messages endpoint looks something like this:
[
{
"id": 10,
"userId": 2,
"fromId": 4,
"message": "messsage",
"read": 0,
"createdAt": "2022-02-22T11:05:54.000Z",
"updatedAt": "2022-02-22T11:05:54.000Z",
"user.first": "Fourth",
"user.last": "NNe",
"user.email": "hello3#feathersjs.com",
"user.phone": null,
"user.location": null,
"user.isVerified": 0,
"user.roles": "user"
},
{
"id": 9,
"userId": 4,
"fromId": 2,
"message": "messsage",
"read": 0,
"createdAt": "2022-02-22T08:32:01.000Z",
"updatedAt": "2022-02-22T08:32:01.000Z",
"user.first": "Jane",
"user.last": "Doe",
"user.email": "hello1#feathersjs.com",
"user.phone": null,
"user.location": null,
"user.isVerified": 0,
"user.roles": "user"
},
{
"id": 8,
"userId": 2,
"fromId": 3,
"message": "messsage",
"read": 1,
"createdAt": "2022-02-22T08:31:52.000Z",
"updatedAt": "2022-02-22T08:31:52.000Z",
"user.first": "Third",
"user.last": "Tatu",
"user.email": "hello2#feathersjs.com",
"user.phone": null,
"user.location": null,
"user.isVerified": 0,
"user.roles": "user"
},
{
"id": 7,
"userId": 2,
"fromId": 4,
"message": "messsage",
"read": 1,
"createdAt": "2022-02-22T08:31:48.000Z",
"updatedAt": "2022-02-22T08:31:48.000Z",
"user.first": "Fourth",
"user.last": "NNe",
"user.email": "hello3#feathersjs.com",
"user.phone": null,
"user.location": null,
"user.isVerified": 0,
"user.roles": "user"
},
{
"id": 6,
"userId": 3,
"fromId": 2,
"message": "messsage",
"read": 0,
"createdAt": "2022-02-22T07:29:49.000Z",
"updatedAt": "2022-02-22T07:29:49.000Z",
"user.first": "Jane",
"user.last": "Doe",
"user.email": "hello1#feathersjs.com",
"user.phone": null,
"user.location": null,
"user.isVerified": 0,
"user.roles": "user"
},
{
"id": 5,
"userId": 2,
"fromId": 1,
"message": "messsage",
"read": 0,
"createdAt": "2022-02-22T07:29:48.000Z",
"updatedAt": "2022-02-22T07:29:48.000Z",
"user.first": "Joseph",
"user.last": "Mungai",
"user.email": "hello#feathersjs.com",
"user.phone": null,
"user.location": null,
"user.isVerified": 0,
"user.roles": "admin"
},
{
"id": 4,
"userId": 1,
"fromId": 2,
"message": "messsage",
"read": 0,
"createdAt": "2022-02-22T07:20:37.000Z",
"updatedAt": "2022-02-22T07:20:37.000Z",
"user.first": "Jane",
"user.last": "Doe",
"user.email": "hello1#feathersjs.com",
"user.phone": null,
"user.location": null,
"user.isVerified": 0,
"user.roles": "user"
},
{
"id": 2,
"userId": 2,
"fromId": 1,
"message": "If you have any questions or problems, you can ask them here.",
"read": 0,
"createdAt": "2022-02-22T07:16:04.000Z",
"updatedAt": "2022-02-22T07:16:04.000Z",
"user.first": "Joseph",
"user.last": "Mungai",
"user.email": "hello#feathersjs.com",
"user.phone": null,
"user.location": null,
"user.isVerified": 0,
"user.roles": "admin"
}
]
Now what I can't figure out is how to manipulate the array response to get different arrays of each conversation. I think I should use array.reduce method but I can't wrap my head around how to do that.
As shown in the image below for the UI, I would like the column marked 'names' in red, to have the different arrays of each conversation. And the column marked 'chat' in blue to have the contents of each of these conversations.
I am pretty sure this is doable, please help.
So you would need an array of messages which have both the sender Id and the recipient Id, or I think maybe a better approach could be to identify the objects with a conversationId which would make things a lot easier and you could group the results directly from sql. You could also group the results in your query by userId and fromId but that's out of the scope of this question. Anyway, this is what you are looking for
const mySegmentedArray = messageArray.reduce((finalArray, message)=>{
const addedConversation = finalArray.find(ma=>
((ma.user1 == message.userId && ma.user2 == message.fromId) ||
(ma.user1 == message.fromId && ma.user2 == message.userId))
if(addedConversation){
addedConversation.messages = [...addedConversation.messages, message]
}else{
finalArray = [...finalArray,
{
user1: message.userId,
user2: message.fromId,
messages: [message]}
}]
}
return finalArray
}, [])
Instead of a user having to fetch ALL the messages from the database for opening only 1 chat, I suggest to change the API. The /messages Api should accept userId parameter (the active user which is using the app), and fromId (the userId of a user you want to show messages from.
Whenever a user now clicks on any chat, you can do a GET request to server which would then return one single array of messages which you can directly display. No complex data manipulation, more maintainable code!.
Using SQL on serverside:
-- Pseudocode..
Select * from messageTable where userid=${userId} and fromId=${fromId};
The db is designed for receiving/filtering large amounts of data very fast and allows you to easily build up the data structure you like and is probably faster than doing it in javscript/node
Scaling
Think of a user having 100+ chats, it would be large amounts of data to fetch for him before being able to even open 1 chat! You could for example load the 5 "last active" chats on app start and then lazy load the other chats in the background. (or only load them on click.)
Later on you can finetune your API and provide an endpoint to maybe fetch a few chats at once but for beginning don't bother to make 1 Request per chat!.
You may also want to fetch only the latest x messages, when the app would be used multiple years, the data amount could become quiet large.
Related
If anyway to select columns from relation without using querybuilder ???
This is my current query:
const areas = await this.areaRepository.find({
where: { ...isActive },
relations: ["division"],
});
Output :
{
"id": 1,
"version": 9,
"isActive": 1,
"createdBy": null,
"updatedBy": 1,
"createAt": "2022-04-18T15:42:12.000Z",
"updatedAt": "2022-09-23T11:04:53.000Z",
"name": "Dhaka",
"division": {
"id": 3,
"version": 1,
"isActive": 1,
"createdBy": null,
"updatedBy": null,
"createAt": "2022-04-18T15:42:00.000Z",
"updatedAt": "2022-04-18T15:42:00.000Z",
"name": "Dhaka"
}
},
But is there anything like:
const areas = await this.areaRepository.find({
select: ['id','division.id division_id']
where: { ...isActive },
relations: ["division"],
});
And the output will be:
{
"id": 1,
"division_id": 3
}
This is a duplicated question of: How to select fields from joined table using TypeORM repository?
You can check the answer here. Also this depends on which TypeOrm version you are currently using, as far as I know on version under 0.3 this doesn't work.
I get an array from the API as follows:
[ { "URL": "", "avatar": "", "characterID": 853, "creationDate": "2022-01-22T17:12:42", "description": "description", "foreignSuggestionID": 0, "id": 5, "seriesID": 0, "type": "IMAGE", "userID": 168314031248113660 } ]
However I expect
[ { "URL": "", "avatar": "", "characterID": 853, "creationDate": "2022-01-22T17:12:42", "description": "description", "foreignSuggestionID": 0, "id": 5, "seriesID": 0, "type": "IMAGE", "userID": 168314031248113664} ]
The userID gets transformed in the request by Javascript, since when I use transformResponse: data => data I get the correct ID.
This gets me to the conclusion that it cannot contain the large number in Javascript. My question here would be, is there a way to make sure that a specific value within the json array is seen as a bigint or can I convert the column into a string on receiving the response?
I use axios and my code is as follows:
this.$axios.get(url, {
withCredentials: 'true'
})
.then(response => {
this.data = response.data
})
So this is my case:
In my angular 8 application i create invoices. this is an invoice object:
{
"_id": {
"$oid": "5ea9ad58f65d8d49841362bd"
},
"details": [
{
"_id": "5ea1eff27a1fcb29c4e7d1b6",
"Client": "test",
"km": 88,
"Subject": "test",
"Location": "test",
"StartTime": "2020-04-27T09:00:00.000Z",
"EndTime": "2020-04-27T17:00:00.000Z",
"IsAllDay": false,
"StartTimezone": null,
"EndTimezone": null,
"Description": "oekfokef",
"RecurrenceRule": "FREQ=DAILY;INTERVAL=1;COUNT=5;",
"Id": 2,
"CreatedBy": "bob"
},
{
"_id": "5ea1f36297a9a315bc8ed078",
"Client": "test",
"km": 88,
"Subject": "ewfwefwe",
"Location": "fwefwefwefewfwefwef",
"StartTime": "2020-04-20T09:00:00.000Z",
"EndTime": "2020-04-20T17:00:00.000Z",
"IsAllDay": false,
"StartTimezone": null,
"EndTimezone": null,
"Description": "wefwefewfwef",
"RecurrenceRule": "FREQ=DAILY;INTERVAL=1;COUNT=5;",
"Id": 3,
"CreatedBy": "bob"
},
{
"_id": "5ea1f38d97a9a315bc8ed083",
"Client": "test2",
"km": 38,
"Subject": "test",
"Location": "test",
"StartTime": "2020-05-04T09:00:00.000Z",
"EndTime": "2020-05-04T16:00:00.000Z",
"IsAllDay": false,
"StartTimezone": null,
"EndTimezone": null,
"Description": "test",
"RecurrenceRule": "FREQ=DAILY;INTERVAL=1;COUNT=5;",
"Id": 4,
"CreatedBy": "bob"
}
],
"client": "Robin",
"hoursWorked": 75,
"kmsTravelled": 880,
"invoiceDate": "2020-04-29T16:37:44.948Z",
"paid": "false",
"subTotal": 3917.2,
"travelexpenses": 167.2,
"tax": 879.7,
"hoursCosts": 3750,
"total": 4796.9,
"createdBy": "bob",
"__v": 0
}
But during the use of the application, certain properties change value, like hoursWorked, total, kmTravelled, hourCosts and details. The updated objects get printed to the console. So whenever the user opens the component, i want it to post the whole object, but if an invoice with that Client name alread exists , only update those properties of each invoice per client.
the updated object is this.invoice:
this.invoice = {
client: element.Client,
hourCosts: (element.difference)*this.Client.price,
hoursWorked: (element.difference),
kmsTravelled: element.km,
travelexpenses: this.Client.kmPrice* element.km,
subTotal: (this.Client.kmPrice* element.km) + ((element.difference)*this.Client.price),
total: ((this.Client.kmPrice* element.km) + ((element.difference)*this.Client.price)) + ((this.Client.kmPrice* element.km)+((element.difference)*this.Client.price) * this.tax/100),
createdBy: this.userName,
details: this.details
}
So how do I go about this? Sorry for a quite vague question, but i stuck with this quite a while now. If you need more info please let me know
You could query by 'id' to search for specific values that have changed with something like: db.getCollection('hourCosts').find({_id:'5ea9ad58f65d8d49841362bd'})
Searching in a collection by id with .find() will be efficient. There's a really helpful mongoose doc page here.
Also, just clarifying, you're posting the full customer schema if the name is unique, otherwise updating the relevant invoices by 'client', is that correct? The doc page linked should be useful for most query types and you can create additional mongoose schemas for some added granularity in what content you change.
you can use mongoDB $set in the update command, example:
db.city.update({_id:ObjectId("584a13d5b65761be678d4dd4")}, {$set: {"citiName":"Jakarta Pusat"}})
make sure you are passing the _id as ObjectId
https://www.djamware.com/post/58578ab880aca715e80d3caf/mongodb-simple-update-document-example
I have an asp.net WebApi project, I wrote a linq to sql code to return two outer and one inner nested json. However, what I have been able to write is three nested json and when second array don't have data, it blocks the last array even if the last array has data.
I have used DefaultIfEmpty() but to no avail. I have tried creating different queries and merge them but to not avail
Here's my code:
Public Function PostAction(<FromBody()> data As Data)
Dim q = (From x In ctx.CommentsViews
Where x.PostID = getPostID
Order By x.CommentDate Descending
Select New With {x.PostID, x.CommentID, x.Username, x.CommentDate, x.Comment, x.LikesCount, x.RepliesCount, .replies = From y In ctx.ReplysViews.DefaultIfEmpty() Where y.CommentID = x.CommentID Order By y.ReplyDate Descending Select New With {y.ReplyID, y.CommentID, y.Username, y.Fullname, y.Picture, y.Reply, y.LikesCount, y.ReplyDate, .likes = From l In ctx.LikesTbls Where l.PostID = x.CommentID And l.Username = getUser Select New With {l.Username}}}).ToList().Take(3)
Return q
End Function
Below is my json result using Postman
[
{
"PostID": "108",
"CommentID": 81,
"Fullname": "David Razak",
"Username": "550",
"Picture": "https://localhost:44363/images/C5810018-733D-461E-82A7-487048494049.jpeg",
"CommentDate": "2019-08-27T21:55:52.91",
"Comment": "She must be up to something",
"LikesCount": 2,
"RepliesCount": 0,
"replies": []
},
{
"PostID": "108",
"CommentID": 80,
"Fullname": "David Razak",
"Username": "550",
"Picture": "https://localhost:44363/images/C5810018-733D-461E-82A7-487048494049.jpeg",
"CommentDate": "2019-08-27T21:53:59.753",
"Comment": "Why is she closing her eyes?",
"LikesCount": 0,
"RepliesCount": 1,
"replies": [
{
"ReplyID": 1066,
"CommentID": 80,
"Username": "550",
"Fullname": "David Razak",
"Picture": "https://localhost:44363/images/C5810018-733D-461E-82A7-487048494049.jpeg",
"Reply": null,
"LikesCount": 0,
"ReplyDate": "2019-08-28T12:09:46.243",
"likes": []
}
]
}
]
In the first response, the "likes":[] is not showing because replies has no data. On the second response, the "likes":[] is showing because replies has data
Currently, this works and doesn't give my error while running but my text editor is giving me an error that says property 'categories' does not exist on type 'CategoryInterface[]' (on the line where response.categories is assigned to variable) so I'm not sure if I'm doing things right.
public categories: CategoryInterface[];
.subscribe((response: CategoryInterface[]) => {
this.categories = response.categories;
console.log(this.categories);
});
My backend returns this:
{
"categories": [
{
"categoryId": 1,
"name": "Important",
"description": "This category is important.",
"order": 1,
"createdBy": null,
"createdAt": "2017-11-25 12:09:04",
"updatedBy": null,
"updatedAt": "2018-01-17 23:53:25",
"categoryBoards": [
{
"categoryBoardId": 1,
"categoryId": 1,
"name": "Announcements",
"description": null,
"order": 2,
"createdBy": null,
"createdAt": "2017-11-25 12:09:49",
"updatedBy": null,
"updatedAt": "2018-01-18 00:09:02"
},
{
"categoryBoardId": 23,
"categoryId": 1,
"name": "Rules",
"description": null,
"order": 1,
"createdBy": null,
"createdAt": "2018-01-18 00:08:57",
"updatedBy": null,
"updatedAt": "2018-01-19 00:05:51"
}
]
}
]
}
You are trying to cast your api response to an array of CategoryInterface which is not the case, you better use your subscribe method like this:
.subscribe((response: any) => {
this.categories = <CategoryInterface[]> response.categories;
console.log(this.categories);
});
It's the your api response categories which needs to be casted to CategoryInterface[]
Bonus: The angular style-guide notice that you need to declare classes instead of interfaces and you don't have to suffix the class name with Interface, so just name your CategoryInterface to Category.
You get the error because you declare response as a CategoryInterface[], but response.categories is actually the CategoryInterface[]. response is just a wrapper around the array. All the types are stripped out when the typescript is converted to javascript, which is why it works fine at runtime.