Neo4j unique user node - neo4j

I am making a user registration page. In this page users can enter username, name, email address and their password
I wanted to first check if the username was already taken, for this I used the following
MERGE (n:user { username: "newuser" })
ON CREATE SET n += { other properties }
RETURN n
This seems to work well. However, I actually want to check if for two fields username and email address. Basically allow new user if username and email address aren't taken.

MERGE (n:user { username: "newuser", email: "email" })
Will check for both fields and create/merge accordingly
If you want to check availability first, you should do another query for that.
To do the check
MATCH (n:user)
WHERE n.username = {newuser} OR n.email = {email}
RETURN count(n)
If it returns > 0, you're matched.

Related

Query to get user by email when email is stored in multiple columns

My attempt:
var user = (await client.Users.Request()
.Select(x => new { x.Id, x.OtherMails, x.Identities })
.Filter($"otherMails/any(id:id eq 'zed#gmail.com') or identities/any(ids:ids/issuerassignedid eq 'zed#gmail.com')")
.GetAsync());
which fails with:
Complex query on property identities is not supported.
See also this:
https://graph.microsoft.com/v1.0/users?$filter=startswith(displayName,'mary') or
startswith(givenName,'mary') or startswith(surname,'mary') or startswith(mail,'mary') or
startswith(userPrincipalName,'mary')
Note that email / OtherEmail fields are not populated.
Update - revised query fails with same error message.
public async Task<User> GetUserByEmailAddress(string email)
{
IGraphServiceUsersCollectionPage users = await client.Users.Request()
.Select(x => new { x.Id, x.Mail, x.OtherMails, x.Identities })
.Filter($"mail eq '{email}' or otherMails/any(id:id eq '{email}') or identities/any(ids:ids/issuerassignedid eq '{email}' and ids/signintype eq 'emailAddress')")
.GetAsync();
if(users.Any())
return users[0];
return null;
}
Identities and OtherMails properties of user object support $filter as described here.
Also when filtering on Identities object, you must provide both issuer and issuerAssignedId - refer to this doc for details.
I am not aware of your requirement but using Identities as filtering for email might not the best choice because ids wont necessarily be emails. If you are fltering for email you can use mail and other mail property - https://graph.microsoft.com/beta/users?$filter=otherMails/any(id:id eq 'mary#xx.live') or mail eq 'mary#xx.live'
Per the ticket I raised with Microsoft: "the error message is clear. You cannot filter (due not being supported) by identities and some additional property."
So if you need to search by email you have to load your entire user table into memory.

How to read results using Apollo Client for iOS

I'm trying to use GraphQL in iOS with Apollo Client. I have the following mutation:
login(user: String!, password: String!): UserType
and the UserType looks like this:
id: ID
user: String!
password: String!
name: String
lastname: String
email: Email!
groups: [GroupType]
In iOS, I have configured aopllo client as the doc says and is working perfectly, but I don't know how to get access to every field in the response. When the login success I want to read the json I receive as response with the UserType fields, so, I'm doing this:
apolloClient.perform(mutation: loginMutation) {
resultsGQL, error in
...
}
My question is, how can I read every field from resultGQL which belongs to the UserType data defined in my grapql schema?
Regards
The question is not 100% clear, since it is missing some code your mutation: A GraphQL mutation has to return at least one value, which you have to define. Since i'm not sure about your method
login(user: String!, password: String!): UserType
i am giving you a simple example for updating an existing userProfile with a GraphQL mutation and then returning every field being defined in your schema for userType.
Let us assume you have a userType (and therefore know the corresponding userId) and you want to modify the email:
mutation updateUserProfile($id: ID!, $newEmail: String) {
updateUser(id: $id, email: $newEmail) {
id
user
password
name
lastName
email
}
}
As you can see, after executing
updateUser(id: $id, email: $newEmail)
all return values are defined inside the following {...} parentheses and are therefore accessible in your callback variable
resultsGQL
That means:
apolloClient.perform(mutation: loginMutation) { resultsGQL, error in
if let results = resultsGQL?.data {
// "results" can now access all data from userType
}
}
Since you defined all entities of your userType schema to be returned from the mutation, you can access them now in your callback variable.

In my neo4j db, the graphql mutation is not working as expected for 2 types

I am using neo4j dB and apollo-graphql server for my test project. I am trying to create a Q&A app where a user could create a question (with unique id) and then it would be answered by other user (similar to stackoverflow but with very minimum features).I want to create relationship between "Questions" node and "Users" node in my neo4j db via mutation.
so, in the mutations I created User Type and Question Type. But when I am trying to create relation between the user (who created question) and the question, the mutation is not working as expected.
Here is the graphql schema...
type Question {
questionID: Int! # unique id for a particular question
title: String! # Question Title
details: String!
createdBy: User!
}
type User {
userID: Int!
name: String!
email: String
questions: [Question] # specific questions related to the user
}
type Mutation {
createUser(name: String!, userID: Int!): [User]
createQuestion(questionID: Int!, title: String!, details: String!,userID: Int! ): Question
And here is the mutation snippet from resolver...
Mutation: {
createQuestion(_,params){
let session = driver.session();
let query = "MERGE (q:Questions {questionID:{questionID},title:
{title},details:{details},userID:{userID}})-[:CREATED_BY]->
(u:Users{userID:{userID}}) RETURN q;"
return session.run(query,params)
.then( result => {
return result.records.map( record => {
return record.get("q").properties
}
)
}
)
},
}
I had already inserted a dummy user with unique userID "1" in neo4j db before running this mutation query. And I ran this "createQuestion" mutation in graphiQL to create a question for the existing user and so I passed userID "1" as argument,
//createQuestion(questionID: Int!, title: String!, details: String!,userID: Int! )
And after running this mutation query, I was hoping to get a relation of [:CREATED_BY] between the existing user and the question created,
But my database now has 2 users with same user id "1" (i.e. one more user with same userid "1" has been inserted and the relationship is now present between this newly inserted user and question node).
What I need is to avoid to create new duplicate user with same id , and rather just to create relationship between the existing user and the question. So could someone please let me know what I have mistaken here ?
You must change your query:
let query = "
MERGE (u:Users{userID:{userID}})
MERGE (q:Questions {questionID:{questionID},title:
{title},details:{details},userID:{userID}})
MERGE (q)-[:CREATED_BY]->(u)
RETURN q;"
As you have the questionID already in place, which i'm guessing is a unique identifier I would change the query to:
let query = "
MERGE (u:Users{userID:{userID}})
MERGE (q:Questions {questionID:{questionID})
ON CREATE SET q.title=
{title},q.details={details}
MERGE (q)-[:CREATED_BY]->(u)
RETURN q;"
Notice I also removed userID property from question as you do not need it as you already create a relationship from User to Question
Edit:
You can solve the problem from the comment in two ways:
by adding UserID property to question:
let query = "
MERGE (u:Users{userID:{userID}})
MERGE (q:Questions {questionID:{questionID},userID:{userID}})
ON CREATE SET q.title= {title},q.details={details},
MERGE (q)-[:CREATED_BY]->(u)
RETURN q;"
Or with local merges:
let query = "
MERGE (u:Users{userID:{userID}})
MERGE (u)<-[:CREATED_BY]-(q:Questions {questionID:{questionID}})
ON CREATE SET q.title= {title},q.details={details},
RETURN q;"

Grails Audit Logging: Selective auditing and onChange method

We are using grails audit logging plugin for auditing few domain objects in our project
https://grails.org/plugin/audit-logging
Facing couple of issues related to it:
We have a user domain object which we want to audit.
We also create lot of users during our system set up. So, when we make auditable = true for User domain, it audits that as well - creates a new row in audit_log_event table for each user that gets created.
Is there a way to avoid this? We just want to audit when a user is created/updated by an admin user (basically when the admin user is logged in)
For some of the attributes of a domain object that we are auditing, we want to make some modification in the new value.
Example: Lets say email address of User object is getting updated from a#gmail.com to b#gmail.com, then we want to insert "***B#gmail.com-12:23:47" in new_value column of audit_log_event table.
I thought by updating the value of newMap[key] in onChange() method, we can do this, but that doesn't seem to work.
Is there a way to do this?
Tried this:
def onChange = {
oldMap,newMap ->
println "User was changed ..."
oldMap.each({ key, oldVal ->
if(oldVal != newMap[key]) {
println " * $key changed from $oldVal to " + newMap[key]
if(key == "email") {
newMap[key] = "*****Some value udpated****"
println "email is now again changed ..."
println " * $key changed from $oldVal to " + newMap[key]
}
}
})
}
Console output:
User was changed ...
* email changed from a#gmail.com to b#gmail.com
email is now again changed ...
* email changed from a#gmail.com to *****Some value udpated****
However, in DB:
old_value = a#gmail.com
new_value = b#gmail.com

firebase session for two users [duplicate]

In my main page I have a list of users and i'd like to choose and open a channel to chat with one of them.
I am thinking if use the id is the best way and control an access of a channel like USERID1-USERID2.
But of course, user 2 can open the same channel too, so I'd like to find something more easy to control.
Please, if you want to help me, give me an example in javascript using a firebase url/array.
Thank you!
A common way to handle such 1:1 chat rooms is to generate the room URL based on the user ids. As you already mention, a problem with this is that either user can initiate the chat and in both cases they should end up in the same room.
You can solve this by ordering the user ids lexicographically in the compound key. For example with user names, instead of ids:
var user1 = "Frank"; // UID of user 1
var user2 = "Eusthace"; // UID of user 2
var roomName = 'chat_'+(user1<user2 ? user1+'_'+user2 : user2+'_'+user1);
console.log(user1+', '+user2+' => '+ roomName);
user1 = "Eusthace";
user2 = "Frank";
var roomName = 'chat_'+(user1<user2 ? user1+'_'+user2 : user2+'_'+user1);
console.log(user1+', '+user2+' => '+ roomName);
<script src="https://getfirebug.com/firebug-lite-debug.js"></script>
A common follow-up questions seems to be how to show a list of chat rooms for the current user. The above code does not address that. As is common in NoSQL databases, you need to augment your data model to allow this use-case. If you want to show a list of chat rooms for the current user, you should model your data to allow that. The easiest way to do this is to add a list of chat rooms for each user to the data model:
"userChatrooms" : {
"Frank" : {
"Eusthace_Frank": true
},
"Eusthace" : {
"Eusthace_Frank": true
}
}
If you're worried about the length of the keys, you can consider using a hash codes of the combined UIDs instead of the full UIDs.
This last JSON structure above then also helps to secure access to the room, as you can write your security rules to only allow users access for whom the room is listed under their userChatrooms node:
{
"rules": {
"chatrooms": {
"$chatroomid": {
".read": "
root.child('userChatrooms').child(auth.uid).child(chatroomid).exists()
"
}
}
}
}
In a typical database schema each Channel / ChatGroup has its own node with unique $key (created by Firebase). It shouldn't matter which user opened the channel first but once the node (& corresponding $key) is created, you can just use that as channel id.
Hashing / MD5 strategy of course is other way to do it but then you also have to store that "route" info as well as $key on the same node - which is duplication IMO (unless Im missing something).
We decided on hashing users uid's, which means you can look up any existing conversation,if you know the other persons uid.
Each conversation also stores a list of the uids for their security rules, so even if you can guess the hash, you are protected.
Hashing with js-sha256 module worked for me with directions of Frank van Puffelen and Eduard.
import SHA256 from 'crypto-js/sha256'
let agentId = 312
let userId = 567
let chatHash = SHA256('agent:' + agentId + '_user:' + userId)

Resources