cancel
Showing results for 
Search instead for 
Did you mean: 

Add list or map to field

tideon
Graph Buddy

Hello Everyone,

I am attempting to take a list of values and add to a field, but the manual is so vague and only uses the return clause to show List & maps, but nothing on how to for instance use the SET clause to actually assign it to a field on a node.

So here is an example of what I am trying to achieve.


SET gd.Postcode = "1234 HA"
SET gd.City = "London"
SET gd.URL = "https://somewhere.com"
SET gd.Telfoon = "03-6336000"
SET gd.Fax = "02344-6515328"
SET gd.BankAccounts = [{ABN-AMRO:"NL56 ABNA 123.456.780}, {ING:123.456.789}]

I can't get the bank account assignment to work. I would like to have a series of bank accounts in a list.

1 ACCEPTED SOLUTION

clem
Graph Steward

How to add a list to an attribute:

MATCH(p:Person{name:'Tom Hanks'})
SET p.favoriteThings = ['rain drops', 'boxes without tops']
RETURN p

returns:

{
  "identity": 71,
  "labels": [
    "Person"
  ],
  "properties": {
"name": "Tom Hanks",
"favoriteThings": [
      "rain drops",
      "boxes without tops"
    ],
"born": 1956
  }
}

Note that the list can't even be heterogeneous, e.g. it can't have a mix of strings and numbers.

Generally the Neo4J documentation is pretty good, but there are definitely places where it lags and where it could use more examples or better explanation.

I think given where they are, they've done a commendable job.

View solution in original post

32 REPLIES 32

johnmattgrogan
Node Clone

Hi tideon,

What error are you receiving? It looks like you have one ' " ' in [{ABN-AMRO:"NL56 ABNA 123.456.780}, {ING:123.456.789}] that could be causing an issue.

Hello John,

This is the error


Invalid input '{': expected "+" or "-" (line 10, column 24 (offset: 334))
"SET gd.BankRekening = [{ABN-AMRO:"NL56 ABNA 123.456.780]"

So I changed it to the following and still get an error


SET gd.BankRekening = [{ABN-AMRO:"NL56 ABNA 123.456.789"}]

The error is

Invalid input '{': expected "+" or "-" (line 10, column 24 (offset: 334))
"SET gd.BankRekening = [{ABN-AMRO:"NL56 ABNA 123.456.789"}]"

What is the correct syntax to have this work?

johnmattgrogan
Node Clone

Can you set the gd.BankRekening = {"ABN-AMRO" : "NL56 ABNA 123.456.789" , "ING":"123.456.789"}

I'm doing some searching, however I'm not sure a List can contain Map types. Does your map need to be stored in a list?

I got a similar error. I have been at it reading all over the internet for two days. The manual is also so vague.

The reason I wanted a list with key value pairs is because certain companies can have one bank account whilst others can have multiple, so it makes sense to have a list that I can call up.

johnmattgrogan
Node Clone

So it seems a check of the manual would have been a good place for both of us

The property types:

  • Number , an abstract type, which has the subtypes Integer and Float
  • String
  • Boolean
  • The spatial type Point
  • Temporal types: Date , Time , LocalTime , DateTime , LocalDateTime and Duration

The adjective numeric , when used in the context of describing Cypher functions or expressions, indicates that any type of Number applies (Integer or Float).

Homogeneous lists of simple types can also be stored as properties, although lists in general (see Composite types) cannot be stored.It

tideon
Graph Buddy

Thank for finding that, but i still don't understand what it is pointing at. It seems just say that something is not possible, but not showing me what is and also what solution I can use for my situation.

So could you write the code that would work, because I am not grasping it.

johnmattgrogan
Node Clone

Is there a specific reason for needing the map / dictionary as a property?

Could you create a relationship with an account # as a property to a bank node?

(user)-[account:HAS_ACCOUNT {number:'abc123'}]->(b:Bank {bank:'bank123')

It is that a company can have multiple bank accounts, so it is a variable amount. So when I write a query to the bank accounts I would like to just like to be presented all the bank accounts

A Company having multiple Bank Accounts sounds very much like a bank account should be a node, with a relationship connecting them.

If you want the results back as a list of bank account properties for a given company, you can absolutely create a query to MATCH to what you need and return it in that format.

Hello Andrew,

Didn't think of it like that. See I was learning Python before I started learning NEO4j, so after undestanding data structures from PHP & Python, then seeing maps and lists here I figured the were also able to be a means of storage, But they seem to only be a means of presentation / collecting single pieces of nodal data fields.

So my question still remains about list. How can you assign a list to a field?

clem
Graph Steward

This is a limitation of Neo4J...

What you want to do seems like it should be allowed but it is not.

What you can do, is convert your complex data structure into a string and store the string. And when you need a map, convert the string back into a complex data structure.

This may help:

Hello Clem,

THis is crazy that this is not possible. I am truly stunned.

  1. So if I understand it, you cannot store a list in a field?
  2. You cannot store a a map in a field?

Really curious to the response to the two questions.

clem
Graph Steward

You can store a simple list in a field, not a list that contains complex items in it. However, you can't create index the list's individual elements.

You can't store a map.

It's a bit of an engineering trade-off in terms of time and resources: do you want to make a system that can handle all the complexity you want? Or do focus your limited engineering resources on other things?

OTOH, this isn't necessarily a bad thing. It forces the schema designer to make a schema that's "cleaner". If you have a complex data structure stored in an attribute, it's hard to use the built-in Cypher language to access the individual components. But if the components are made into individual attributes, then you have the full power of Cypher.

For example, instead of list, why not have the items be nodes and have a relationships to those nodes? Those nodes can be indexed (for faster retrieval.) There may be other interrelationships between those nodes that could be interesting.

I think the theme of Neo4J is create the nodes and relationship so you can start making queries that would be difficult to make in SQL. You also have a lot of flexibility in Neo4J, so if your understanding changes, it's relatively simple to rejigger your schema (using APOC functions.)

Every language/system has its strong and weak points. You just have to pick the right tool for the right job.

Hello Clem,

Can you write the example code for writing a list to a field? To my utter disbelief the manual has no explanations of how this is done.

clem
Graph Steward

How to add a list to an attribute:

MATCH(p:Person{name:'Tom Hanks'})
SET p.favoriteThings = ['rain drops', 'boxes without tops']
RETURN p

returns:

{
  "identity": 71,
  "labels": [
    "Person"
  ],
  "properties": {
"name": "Tom Hanks",
"favoriteThings": [
      "rain drops",
      "boxes without tops"
    ],
"born": 1956
  }
}

Note that the list can't even be heterogeneous, e.g. it can't have a mix of strings and numbers.

Generally the Neo4J documentation is pretty good, but there are definitely places where it lags and where it could use more examples or better explanation.

I think given where they are, they've done a commendable job.

Thank you Clem for this, this is so clear to understand.
I would like to ask Andrew to add this to the manual as a clear example. This is clearer than anything there right now. Your entire explanation.

clem
Graph Steward

You can propose a change (which will be managed by git), and eventually it will get pushed through. I've proposed a few changes to Neo4J doc via git and they got (eventually) accepted. (If you click on the unhappy face and write something there, it doesn't get looked at too often.)

I don't know if you've ever been involved with a big development type system, but it's hard to get it right and there are so many places to get it wrong.

This is especially true for a tech writer who's very experienced with the material trying to imagine what a newbie might have trouble understanding. (I think I'm pretty good at it...)

You'll also see some gaps with APOC functions, because often they are wrappers around Java standard libraries which aren't very well explained and APOC documentation for those tend to parrot the Java documentation which also isn't very good.

I've looked at the documentation for TigerGraph and for Gremlin. Those two are far worse shape and far more confusing and lacking.

Language design and implementation is actually quite tricky. You probably don't appreciate how difficult it is nor how far it has come in 70 or so years.

The final thing I will say, is Graph DB's aren't easy.... I was involved in a company years ago trying to do this, and we just didn't have the software tools nor the hardware capability to do this. Neo4J is quite an achievement!

Hello Andrew,

Just for clarity, I appreciate that you are doing what you do, I have read your blog and now I also know you write for the manual. I saw a video on Youube explaining this exact phenomenon, in that if someone knows a subject extremely well, there brain fills in the blanks of what it thinks someone would already know, because the brain sees many of those things as nothing new.

So I am open to helping to update the manual with feedback and also write questions that aren't answered. Last year I figured out how the REGEX works, as the manual has next to no information, so that would be one of the things I would like to contribute, as it took me almost two weeks of trial and error to understand what NEO4J was allowing and not, as it isn't as straight forward as normal REGEX in python or in SUBLIME or Visual code

I will get back to you, as I have to figure out GIT, also one thing I would like to ask, is that the feedback button on the bottom of the page has the option to write text when you click the smiley face or frown, as i wanted to give detailed feedback on a page at times, but couldn't explain what would be better.

I would also like to send you video captures showing clear example to compare what me as a newbie would have needed to see sfor it to instantly click.

clem
Graph Steward

BTW, if you're having trouble with regex (and who doesn't when it gets a bit hairy!)

try the regex debugger: https://regex101.com/

Hello Clem,

I am just getting accustom to writing things out in such an atomic form. I am more accustom of normalisation norms from the Relational database aspect. Also the way that Python & PHP would do it is to allow / use nested data structures, such as nested list and using indexes to access them.

So I will look at your suggestion, but also as askd above, I would really like to see how to assign a list to a field.

In the movies database, the :ACTED_IN relationships have a roles property which is a list of strings.

As an example of setting it:

MATCH (:Person {name:'Keanu Reeves'})-[r:ACTED_IN]->(:Movie {title:'The Matrix'})
SET r.roles = ['Neo', 'Mr. Anderson']

That's for a list literal, you could also pass in a list of strings as a parameter, or assemble the list dynamically in Cypher. If you want to add to the list you can just use the + operation on the existing list:

MATCH (:Person {name:'Keanu Reeves'})-[r:ACTED_IN]->(:Movie {title:'The Matrix'})
SET r.roles += 'Mr. Anderson'
// or SET r.roles += ['Mr. Anderson']

Same result, the first approach appends an element to the exiting list and replaces the property. The second approach is combining two lists into one.

Yes, but it's harder to search for the roles.... if they are in a list...

I posted this about a real situation from the movies (but not in the sample Movie DB...): Alec Guinness and his 9 roles in "Kind Hearts and Coronets". I'm not sure what the schema choice should be:

Hello Andrew,

I get the idea that you have some official connection to the Neo4J company. Could you update the help file to reflect this piece of knowledge you added here. Because if you look at the manual about list, nowhere does it show how to assign to a field, nor does it explain the aspect of not being able to store complex structures, same goes for the maps. nowhere does the map page explain that it can't be added to a field. And that is extremely important to know.

I have read here more clear information about list & maps, than is contained in the manual. I have studied many applications / software packages and read many manual, but this manual is really written from an engineers point of view, and there is so much missing when things are explained.

I bought the O'Reilly book you guys published, but that reads more like a : look this is what the application can do, and not as a means to learn the Neo4j platform. I read it and it more felt like it was written to entice banks and large IT infrastructures to use your platform, so i didn't learn much other than what scenarios Neo4j would flourish in and how theoretically it could solve the problem.

As with this problem I spent two days all over the internet reading trying to figure out how to use List and maps, and here in these responses I got more clarity than the manual offers.

I really think Neo4j needs to get a dedicated person who's training is to write user manual, as they are skilled in taking a technical thing ( engineers language ) and turn it into a user manual.

To put things in perspective, I an self learning Linear Algebra at the moment and I am finding it easier to understand that than understand how to do simple things in neo4j once I read the manual, as the manual often leaves crucial things out. And a good example is list. In the manual it doesn't tell you how to assign it, it only uses the return clause to show that it is able to create it. And that is often so in the manual, return is used so often, but not the practical use.

Google says you are an engineer at Neo4J

Hello,

Yes, I'm an employee at Neo4j.

I've passed along your feedback and linked the thread. While we do have the Expressions section in the docs that provides an example of list and map literals (and keep in mind that we can work with lists and maps as variables, so literal expressions don't always have to come into play when working with list properties), and documentation for the SET clause, I believe the Values and Types documentation seems very complex and tough to understand. I think there's certainly opportunity to make that clearer with some examples of what is allowed and what is not.

In addition the SET clause documentation seems like the right place to talk about what can and cannot be set as node properties, and some examples would absolutely be helpful here.

Thank you for your feedback.

Hello Andrew,

That is exactly what I meant. I really struggle with your manual. I have been reading it for a year, and almost always I have to come here to ask questions like you see in this thread. The style used to write the manual doesn't allow you to see things in full use.

And added more exampel to SET would be a good idea. We need to really know what can and cannot work. This thread made me for th firt time understand more about what cannot be added to a field.

One major issue I encounter with the manual is that when i do a search it takes me out of the scope of where I am reading. So if you go to this page The Neo4j Cypher Manual v4.2 - Neo4j Cypher Manual

and you search for list from the right hand corner. I shows you even things from APOC, it just overloads you with non-relevant things. I often just want to search within the scope I am in, so if I am in manual 4.2, I would like it to search in there only.

Hi tideon,

In the cypher manual it does list the data types that can be assigned to properties. I've fallen into the trap of trying to make cypher act like python, and I've found for me this is usually a red flag that I may need to re think my design. The flexibility of having data stored as relationships rather than key / value pairs can make your life easier as the model gets more complex. However I understand every scenario has it's trade offs.

Values and types - Neo4j Cypher Manualvpwzyr_gaMTk5Njg5NTU3LjE2MTQyMjUyMDA._ga_DL38Q8KGQC*MTYyMjIyMjYwNC44LjEuMTYyMjIyMzUzMy4w&_ga=2.164667903.1541417655.1622214281-199689557.1614225200

Hello Andrew,

I was even on your blog reading about list, but there also I could not find anything from your post on list on how actually to asign it ot a field, and that maps and nested structures are not allowed. Even the manual doean't mention this crucial fact as far as I recall.

johnmattgrogan
Node Clone
MATCH (u:User {id:'abc'})-[acct:HAS_ACCOUNT]->(bank:Bank)
RETURN u.id, bank.id, acct.account_id

This will get you most of the way there. If you need those 1 user per row with the map of bank/accounts there are some apoc functions that seem to do the trick. However, I'm not an APOC wizard so I will leave that to someone else.

tms
Graph Buddy

Hi Tideon,

I too came to Cypher and Neo4J about a year ago and have had similar "attunement" issues (I have deep OO/Python/Javascript/Smalltalk experience). I call the structure that you contemplate for gd.BankAccounts an OrderedDictionary -- a list where each element is a pair and where order is preserved. As others have suggested, I think you've inadvertently created what we EE's would call an "impedance mismatch" between your contemplated structure and the natural semantics of Neo4J.

I've found it helpful to draw the data structure on a piece of paper or whiteboard. When I did this just now, I drew a circle for "gd", and an arc to another circle that I labeled "BankAccount". I draw each property as a horizontal rectangle divided into left and right cells. The left contains the property name, the right contains the property value. For each BankAccount node, I added the following properties: name, accountCode, and accountNumber. I drew two instances of BankAccount (each a node), one for each element of your list. I connected those with a bidirectional "next/previous" relationship.

Now I transliterate that drawing into a Cypher query. I like to use the Neo4J browser (https://your.domain.name:7474) as a REPL so that I fiddle with it. I haven't tried it, but I think it looks something like:

MATCH (gd: Party)-[BANK_ACCOUNTS: LINK]->(abn_amro: BankAccount {name: "ABN-AMRO", accountInformation: "NL56 ABNA 123.456.789"})-[NEXT: LINK]->(ing: BankAccount {name: "ING", accountInformation: "123.456.789"}) RETURN gd

The advantage of doing it this way is that you gain all the power of Neo4J in managing these lists. In particular, each list can be queried and manipulated. After you've created a few thousand gd instances, it's going to be tedious if you find yourself needing to add an additional field to each BankAccount element and you've built out your original approach.

It's analogous to a design question that comes up in Python (and other OO languages) all the time -- when I want to model a dictionary of key-value pairs, do I use a dictionary or create a new class/object. Most languages (Cypher, Python, PHP, etc) have at least an implicit bias towards the answer to that question. In a language like C, Java, PHP and others, creating and then instantiating a new class is a heavyweight operation and might lead to the approach you've contemplated. In Neo4J/Cypher, creating nodes and relationships is MUCH easier.

One final thought that might help -- consider how you'd store this BankAccounts field in relational DB like MySQL. While it might be tempting to express it as, for example, a JSON string that is stored in a string-valued column (VARCHAR or whatever), that approach would give your DBAs apoplexy. If you were designing a SQL schema for this, you'd almost immediately start thinking about a separate "BankAccount" table, foreign keys, and joins. THAT is your tipoff that your best bet in Neo4J is promote the BankAccount property to a relationship named bankAccounts added to gd and then connecting instances of a BankAccount node in an list using instances of Relationship.

I hope this helps!

ponceortiz
Node Clone

Hi , 

I've used the function toFloatList()  in my data to create an list property. I have a Coffe DataSet , with some coffee properties aroma, bode, acidity ... and the altitude in meter.

this is the how I used it 

MATCH (c:Coffee_Bean) 
  RETURN toFloatList([c.altitude,c.moisture,c.aroma])  
and my result looks like this  
[2075.0, 0.12, 8.25]

Hope this is what your need