Don't store documents with duplicated key in Firestore - firebase

I want to avoid duplicated keys in different documents.
For example, in the document 1 I have this:
{
"user": "myuser",
"email: "a#a.com"
}
In the document 2 I have:
{
"user": "myuser2",
"email: "b#b.com"
}
And in document 3 I have:
{
"user": "myuser",
"email: "c#c.com"
}
I don't want to be possible to create document 3, as the user key has the same value. Is possible to do this with database rules?
Thanks.

add the document with user as a unique ID like that
db.collection("Your Collection").document(user).set(Your Object)
"user" is the user field of your object

one solution could be using email address as an unique id for your document, because email addresses are always unique:
db.collection("collection/" + userObject.email).set(userObject)
or
db.collection("collection").doc(email).set(user object)

Related

How to create a friends relationship in Hasura

I have 2 tables: users and friends.
friends has a user_id foreign key which obviously maps to the users id key.
friends also has a friend_id key which maps to the users id key as well.
I am using JWT Auth. I have successfully set the permission on the users table so that when the user queries for users the user pulls only that specific user's column. I have also created an array relationship for friends on the users table so user rows can have friends via the relationship.
However, when querying something like:
{
user {
username
friends {
id
username
}
}
}
This returns null for friends, because the user role does not have access to friends (due to the session variable X-Hasura-User-Id used to determine user by session. How do I reconcile having the session variable work but also be able to query other friends?
Put this permission on friends, and then users can see all friend rows where the friend_id is their user.id:
{
"friend_id" :{
"_eq" : "X-Hasura-User-Id"
}
}
Permissions for relationships are inherited from the relationship table. So you just need to ensure that the user can regularly access the related table rows by themselves, and if you can do that it will apply to the relationships as well.
Unrelated, it looks like these tables have the same columns. You might want to use a self-referential relationship from users->users as friends if they're identical. If not ignore this =)
Edit:
Try using an _exists permission:
(You can probably simplify this using the relationships direct access to friends and user but I don't know your exact table and relationship structure)
"If there exists in table friends, a row where the 'friend_id' is 'X-Hasura-User-Id' and the 'user_id' is equal to this row's user ID"
{
"_or": [
{
"_exists": {
"_table": { "schema": "public", "name": "friends" },
"_where": {
"_and": [
{ "friend_id": { "_eq": "X-Hasura-User-Id" } },
{ "user_id": { "_ceq": "user_id" } }
]
}
}
},
{ "id": { "_eq": "X-Hasura-User-Id" } }
]
}

Require unique username when Users container has userId as partition key

I am using this article as an example
https://learn.microsoft.com/en-us/azure/cosmos-db/how-to-model-partition-example with a Users container with userId and username and the partition key as userId.
{
"id": "54c7da13-f4b8-4668-90dc-7c1aa968a73e",
"userId": "54c7da13-f4b8-4668-90dc-7c1aa968a73e",
"type": "user",
"username": "jeffw"
}
In my create user page I want to make sure the username is unique before adding a new user. I tried a pre-trigger but found that "You can't run stored procedures or triggers across multiple logical partitions." How do I make sure that when a user is created that they have selected a unique username? I think I could change the partition key to username but why does the article use userId instead?
SOLUTION
See answer from #mark-brown.
Create a unique key on the Users container and /username:
await database.Database.DefineContainer(name: "Users", partitionKeyPath: "/userId")
.WithUniqueKey().Path("/username").Attach()
.CreateIfNotExistsAsync();
Then try to create a new User with userId as "unique_username" and the new username that is attempting to be created:
{
"id": "06af2937-4677-4d27-a167-5517aa6d0ffd",
"userId": "unique_username",
"type": "unique_username",
"username": "jeffw"
}
await _usersContainer.CreateItemAsync(uniqueUser, new PartitionKey("unique_username"));
This will return a Conflict status if the username already exists. Example is here https://github.com/jwidmer/AzureCosmosDbBlogExample/blob/master/BlogWebApp/Services/BlogCosmosDbService.cs
Changing the partition key to username won't help because you can have multiples of that value in your container. One way you could do this is to have a new partition where you store a unique instance for every user name and use a unique index on the container (unique indexes are unique within a logical partition).
Create a new type = "unique_user" and a userId = "unique_user". Then add a new record of that type with the new username as they register. This should get you millions of users without going over the 20GB limit. Then when creating a new user do an insert on the container with the "unique_user" type and id with the new username. If you get a 201 then do another insert with type= "user" and the rest of the user data.
hope that helps.
You can set up an index policy for unique values.

How to query nested objects in firestore [duplicate]

This question already has answers here:
Firestore - Nested query
(2 answers)
Closed 2 years ago.
I want to store data in following format:
{
"chatName": "Football",
"chatMembers":
[
{
"userId": "nSWnbKwL6GW9fqIQKREZENTdVyq2",
"name": "Niklas"
},
{
"userId": "V3QONGrVegQBnnINYHzXtnG1kXu1",
"name": "Timo"
},
]
}
My goal is to get all chats, where the signed in user with a userId is in the chatMembers list. If the userId of the signed in user is not in the chatMembers property, then that chat should be ignored. Is this possible?
If this is not possible, how can i achive this with subcollections?
My development language is dart, but you can also post solutions in other languages.
My current attempt is this, but this is not working:
_firestore.collection(collectionName).where("chatMembers.userId", isEqualTo: userId).snapshots()
Since August 2018 there is the new array_contains operator which allows filtering based on array values. The doc is here: https://firebase.google.com/docs/firestore/query-data/queries#array_membership
It works very well with arrays of string. However, I think it is not possible to query for a specific property of an object stored in the array. One workaround is to query for the entire object, as follows (in Javascript). Of course this may not be feasible in every situation....
var db = firebase.firestore();
var query = db.collection('chatDocs').where("chatMembers", "array-contains", { userId: "xyz", userName: "abc" });
Renaud Tarnec's, which complete, doesn't work in every case. Situations where only one or not all of the fields are known won't return the expected documents. However, by restructuring the data in the document, a query can be made to work where only one field is known, like a user identifier (uid).
Here's the data structure inside one of the document:
{
"members": {
"user4": {
"active": true,
"userName": "King Edward III",
"avatar": "www.photos.gov/animeGirl5.png"
},
"user7": {
"active": true,
"userName": "Dave K.",
"avatar": "www.photos.gov/gunsAmericanFlag.png"
}
}
Here's the query:
uid = 'user4';
collectionQuery = collectionReference.where(`members.${uid}.active`,"==", true);
In this example, "user4" represents a user who may or may not be in a group. This will return all documents in the collection where the uid "user4" is an active member. This works by only needing to know the UID of the member and works without needing to know their name or avatar uri ahead of time.

How to Filter Nested Array Object in DynamoDB

I am very beginner to AWS DynamoDB, I want to scan the DynamoDB with SENDTO.emailAddress = "first#first.com" as FilterExpression.
The DB Structure looks like this
{
ID
NAME
MESSAGE
SENDTO[
{
name
emailAddress
}
]
}
A Sample Data
{
ID: 1,
NAME: "HELLO",
MESSAGE: "HELLO WORLD!",
SENDTO: [
{
name: "First",
emailAddress: "first#first.com"
},
{
name: "Second",
emailAddress: "second#first.com"
}
]
}
I want to retrieve document that match emailAddress. I tried to scan with filter expression and here is my code to retrieve the data. I am using AWS Javascript SDK.
let params = {
TableName : "email",
FilterExpression: "SENDTO.emailAddress = :emailAddress",
ExpressionAttributeValues: {
":emailAddress": "first#first.com",
}
}
let result = await ctx.docClient.scan(params).promise();
In order to find the item by sendto attribute, you need to know both name and emailAddress attribute value. DynamoDB can't find the data by just one of the attributes in an object (i.e. email attribute value alone).
CONTAINS function can be used to find the data in List data type.
CONTAINS is supported for lists: When evaluating "a CONTAINS b", "a"
can be a list; however, "b" cannot be a set, a map, or a list.
Sample code using Contains:-
var params = {
TableName: "email",
FilterExpression: "contains (SENDTO, :sendToVal)",
ExpressionAttributeValues: {
":sendToVal": {
"name" : "First",
"emailAddress" : "first#first.com"
}
}
};
If you don't know the value of name and emailAddress attribute, you may need to remodel the data to fulfill your use case.
I think that you should create two tables for users and for messages.
The user table has partition_key: user_id and sort_key: email and a field with an array of his messages ids.
The message table has partition_key: message_id and a field with an array of users ids.
When you will get the array of users ids you can use BATCH GET query to get all users of one message.
When you will get the array of message ids you can use BATCH GET query to get all messages of one user.
If you want to get one user by email you can use QUERY method.
Docs

Referring to a user by several possible identities

My application keeps multiple profile attributes for its users, such as:
An internal userId
Their phone number
Their email
etc. Each attribute is unique to a user; they can all be used as identity information.
I am designing an API with operations that refer to a specific user, eg charge.
I want to allow clients to identify users by any of the available profile attributes. In my specific domain, it is not possible to just enforce clients to use the internal userId, even if they can receive it in a separate call (eg getUserIdFromProfileAttribute).
Assuming the charge operation, it is a POST request with a JSON document inside the body. What would be the best way to identify the users? I am thinking one of the following:
Top-level key/value pairs for both the id and the id type:
{
"userId": <id>,
"userIdType": <idType>
}
Nested key/value pairs inside a user key:
{
"user": {
"id": <id>,
"type": <idType>
}
}
Single key/value pair, using a URI format with (possibly) custom protocols:
{
"user": <uri> # eg id:1234, tel:+19283912000, email:user#mail.com
}
Single key/value pair, using different keys for each id (one key per call):
{
"userId": <id> *OR*
"userMsisdn": <msisdn> *OR*
"userEmail": <email>
}
Same as above, but nested inside a user key:
{
"user": {
"id": <id> *OR*
"msisdn": <msisdn>
}
}
Any suggestions about best practices? Anyone can point me to some standard / widely used APIs with a similar need?
I should repeat that using just the internal userId in all calls is not possible, and using a separate call for each id (eg chargeById, chargeByEmail) is not practical as there are many such calls.

Resources