Create Optional 1 to Many Relationships for Existing Nodes

Howdy,

I'm using Neo4j 3.5.6 in a Node.js app. I have a data set of existing nodes containing a b:Beer that can have 1 or more relationships to a h:Hop. I am trying to write a query that allows for sometimes having only 1 hop relating to 1 beer, or sometimes having 7+ hops relating to 1 beer.

I have been trying variations of the following query, but nothing is working...

Match (b:Beer {name:{beernameParam}})
Optional Match (h:Hop {name:{hopnameParam}})
With collect(h.name) as hop,b
Unwind hop as hops
Create (hops)<-[r:BREWED_WITH]-(b) 
Return hops,b

My hope is that from my form input option of </>select multiple</> hops that I coud sometimes add only 1 relationship or sometimes many relationships.

Here is my ejs form code...

            <form  method="post" action="/beerhop/add">
                <label>Beer Name</label>
                <select class="form-control" type="text" name="beername">
                        <% beer.forEach(function(beer){ %>
                            <option><%= beer.name %></option>
                        <% }) %>
                </select>
                <label>Hop Name</label>
                <select class="form-control" type="text" name="hopname" multiple>
                        <% hop.forEach(function(hop){ %>
                            <option><%= hop.name %></option>
                        <% }) %>
                </select>                
                <input class="button" type="submit" value="Submit">
            </form>

Here is my server-side app.post...

app.post('/beerhop/add',async (req, res) => {
  const {beername, hopname} = req.body;
  try {
    const result = await session.run(`Match (b:Beer {name:{beernameParam}})
                                      Optional Match (h:Hop {name:{hopnameParam}})
                                      With collect(h) as hops, b
                                      Unwind hops as hop
                                      Create (hop)<-[r:BREWED_WITH]-(b) 
                                      Return hop,b`, 
                                      {hopnameParam:hopname, beernameParam:beername});
    if (result) {
      res.redirect('/');
      console.log(hopname);
      session.close()
    }
  } catch (e) {
    console.log("Something went wrong", e)
  };
});

What am I doing wrong in my cypher query to create an optional many hops to 1 beer relationship?

Thanks,

Ka

Hello @kave :slight_smile:

MATCH (b:Beer {name:{beernameParam}})
OPTIONAL MATCH (h:Hop {name:{hopnameParam}})
WITH collect(h) AS hops, b
UNWIND hops AS hop
CREATE (hop)<-[r:BREWED_WITH]-(b) 
RETURN hop, b

Regards,
Cobra

1 Like

Thanks so much Cobra, unfortunately something is still not working, but I don't know why your answer wouldn't work because the query works when I run it as a general query in Neo4j Browser like this...

Match (b:Beer {name:'...Before the Dawn'})
Optional Match (h:Hop)
Where h.name in ['Amarillo', 'Aramis', 'Aurora', 'Azacca', 'Blanc']
With collect(h) as hops, b
Unwind hops as hop
Create (hop)<-[r:BREWED_WITH]-(b) 
Return hop,b

Actually I don't even need the With and Unwind clauses if I use the Where clause (and change the variable names), it makes the expected relationships.
And when I console.log(hopname); I can see the expected multiple hops show in an array format being passed from my client-side form to my app.post on server-side. I've added ejs form code and server-side post code.

But the multiple hops to 1 beer relationships are still not being made. (1 to 1 works)

Is there a chance something is still missing from the Neo4j query? Or is there a neo4j.conf line that allows for something missing?

What is doing this?

MATCH (b:Beer {name:'...Before the Dawn'})
MATCH (h:Hop)
WHERE h.name IN ['Amarillo', 'Aramis', 'Aurora', 'Azacca', 'Blanc']
CREATE (h)<-[r:BREWED_WITH]-(b) 
RETURN h, b
1 Like

Thanks Cobra, yes your query above works in Neo4j Browser (though "Return h, b"), but if I try a dynamic version in my app...

`Match (b:Beer {name:{beernameParam}})
 Match (h:Hop {name:{hopnameParam}})
 Where h.name in [{hopnameParam}]
 Create (h)<-[r:BREWED_WITH]-(b) 
 Return h,b`, 
 {hopnameParam:hopname, beernameParam:beername});

it does not work for multiple, even though the console log shows the returned array, [ 'Ekuanot', 'Ella', 'Endeavour' ].

Is it possible that Neo4j is not parsing the array being returned to it and needs extra help?

Which language are you using (driver)?

Is it normal that you used the same paramter?

Match (h:Hop {name:{hopnameParam}})
Where h.name in [{hopnameParam}]

Moreover to use parameters you should use $beernameParam instead of {beernameParam}

1 Like

I'm using Neo4j JavaScript dirver for my Node.js app: https://neo4j.com/developer/javascript/
It seems normal using the same parameter, it's working in all my other queries and with this query in question when I only create a 1:1 relationship.
I tried changing to $

`Match (b:Beer {name: $beernameParam})
Match (h:Hop {name: $hopnameParam})
Where h.name in [$hopnameParam]
Create (h)<-[r:BREWED_WITH]-(b) 
Return h,b`, 
{hopnameParam:hopname, beernameParam:beername});

but it's still not working for creating multi relationships.

But I think the WHERE clause is useless if you already put it in the match?

`MATCH (b:Beer {name: $beernameParam})
MATCH (h:Hop {name: $hopnameParam})
CREATE (h)<-[r:BREWED_WITH]-(b) 
RETURN h,b`, 
{hopnameParam:hopname, beernameParam:beername});
1 Like

Good point, though it's still not working as expected for multiple relationships. I'm not sure what more to do, your examples are obviously correct so I'll mark one as helpful but I don't want to mislead others into thinking I've got the problem fixed.

Could you share some data to create a database? I will try to write a query on Neo4j Desktop and I will come back to you after :slight_smile:

Wow!! You are going above and beyond, thank you! I've emailed you a link to the datastore.

I put here you example data:

// Beers
CREATE (:Beer {name: "Before the Dawn", uid: "923574e8-d85f-40fd-a401-0547b21973b3"})
CREATE (:Beer {name: "I Don't Like Mondays", uid: "bf2e8ae1-c6e8-458c-a1d1-a11ec028e18d"})
CREATE (:Beer {name: "Disco Wolf", uid: "cb6cc4e4-8c2b-4b25-bd46-3e2dc6b0cf84"})
CREATE (:Beer {name: "Spillmatic", uid: "9dfbd2a7-411a-4749-994b-a055c9545283"})
CREATE (:Beer {name: "Enchantments Hazy IPA", uid: "4f522052-a4ae-4125-af68-f8c6f335cfe2"})
CREATE (:Beer {name: "Mystic Topaz", uid: "3fbfbe67-afd3-488e-9223-79b898574e0e"})
CREATE (:Beer {name: "Smoke and Mirrors", uid: "ec5374a2-0b53-4761-bbbf-f76c3cfc2329"})

// Hops
CREATE (:Hop {name: "Citra", uid: "32e04a80-eccb-11ea-adc1-0242ac120002"})
CREATE (:Hop {name: "Admiral", uid: "32e031c6-eccb-11ea-adc1-0242ac120002"})
CREATE (:Hop {name: "Amarillo", uid: "32e07a14-eccb-11ea-adc1-0242ac120002"})
CREATE (:Hop {name: "Galaxy", uid: "32e08662-eccb-11ea-adc1-0242ac120002"})
CREATE (:Hop {name: "Mosaic", uid: "32e0a7be-eccb-11ea-adc1-0242ac120002"})
CREATE (:Hop {name: "Sabro", uid: "32e0ad04-eccb-11ea-adc1-0242ac120002"})
CREATE (:Hop {name: "Simcoe", uid: "bab6c77-8c73-45be-a1f1-33c6a1886ff1"})

This query works on my database and it creates 5 relations:

MATCH (b:Beer {name:'Before the Dawn'})
MATCH (h:Hop)
WHERE h.name IN ['Amarillo', 'Admiral', 'Citra', 'Mosaic', 'Sabro']
CREATE (h)<-[r:BREWED_WITH]-(b)

Yes, thank you so much, the query works for me as well (I think I noted that above) when I directly query the datastore with Neo4j Browser. And the query work in my app when I want to make a 1 to 1 relationship. But when I query in my app to make a Many to 1 relationships, the relationships are not made. It's looking more like this is an issue with my app, but was hoping there might be something I was missing with Neo4j.
Thanks for your help!

Yeah I think it's coming from your app, check the content of variables that you sent to the query, it would be weird to see different behaviour between the desktop and the driver :slight_smile:

Howdy,

It turns out it's not an issure with my app. This is more in line with what I was looking for in a query...

const result = await session.run(`MATCH (b:Beer), (h:Hop)
                                  WHERE b.name = $beername AND h.name IN $hopnames
                                  CREATE (h)<-[r:BREWED_WITH]-(b)
                                  RETURN h.name, b.name`, 
                                  {hopnames: Array.isArray(hopname) ? hopname : [hopname], beername: beername})

Instead it's an issue of an Array being returned and 'separated' so Neo4j can post multiple items.