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?
Related
Hello, I have a problem I created a Registration form and im trying to check if there is any user which have a certain username inside the Firebase Db. I tried to get the reference of all the users.
var users = Database.database().reference("users")
But I don't know how I could check if there is any user with a specified username.
You'll want to use a query for that. Something like:
let query = users.queryOrdered(byChild: "username").equalTo("two")
Then execute the query and check whether the result snapshot exists.
Note though that you won't be able to guarantee uniqueness in this way. If multiple users perform the check at the same time, they may both end up claiming the same user name.
To guarantee a unique user name, you will need to store the user names as the key - as keys are by definition unique within their parent node. For more on this, see some of these top search results and possibly also from here.
I've been trying to implement live messaging in my application and I cannot seem to think of a convenient data structure inside Firestore. My current structure looks like this:
collection("conversations").document(id).collection("messages")
Each document holds two attributes user1 and user2 with nicknames of contributors to the conversation. Each document also owns a collection called messages which holds documents where each represents a single message sent with some info.
What I'm trying to do next is to check if the conversation already exists, if not then create it. The problem for me is write a correct query to find out if it exists.
My first idea was: create users array instead which holds nicknames of users and then simply query:
db.collection("conversations").whereField("users", in: ["username1", "username2"])
Problem with this is that it means "where users contains username1 OR username2", but I need it to contain "username1 AND username2".
I tried to be smart and chain the whereField function as following:
db.collection("conversations").whereField("users", arrayContains: "username1").whereField("users", arrayContains: "username2")
Turns out that you cannot use arrayContains more than once in a single query.
After that I came back to the structure as displayed on the screenshot with user1 and user2 and ran a new query:
db.collection("conversations").whereField("user1", isEqualTo: user).whereField("user2", isEqualTo: friend)
This query is ran in a function where user and friend are string parameters holding nicknames of both sender and receiver of the message we're currently sending. Imagine you are sending a message, user is always going to be your nickname and friend the receiver's one. The problem with the query is that you're nickname might be saved under user1 or user2 and receiver's nickname aswell. In either of those situations the conversation exists. How would I have to change the query since I don't know in an advance who will have which position in the query aswell as in Firestore. Running the last query that I included twice while switching user and friend parameter seems very unconvenient.
Any tips or solutions to progress in this problem will be much appreciated!
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 have a table that has a long column that is a GroupCode. I can have groups of products, so to get all the product of a group I just get all the products which GroupCode is the same.
I can change a product from one group to another, and if I change a product from a group, I want that all the products of the group change to the new group.
If I use optimistic concurrency, it could happen this:
One user wants to change a product from a group, so he gets all the products with the same groupCode. Set the new new groupCode to all this products.
A second user add a new product to the group. But the first user doesn't have this product because he got all the products before the second user add the new product.
So at the end, a new product has a wrong GroupCode, because the code is not correct because all the products of the group was change to the new group. So I would have a group with only one product, and it wouldn't be correct.
With pessimistic concurrency, the first use get all the products of the group, block all the products.
The second user try to add a new product to the group, to do that, first try to get one of the products of the group as reference product, but how it is blocked by the first user, the second user has to wait.
The first user changes all the products to the new group and unblock all the products.
The second user get the reference product, that has the new groupCode, so the new product is added to the correct group.
In summary, I want that when I change a product from one group to another, I want to change all the products of the group, and avoid that a new product belongs to the old group.
Is it possible to solve this case with optimistic concurrency? Or I have to use pessimistic concurrency?
I honestly don't see the issue here. If you want to implement it as OCC, you should just follow the OCC phases.
User A gets all records which belong to group ABC
User B gets a reference to Record1, which belongs to ABC at the moment
User A moves Record1 to group XYZ
User B wants to add a new record to the group to which Record1 belongs. So just before inserting the record, get the group of Record, which is now XYZ
This is assuming that you go with the 'referential record' approach. If your screen (or whatever) just lists the currently available groups, and meanwhile one of those groups becomes empty (because you have moved all records to another group), there is no way of telling if that's a concurrency issue or it is working as expected. In such case, you should normalize your database and split the categories into a separate table, so that at least the user gets an error that the group no longer exists.
Suppose I have two objects
1.Account- standard object[it has a field name Status_c which is a picklist having value inprogress and closed]
2.Client_c - custom object[it also have same field name Status__c which is a picklist having value inprogress and closed]
and Client__c has lookup to Account name which means Account has a related list of client object .
My question is :
I want to write a trigger where if I put account status to "closed" I can not put client status to "closed",it should throw an error message on client object or if I put client status to closed I can not put account status to closed vice versa.
Can any one please help me to write a trigger on this??
Conceptually, I think what you are looking to do is set up Validation Rules on both of those objects. Your validation rule on Client_c should be pretty simple: TEXT(Status_c) == 'Closed' && TEXT(Account_c.Status_c) == 'Closed'
The more interesting piece is how you handle making sure none of your related items are Closed when you move the Account to Closed. I tend to prefer creating a field on the Account that keeps track of the status of the related items (checkbox) that basically tells me whether it is valid for me to change my status or not. In this case, the validation rule becomes pretty simple. In order to set that boolean value, I end up using a Trigger on Client__c that basically just grabs all the Accounts when a Client is being modified in the batch (taking into account both inserts, upserts, and deletes):
SELECT Account__c.Id FROM Client__c WHERE Id =: Trigger.new OR Id =: Trigger.old
Then create a Set of all the Account Ids (in this example, named accounts), and run a query to retrieve ALL Clients related to those Ids (in a single query to ensure you don't hit SOQL limits).
SELECT Account__c.Id, Status__c FROM Client__c WHERE Account__c.Id =: accounts
From the results of this, you will iterate over all of the entries, tossing them into a Map keyed by the Account Id where the value is a List of Clients. When you are done, run a query to get all accounts based on the "accounts" list from earlier (which was just a list of strings, not actual Accounts), subsequently iterate over all the Clients associated with that Account, and if a Client is marked as Closed, you will update the metadata of that Account accordingly. If no Clients are closed, the Account will be marked as such. Once you are finished, run an update statement to update the list of Accounts that you have modified.