2
votes

I am building a system where a User can authenticate with one of several Providers - e.g. Google, Twitter, Facebook, etc.

In my data store, I'm modelling this as having a :USER node for each User record, a :AUTH_PROVIDER node for each Authentication Provider, and then a Relationship from :USER to :AUTH_PROVIDER for the users authentication details at that provider. This works great.

I've also got a DAO method to load a User by the internal User ID, which returns an object containing - amongst other things - the details of all the Providers that the User is related to. This works great too. The query for that is simply

MATCH (u:USER{id:{id}})-[r:AUTHENTICATED_BY]->(p:AUTH_PROVIDER) 
RETURN u AS user, COLLECT({auth:r, provider:p}) AS providers

What I'm now trying - and struggling - to do is a DAO method to load the same User record, but resolved by the Provider link. So, for example, load the User details for the User with ID "abcde" at Provider "google".

I've got something working, but it's not great.

MATCH (p:AUTH_PROVIDER{id:"google"}), (u:USER)-[r:AUTHENTICATED_BY]->(p1:AUTH_PROVIDER) 
WHERE (u)-[:AUTHENTICATED_BY{id:"abcde"}]->(p) 
RETURN u AS user, COLLECT({auth:r, provider:p1}) AS providers

In particular, the Neo4J browser warns me "This query builds a cartesian product between disconnected patterns"

Is there a better way to achieve the same thing? Specifically: * Find the :USER node that is linked to a particular :AUTH_PROVIDER node by a particular Relationship * Return that :USER node, and all of the :AUTH_PROVIDER nodes that it is related to

The other things that I've tried - which I've not got written down - either return only the one matching :AUTH_PROVIDER node or else all of the other :AUTH_PROVIDER nodes, but never the complete set.

Cheers

Edit: This will create some test data to show what I mean:

CREATE 
    (u1:USER{name:"Graham"}), 
    (u2:USER{name:"Bob"}), 
    (p1:AUTH_PROVIDER{id:"google"}), 
    (p2:AUTH_PROVIDER{id:"twitter"}),
    (p3:AUTH_PROVIDER{id:"facebook"}), 
    (u1)-[:AUTHENTICATED_BY{id:"123"}]->(p1), 
    (u1)-[:AUTHENTICATED_BY{id:"456"}]->(p2), 
    (u1)-[:AUTHENTICATED_BY{id:"789"}]->(p3), 
    (u2)-[:AUTHENTICATED_BY{id:"abc"}]->(p1), 
    (u2)-[:AUTHENTICATED_BY{id:"def"}]->(p2), 
    (u2)-[:AUTHENTICATED_BY{id:"ghi"}]->(p3)
1

1 Answers

1
votes

This query does not build a cartesian product and return the desired data:

MATCH (u:USER)-[:AUTHENTICATED_BY{id:"123"}]->(:AUTH_PROVIDER {id:"google"})
MATCH (u)-[r:AUTHENTICATED_BY]->(p:AUTH_PROVIDER)
RETURN u AS user, COLLECT({auth:r, provider:p}) AS providers