cancel
Showing results for 
Search instead for 
Did you mean: 

Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.

How to Post and Create Multiple Nodes from Passed Array

keithave
Node Clone

Greetings,

I'm using Node.js, Express, & Neo4j.

I have a form that passes parameters to the server and they are being passed, but 1 parameter is a single item and the other parameter contains multiple objects in an array. I'm trying to create all of these from scratch and then relate one to the many. At this point what I have works, but instead of one to many I can only create 1 node from each different parameter and relate them.


Here's my form code on the ejs page...

<form method="post" action="/datasource/add"><% pubDataSource.forEach(function(pds){ %>
    <input  type="text" name="datasourcename" id="title" value="<%= pds.name %>">
    <% pds.fields.forEach(function(fieldName) { %>
    <input  type="text" name="datasourcefieldname" id="title" value="<%= fieldName.name %>">
    <% }) %><% }) %>
    <input class="button" type="submit" value="Submit">
</form>

 

The items being passed are this...

datasourcename = Datasource_Model

datasourcefieldname = ['fieldnameOne', 'fieldnameTwo', 'fieldnameThree', 'fieldnameFour', 'fieldnameFive', ...]

Here is my server code...

app.post('/datasource/add', async (req, res) => {
  const {datasourcename} = req.body;
  const {datasourcefieldname} = req.body;
  try {
    const result = await session.run(
`Create(ds:DataSource {Title: $datasourcenameParam})
Create(dsf:DataSourceField {Title: $datasourcefieldnameParam})
Create (ds)-[:HAS_FIELD]->(dsf)
Set ds.date = apoc.date.format(timestamp(),"ms","MM/dd/yyyy"), ds.Uid = apoc.create.uuid() ,dsf.date = apoc.date.format(timestamp(),"ms","MM/dd/yyyy"), dsf.Uid = apoc.create.uuid()
Return ds,dsf`,
{
datasourcenameParam:datasourcename, datasourcefieldnameParam: datasourcefieldname})
 
    if (result) {
      res.redirect('/gql');
      console.log(datasourcename, datasourcefieldname);
      session.close()
    }
  } catch (e) {
      console.log("Something went wrong", e)
  };
});

When I submit the data, data is created in Neo4j, but it's only creating 2 nodes: 1 ds:DataSource node and 1 dsf:DataSourceField node (the first one seen in the array) and then they are related as expected.

I don't know how to get my query to create all of the expected dsf:DataSrouceField nodes.

Thank you for any help you can provide.

 

1 ACCEPTED SOLUTION

glilienfield
Ninja
Ninja

The way I read it, it should create one DataSourceField node whose title is an array of values. Is this not the case? 

Assuming datasourcefield name is an array and you want to create a node for each element, you have two options. You can either ‘unwind’ the array into rows, so each can be used to create a node, or you can use a ‘forEach’ loop. 

For the unwind approach, insert between the create ‘ds’ and ‘dsf’ nodes the ‘unwind’ statement;

unwind $datasourcefieldnameParam as fieldname

then change the ‘Title’ value when creating the ‘dsf’ node to ‘fieldname’. Finally, change your relationship ‘create’ to a ‘merge’. 

For the ‘forEach’ approach, you would enclose the final two create statements in a ‘forEach’ loop iterating over $datasoucrefieldnameParam. Again, you need to change the relationship ‘create’ to a ‘merge’ 

https://neo4j.com/docs/cypher-manual/current/clauses/unwind/#unwind-creating-nodes-from-a-list-param...

https://neo4j.com/docs/cypher-manual/current/clauses/foreach/

View solution in original post

5 REPLIES 5

glilienfield
Ninja
Ninja

The way I read it, it should create one DataSourceField node whose title is an array of values. Is this not the case? 

Assuming datasourcefield name is an array and you want to create a node for each element, you have two options. You can either ‘unwind’ the array into rows, so each can be used to create a node, or you can use a ‘forEach’ loop. 

For the unwind approach, insert between the create ‘ds’ and ‘dsf’ nodes the ‘unwind’ statement;

unwind $datasourcefieldnameParam as fieldname

then change the ‘Title’ value when creating the ‘dsf’ node to ‘fieldname’. Finally, change your relationship ‘create’ to a ‘merge’. 

For the ‘forEach’ approach, you would enclose the final two create statements in a ‘forEach’ loop iterating over $datasoucrefieldnameParam. Again, you need to change the relationship ‘create’ to a ‘merge’ 

https://neo4j.com/docs/cypher-manual/current/clauses/unwind/#unwind-creating-nodes-from-a-list-param...

https://neo4j.com/docs/cypher-manual/current/clauses/foreach/

Thanks so much @glilienfield! I'm going with the Unwind version, and think I have followed your directions and the format provided in the Unwind link, but have an error, here's my query now...

`Merge (ds:DataSource {Title: $datasourcenameParam})
With ds
Unwind $datasourcefieldnameParam as fieldname
Merge (dsf:DataSourceField {Title: fieldname.Title})
Merge (ds)-[:HAS_FIELD]->(dsf)
Set ds.date = apoc.date.format(timestamp(),"ms","MM/dd/yyyy"), ds.Uid = apoc.create.uuid(), dsf.date = apoc.date.format(timestamp(),"ms","MM/dd/yyyy"), dsf.Uid = apoc.create.uuid() 
Return ds,dsf`, {datasourcenameParam:datasourcename, datasourcefieldnameParam: datasourcefieldname}

And it returns this error...

Something went wrong { Neo4jError: Type mismatch: expected a map but was String("fieldnameOne")

What am I doing wrong?

 

The value 'fieldnameOne' is one of your elements from $datasourcefieldnameParm that you are passing. After unwinding it into 'fieldname', the value of 'fieldname' for each row is a string. You are trying to access the property 'Title' from this string on line 4, when you are setting the value of 'Title.' This is the cause of the error since 'fieldname'  is not a map. 

Also, your pasted code has a erroneous backpack on lines 1 and 7.  I assume they are not in the original code you are executing. 

Note, you should move the setting of ds.date and ds.Uid to after the merge to get 'ds', as it is setting it multiple times where it is. Have a set after line 1 for the 'ds' properties and have a set after line 4 for the 'dsf' properties. 

Ah right on thanks all around! yes, the backticks are required by the node server but not the cypher itself

keithave
Node Clone

Here we go, did a bit more tweaking (that Unwind documentation seems to want to add an extra key bit that wasn't helpful)

Merge (ds:DataSource {Title: $datasourcenameParam})
With ds
Unwind $datasourcefieldnameParam as fieldname
Merge (dsf:DataSourceField {Title: fieldname})
Merge (ds)-[:HAS_FIELD]->(dsf)

Many Thanks @glilienfield