I am very much new to riak and I am currently using it with an API. So, let me explain my scenario.
There is an account - say example#riaktest.com created under accounts bucket.
So, on doing
localhost:8098/buckets/accounts/keys?keys=true it returns {{"keys":["example#riaktest.com"]}}
So, i basically have one 'key' with other data fields inside.
Now, I have a functionality that a user can join a particular group. Lets say, 'group1'.
He logs in, joins group1. I give the snowflake generated ID of the account as the index.
Question is:
How do I see a particular key's list of index?
localhost:8098/riak/group/group1 will give me the values and the main account it is tied to.
Related
How to retrieve child nodes data and update values in firebase:
As I want to make an API in which I create parameters to pass values by it.
Like example I have one node in firebase name as users and under it has user amount is 10. I want to get that data of amount from users node and add some value with it like 10+5 will update in database as 15 in my firebase.
I tried to retrieve data from firebase by seeing youtube, but I'm unable to get what I want; I was only able to get the whole users node data at whole.
That sounds like a 2-step process:
Store the value as a number rather than as a string like you now have it.
Then use the atomic increment operation to increment it by 5.
I have a situation where a user can create a doc and then share it with a group of other users. They could share it to multiple different groups. I don't know how to set a rule for this.
Here is the database structure:
So in the group you have a list of docs that have been shared to it. My app loads the group that a user is in, then wants to load all the docs in the documents array. I need a way server side to say that this is OK. Up until now only the owner of the doc can read it.
I put a field in each doc that contains ids for each group its shared to. I think I want to say "check if the user is a member of any groups in the sharedToGroups" list but I can't work out how to do that unless I maintain another list somewhere say in the userProfile doc that has a list of circles the user is a member of. Even then I'd be trying to compare 2 lists and I'm not sure I can do that client side.
It would be nice to be able to get the group Id somehow from where the request is being issued from and just see if that is in the sharedToGroups array.
Any help or comments on how this can be achieved would be greatly appreciated, maybe it needs a different db structure.
You can try an approach of this sort:
I am not sure if this will help you but off the top of my head maybe you could enable permissions on firestore for the group document. As in, in the rules, for the group set up a function that validates the user with the user ID stored in the document with the ID attached in the auth via the firebase auth
Therefore, rather than trying to restrict access per document, restrict access per group.
I'm going to answer my own question. Not sure if its the correct protocol here (not a professional programmer or experienced Stack Overflower) but it might help someone.
I ended up adding a field in the user_profiles document that has a list of each group they are in. This list needs to be maintained as I create and add / remove people from groups along with the members list in the group itself.
The benefit of this is that I can use the users id from the request object to get that document from the data base in the security rule. I then have a 'sharedToGroup' array in the doc I'm trying to access and a "inGroups" array in the user_profile that I can access also. Then I use the hasAny operator to compare the two arrays and allow access if the sharedToGroup array has any values from the inGroups array.
My rule becomes:
match /_group/{groupId}{
allow create: if isSignedIn();
allow read: if isOwner()
|| resource.data.sharedToGroup.hasAny(get(/databases/$(database)/documents/user_profiles/$(request.auth.uid)).data['inGroups']);
allow write: if isOwner();
}
Only thing left to do is to secure the user_profiles doc to make sure not even the user can write to it since I don't want someone manually adding groups into their array.
I hope this might help someone someday - like I said I'm a not a pro here so take it with a grain of salt.
Is there any way I can find if users is present in both the groups here: user 1 so that notification/data can be sent to only that set of common users only?
As DB grows I think it will be inefficient to check if every user in one group is present in another or not.
Yes there is. You can create a list of users from GroupA, then create another list of users from GroupsB and then just simply use this line of code using Java8:
!Collections.disjoint(list1, list2);
I'm working on a website that mostly displays items created by registered users. So I'd say 95% of API calls are to read a single item and 5% are to store a single item. System is designed with AWS API Gateway that calls AWS Lambda function which manipulates data in DynamoDB.
My next step is to implement voting system (upvote/downvote) with basic fetaures:
Each registered user can vote only once per item, and later is only allowed to change that vote.
number of votes needs to be displayed to all users next to every item.
items have only single-item views, and are (almost) never displayed in a list view.
only list view I need is "top 100 items by votes" but it is ok to calculate this once per day and serve cached version
My goal is to design a database/lambda to minimize costs of AWS. It's easy to make the logic work but I'm not sure if my solution is the optimal one:
My items table currently has hashkey slug and sortkey version
I created items-votes table with hashkey slug and sortkey user and also voted field (containing -1 or 1)
I added field votes to items table
API call to upvote/downvote inserts to item-votes table but before checks constraints that user has not already voted that way. Then in second query updates items table with updated votes count. (so 1 API call and 2 db queries)
old API call to show an item stays the same but grabs new votes count too (1 API call and 1 db query)
I was wondering if this can be done even better with avoiding new items-votes table and storing user votes inside items table? It looks like it is possible to save one query that way, and half the lambda execution time but I'm worried it might make that table too big/complex. Each user field is a 10 chars user ID so if item gets thousands of votes I'm not sure how Lambda/DynamoDB will behave compared to original solution.
I don't expect thousands of votes any time soon, but it is not impossible to happen to a few items and I'd like to avoid situation where I need to migrate to different solution in the near future.
I would suggest to have a SET DynamoDB (i.e. SS) attribute to maintain the list of users who voted against the item. Something like below:-
upvotes : ['user1', 'user2']
downvotes : ['user1', 'user2']
When you update the votes using UpdateExpression, you can use ADD operator which adds users to SET only if it doesn't exists.
ADD - Adds the specified value to the item, if the attribute does not
already exist. If the attribute does exist, then the behavior of ADD
depends on the data type of the attribute:
If the existing data type is a set and if Value is also a set, then
Value is added to the existing set. For example, if the attribute
value is the set [1,2], and the ADD action specified [3], then the
final attribute value is [1,2,3]. An error occurs if an ADD action is
specified for a set attribute and the attribute type specified does
not match the existing set type. Both sets must have the same
primitive data type. For example, if the existing data type is a set
of strings, the Value must also be a set of strings.
This way you don't need to check whether the user already upvote or downvote for the item or not.
Only thing you may need to ensure is that the same user shouldn't be present on upvote and downvote set. Probably, you can use REMOVE or ConditionExpression to achieve this.
I have a data model like this:
Person node
Email node
OWNS relationship
LISTS relationship
KNOWS relationship
each Person can OWN one Email and LISTS multiple Emails (like a contact list, 200 contacts is assumed per Person).
The query I am trying to perform is finding all the Persons that OWN an Email that a Contact LISTS and create a KNOWS relationship between them.
MATCH (n:Person {uid:'123'}) -[r1:LISTS]-> (m:Email) <-[r2:OWNS]- (l:Person)
CREATE UNIQUE (n)-[:KNOWS]->[l]
The counts of my current database is as follows:
Number of Person nodes: 10948
Number of Email nodes: 1951481
Number of OWNS rels: 21882
Number of LISTS rels: 4376340 (Each Person has 200 unique LISTS rels)
Now my problem is that running the said query on this current database takes something between 4.3 to 4.8 seconds which is unacceptable for my need. I wanted to know if this is normal timing considering my data model or am I doing something wrong with the query (or even model).
Any help would be much appreciated. Also if this is normal for Neo4j please feel free to suggest other graph databases that can handle this kind of model better.
Thank you very much in advance
UPDATE:
My query is: profile match (n: {uid: '4692'}) -[:LISTS]-> (:Email) <-[:OWNS]- (l) create unique (n)-[r:KNOWS]->(l)
The PROFILE command on my query returns this:
Cypher version: CYPHER 2.2, planner: RULE. 3919222 total db hits in 2713 ms.
Yes, 4.5 seconds to match one person from index along with its <=100 listed email addresses and merging a relationship from user to the single owner of each email, is slow.
The first thing is to make sure you have an index for uid property on nodes with :Person label. Check your indices with SCHEMA command and if missing create such an index with CREATE INDEX ON :Person(uid).
Secondly, CREATE UNIQUE may or may not do the work fine, but you will want to use MERGE instead. CREATE UNIQUE is deprecated and though they are sometimes equivalent, the operation you want performed should be expressed with MERGE.
Thirdly, to find out why the query is slow you can profile it:
PROFILE
MATCH (n:Person {uid:'123'})-[:LISTS]->(m:Email)<-[:OWNS]-(l:Person)
MERGE (n)-[:KNOWS]->[l]
See 1, 2 for details. You may also want to profile your query while forcing the use of one or other of the cost and rule based query planners to compare their plans.
CYPHER planner=cost
PROFILE
MATCH (n:Person {uid:'123'})-[:LISTS]->(m:Email)<-[:OWNS]-(l:Person)
MERGE (n)-[:KNOWS]->[l]
With these you can hopefully find and correct the problem, or update your question with the information to help others help you find it.