I am trying to create a basic chat app, only contains private messaging.
Background
I have two users, user A and user B.
users/A
- name: "John"
users/B
- name: "Mary"
When user A sends a message to user B, I create a conversation between those two. In this case conversation id would be A_B. Read more here.
user-conversations/A/B # A for user A, B for user B
- conversationId: A_B
user-conversations/B/A # B for user B, A for user A
- conversationId: A_B
And I have user-conversations collections. I've created this structure because when user A opens the app, I want to show his conversations with user B name and image. With this structure, I just need to have an userId, then I can retrieve his/her conversations with other user's informations.
And of course I have conversations collection which includes messages.
conversations/A_B/messages # A_B is conversationId between user A and user B
- text: "hey"
- sender: "A"
Question
So, how can I improve my database structure, specifically user-conversations collection. Because, when someone wants to read or write to conversations/A_B, I want be sure that user is allowed to write and I could achieve that only if I have conversationId associated with user's.
Related
I am about to create a 1 to 1 chat app using firestore. Features are as follow :
User can see a list of contacts that he chat with before.
Number of unseen messages in a chat room.
The last message and time.
I've thought of my Message model class as follow:
messageId (UniqueId),
senderId
receiverId
message
time
isSeen (bool, default value false)
Now lets say user1 & user2 made a conversation. I'm thinking of creating a separate root collection (chats) for chats in the following way :
chats(col) --> user1 --> chats(col) --> chatRoomId --> messages(col) --> messageId1
--> messageId2
--> messageId3
--> user2 --> chats(col) --> chatRoomId --> messages(col) --> messageId1
--> messageId2
--> messageId3
The problem with this approach is that - there will be duplicate of data like same message to be stored for both the users separately. Also it'll be troublesome to update a message, as the message need to be updated in both location.
So keeping all the requirements in mind, what could be a better structure?
Not sure whether it is the best method, but the way I structured firestore:
I have a collection called groups that is used for both group chat and for 1 on 1 chat. groups contains the following information:
chatID (same as the ID of that particular groups row)
members (userID's of all participating app users in that chat, also used for retrieve the chats for an user)
recentMessageText
recentMessageSentAt
recentMessageSendBy
readBy
chatTitle (can be null, since not needed for 1 on 1)
chatType (to indicate whether it is group or 1 on 1 chat)
In the ChatOverviewScreen I have a firestore query that only retrieves the documents from groups that contains his userID in members field.
So in the ChatOverviewScreen you can now display all the chats that the user has and you already know the ID reference needed to retrieve the messages that belong to a particular chat.
All the messages are stored in a collection called chats in which each documentID is also found in the groups document. Each document in chats contains a collection with all the messages.
The following link is also very useful: https://levelup.gitconnected.com/structure-firestore-firebase-for-scalable-chat-app-939c7a6cd0f5
Lets say I have a social media app. There is a Group model that has a field called invitedUsers which is simply an array of user ids that are a part of that group.
On my backend I have a route that a user hits to join that Group. In that route I do something like the following:
group.invitedUsers = lodash.concat(group.invitedUsers || [], userId)
group.save()
where group is the group that the user wants to join and userId is the id of the user that wants to join the group. Upon save everything is updated properly and the user is now a part of the group.
But what happens if two users hit the route at exactly the same time? How does MongoDB ensure that the group will always have both users ids added via the above method. Is there not a chance that group.invitedUsers could be referencing a stale value if both these group.save() are being triggered around the same time?
my flow:
User A selects user B in the user list:
system needs to check if a room for these two users exists, if not create unique room name and then join both users to the room
if exists, then just join users to the room they were already in and populate the chat with previous msges
Now what I am stuck at is how to exactly do it. Few options I am playing with in my head:
a) First how do i create the unique name that ties both users? Sure I can use string combination for both users, for example user A clicks user B --> "A&B", but this won't work when user B clicks user A, because that will be "B&A". I am struggling with creating dynamic unique names that could be applied to both.
b) do I keep an array with the two users info in the specific room saved in DB, and then check the array if user exists in it already? if so just use that room id as the room name? What is the best flow to save created rooms? Do i save by room name, which I guess would act as unique Id as well?
c) should I be checking the DB EVERYTIME user clicks another user to start a chat just to check if a room exists or not?
I know how to create rooms and all that jazz but what I am really struggling with is how to dynamically create room names so that its the same whether A clicks B or B clicks A and how to from a pseudo code level, store created rooms in DB and check for many users.
Here's an idea: Store the room in your database as a document that contains fields user1 and user2, which will contain the IDs of these users. Specifically, ensure that user1 < user2. When you need to query for this document later, you can do db.rooms.findOne({user1: smallerId, user2: largerId}). Then you can either store the room name and not use it in your queries, or you can even generate the displayed room name dynamically at runtime.
This has the benefit of not only guaranteeing the structure of a room document, but making your queries more efficient as well (you're comparing binary vs. comparing strings). There's also the benefit of not breaking the query when a user's name changes.
In general it's recommended that a document A that's associated with a different document B should refer to document B by an immutable ID, rather than by a mutable name. In this case since a room is associated with two users, have room refer to each user's ID.
I'm looking for some best practices when it comes to modeling confidential hierarchical data in general and specifically with DynamoDB.
The scenario is best explained with an example:
Let's say we have a number of users. Each user has a number of products. Each product consists of a number of parts.
Typical use cases:
List all products for a given user
List all parts for a given product
So far I have modeled this in DynamoDB like this:
Users
----------------
HashKey: UserId
Products
-------------------
HashKey: UserId
RangeKey: ProductId
Parts
-------------------
HashKey: ProductId
RangeKey: PartId
The data is confidential and accessed through authenticated REST endpoints where an authentication token can be mapped to a UserId. Each user may be allowed to view other users' data through some group concept.
Listing all products for a given user is simple since UserId is a key in the products table:
GET /users/111/products becomes a simple Query(Table=Products, UserId=111)
But consider the case of listing all parts for a given product:
GET /users/111/products/222/parts
If I simply do a Query(Table=Parts, ProductId=222) then I will get the desired data fast, but I am not protecting against other users querying for data belonging to user 111, provided they somehow know about ProductId 222 (in reality, ID:s will of course be UUID:s or similar so not so easily guessable):
GET /users/119/products/222/parts
... would result in malicious user 119 retrieving data that doesn't belong to him, provided nothing is done to address this.
So here I imagine I need to do something like one of these:
First make another query to make sure product 222 in fact belongs to the given user
Duplicate the UserId in the Parts table and include it in the query condition (which basically means it will match either all rows or no rows when scanning through the set identified by ProductId): Query(Table=Parts, ProductId=222, UserId=111)
Use UserId as the hash key also in the Parts table and instead keep ProductId as a secondary index
Use a composite HashKey such as UserId_ProductId ("111_222") on the Parts table
If I need to return a 401 as opposed to just empty data, option 1 seems like the only approach. But if we imagine a deeper hierarchy of data, e.g. "users having inboxes having messages having parts having attachments" it seems this approach could eventually be expensive (listing all attachments for part P might result in a query to check that part P belongs to message M, that message M belongs to inbox I and that inbox I belongs to user U, and so on).
Does anyone have any good arguments for which approach is most favorable? Or am I doing something stupid and should be modeling my data in some other way completely?
I have a requirement in Openbravo 3.0 framework . I have two user one is HR and the other is employee . Their is a checkbox in user window called HR USER .. In my window I need to write a read only logic so that when HR logins the record has to be editable , and when the employee logins the record has to be non editable,, I know how to do that for normal fields ,, But i am not getting anything about user validation..
In the employee screen i am assigning the user id to that employee.
Please Help
Read Only Logic based on Logged in User:
finding out the ID (primary key) of the User (HR or Employee) using PGAdmin Query tool.
Add read only as shown below.
Read Only Logic based on Logged in Role :
This can be achieved in three steps
creating Auxiliary Input.
finding out the ID (primary key) of the role (HR or Employee)
associating Read Only Logic to the Column.
First of all, we need to add an Auxiliary Input that will make
AD_ROLE_ID of the currently logged in user available to the user
window. Using the System Administrator role navigate to the
Application Dictionary || Setup || Auxiliary Input and create a new
record as shown below:
This will make the #AD_ROLE_ID session variable available to the [user] tab of the HR User window through the #ROLE_ID# variable.
Secondly, you need to find out what the AD_ROLE_ID of the HR role
is. Use the PgAdmin to query the AD_ROLE table and find that out. A
simple query reveals the following:
select ad_role_id, name from ad_role;
ad_role_id | name
----------------------------------+---------------------------
....
1000001 | Admin
SDJFALSDFJKLASJDFKLASDFASLDFJAKLSJ| velmurugan
SDFLAKSDJFLKASJDLFALSDFALDSKFJLAS | Employee
DSKLFJAKLDSJFKLASJFKLADSJFLKAJSDFK| F&B US, Inc. - Admin
....
(38 rows)
The primary key (AD_ROLE_ID) of the HR role is 054A32701D6D4CE6BF4F695DAB23EDB3. This will clearly be different in your case.
With this information, we can now find the HR User field definition
and set its Read Only Logic to
#ROLE_ID#!'054A32701D6D4CE6BF4F695DAB23EDB3' as shown below: