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.

Is it possible to limit search to nodes connected to node A, INCLUSIVE of node A?

I would like to search the graph of nodes that are connected to node A that match a property value.

I ALSO want to include node A in that search .

I want to return the nodes' relationship to A, and A would have to return something like "target".

Is this possible or must I do 2 queries?

13 REPLIES 13

This is a generalized query. It finds the anchor node based a label 'A' and on a 'key' property, and then matches it to all related nodes that have a specific property value. It then returns the anchor node and all matching related nodes in a collection. You can modify this as needed to meet your needs. 

match(n:A{key: 0})
match(n)--(m)
where m.property = 'desired property value'
return n as node, collect(m) as targets

 Does this help? 

@glilienfield you rock really appreciate the response! I have been working on this for 2 days and haven't been able to find a solution and I have scoured the documentation. ...If I can be greedy I have a follow up to answer my actual problem:

0. I have tried your response but it doesn't seem to return the "relationship" the collection m has with the "n" node, just the node itself, am I misunderstanding the response?
1. Is this a 1-step search? (meaning it only returns nodes directly connected to the target node)
2. How could this be altered to not only return the nodes, but the properties that met the criteria in an "OR" search

For example I have a PublicCompany node label, and I want to find if any of these nodes fulfill 1 of 2 criteria.

 

match(n:PublicCompany{Symbol: "AAPL"})
match(n)--(m)
where m.Market_Cap < 1000000000000
OR m.Symbol CONTAINS "A"
RETURN ...?

I would like the query to return data that includes:

Match 1. node Symbol = "AMD"
relationship to target node = "Supplier"
Properties that met criteria = ["Symbol", "Market_Cap"]
Match 2. node Symbol = "AAPL"
relationship to target node = "Target Node"
Properties that met criteria = ["Symbol"]

Is this possible in 1 query or do I have to compose multiple?

thanks for the shout out, and I am glad to help.

You are correct.  The sample query I posted does not return the relationships and it matches only on the immediate relationship. 

I used a shortcut pattern of (n)--(m) to represent node n being directly related to node m with any type and direction of relationship. The following query uses the match pattern (n)-[r]-(m), which is the same as the shortcut, but this one included the relationship r so we can return it. There are many ways to return this data. In this example, I collected all the related nodes and their relationship and maps, which the rel key containing the relationship and the node key containing the related node. 

match(n:Label{key: 0})
match(n)-[r]-(m)
where m.property = 'desired property value'
return n as node, collect({rel: r, node: m}) as targets

 You can change the match pattern to look for paths of greater depth than one. The following is an example specifying paths that are between 1 and 5 hops. Both the minimum number of hops and the maximum number of hops is optional. The * alone indicates no limits. I returned the path 'p' in this case, You can get the nodes along the path from nodes(p), there relationships from relationships(p), and the length with length(p). 

match(n:Label{key: 0})
match p=(n)-[r*1..5]-(m)
where m.property = 'desired property value'
return p

 When you return a node, you get the node's id, the node's labels, the node's properties. The will be returned in a json object. You can get the node's labels from labels(node) and its properties from properties(node). 

In regards to your specific query, I did not understand your requirements exactly, but this example illustrates many of the principles you can use.  I added 'Supplier' label to the target node to restrict relationships to just Suppliers. I added a number of individual properties to the return statement with aliases that have spaces to illustrate the use of the back tick. 

match(n:PublicCompany{Symbol: "AAPL"})
match(n)-[r]-(m:Supplier)
where m.Market_Cap < 1000000000000
OR m.Symbol CONTAINS "A"
RETURN n.Symbol as `Public Company Symbol`, type(r) as Relationship, m.Market_Cap as `Supplier Market Cap`, m.Symbol as `Supplier Company Symbol`

 Let me know if you need help tuning it to meet your requirements.

You may also find this part of the cypher manual informative. 

This is SUPER helpful thank you!

The additional nuance I have is this:

I ALSO want to check if the "target company" (n.Symbol = "APPLE") meets the criteria.

So I want to search ALL nodes that are connected to "APPL", AND, "AAPL" itself!

ALSO, I want to return what properties (out of the 2) met the criteria.

- Search "AAPL" directly connected nodes, AND "AAPL" itself
- Check if 2 or more property conditions are met
- return the nodes, their relationships to "AAPL", and the properties that met the criteria (can be 1 or more)
- AND what properties of "AAPL" met the criteria (if any)

What is your data model, i.e labels, relationships, and properties.  I only need the ones we are concerned with. Is it something like this:

Screen Shot 2022-12-23 at 1.57.50 PM.png

1) I assume you will specify a specific node, and the target nodes are the ones related to the specific node. 

2) What are the properties and conditions on each that you want to test? 

3) You want to know whether the specific node is also a target node, through a self reference? If so, is true/false what you are looking for?

4) When you say 'return the node', do you want the node entity or do you want the properties of the node, or specific properties of the node?

5) for each 'source' node, do you want the target node data collected in a list, or do you want a separate result row for each target node?

I can write the query if I get this? 

Really can't thank you enough, I have been banging my head against a wall!

There is only 1 node label: PublicCompany

There are 3 relationships between PublicCompany nodes: supplier, buyer, competitor

I have ~20 properties for the PublicCompany node: Symbol, Company _Name, Company_Type, Industry, Market_Cap, Revenue, Goods_Services, Input_Materials, Suppliers_Description, Buyers_Description, Competitors_Description, Contractors, Contractors_locations, Employees, Employees_details, Employee_locations, Cash_holdings, Debt_Amount, Debt_To_Equity, Debt_State, Property_Plant_Equipment, Production_Process, Technology_Infrastructure, Intellectual_Property, Licenses, Capital_Expenditures, Research_And_Development, Risks, Distribution, Sales_And_Marketing, Sells_Direct_To_Consumers

1. Correct. I will specify a Symbol of a company, and would like to search that node, as well as all nodes directly connected to that node. So if the Symbol is "AAPL", I want to check the "AAPL" node, as well as all nodes connected to the "AAPL" node.

2. This will vary, but an example is from above, 2 properties that have a condition to pass (it can be 2 or more properties being searched from the properties list above)

where m.Market_Cap < 1000000000000
OR m.Symbol CONTAINS "A"

3. Correct. Out of that list of nodes (the nodes connected to "AAPL" and "AAPL itself), I would like when returned the node to know what the relationship was to the "AAPL" node.
So non -"AAPL" nodes can have relationship: supplier, buyer, competitor
But "AAPL" doesn't have that kind of relationship with itself

4. I would like to return the node, AND the properties that met the criteria.
So if "Market_Cap < 100000000000" was met I would like to return property "Market_Cap"
if "Symbol CONTAINS "A"" was met, I would like to return the property "Symbol"

This way I know what properties were met to meet the condition.

5. Either or, I will need to parse to make JSON anyway.

The closest I have gotten is this:

MATCH (n:PublicCompany)-[r]-(a:PublicCompany {Symbol: "AAPL"})
WHERE n.`Market Cap` > 1000000000000
OR n.Symbol CONTAINS "A"
RETURN n, [key IN keys(n) WHERE ((n[key] > 1000000000000 AND key = "Market Cap") OR (n[key] CONTAINS "A" AND key = "Symbol"))], r

It returns the node, the relationship, and the properties that met the condition

What ^ is missing, is it DOESN'T CHECK the "AAPL" node for those conditions as well. It only checks the nodes connected to "AAPL".

Try this:

 

MATCH (a:PublicCompany {Symbol: "AAPL"})
CALL {
    with a
    return {
        symbol: a.Symbol,
        Market_Cap: a.Market_Cap,
        properties: properties(a),
        related_self: exists((a)-->(a))
    } as node_map
}
MATCH (a)-[r]-(n:PublicCompany)
WHERE a<>n 
AND (n.`Market Cap` > 1000000000000
    OR n.Symbol CONTAINS "A")
RETURN {
    node: node_map,
    target_nodes: collect({
        symbol: n.Symbol,
        Market_Cap: n.Market_Cap,
        relationship: type(r),
        properties: properties(n)
    })
}

 

It returns the data in a json object.  For the node (Apple in your example), the data is returned in the 'node' key. The value is a json object with company's symbol, its market cap, a map of all its properties, and a boolean value that is true if the node references itself. I added market cap as a separate property to show you how. Its value is in the properties map as well. The market cap is added to the output on line 6 and the properties on line 7. Pick the output style you want. You can add more individual properties using the syntax used on line 6. 

The target nodes are a collection of json objects, one per target node. Each contains the target company's symbol, market cap, all properties, and the relationships type. Again, remove properties or market cap, since market cap is in properties. Add more individual properties the same as for the node. This is all done on lines 18-21. 

This is sample output from test data I made up:

Screen Shot 2022-12-23 at 4.32.53 PM.png

 

{
  "node": {
    "symbol": "AAPL",
    "Market_Cap": 2000000000000.0,
    "related_self": true,
    "properties": {
      "Market_Cap": 2000000000000.0,
      "city": "DC",
      "Symbol": "AAPL",
      "key": 100
    }
  },
  "target_nodes": [
    {
      "symbol": "GE",
      "Market_Cap": 1500000000000.0,
      "relationship": "BUYER",
      "properties": {
        "Market_Cap": 1500000000000.0,
        "city": "Rockville",
        "Symbol": "GE",
        "key": 300
      }
    },
    {
      "symbol": "AMD",
      "Market_Cap": 1100000000000.0,
      "relationship": "SUPPIER",
      "properties": {
        "Market_Cap": 1100000000000.0,
        "city": "Bethesda",
        "Symbol": "AMD",
        "key": 200
      }
    }
  ]
}

 

 

 

OOPS. I had copied some of your query, but realized it was wrong. The copy pasted above has the error in it.  The condition below doesn't use the property of the node. You had changes this in your example query. 

n.`Market Cap`

 Here is the one with the correct property name in the 'where' clause:

MATCH (a:PublicCompany {Symbol: "AAPL"})
CALL {
    with a
    return {
        symbol: a.Symbol,
        Market_Cap: a.Market_Cap,
        properties: properties(a),
        related_self: exists((a)-->(a))
    } as node_map
}
OPTIONAL MATCH (a)-[r]-(n:PublicCompany)
WHERE a<>n 
AND (n.Market_Cap > 100e+10
    OR n.Symbol CONTAINS "A")
RETURN {
    node: node_map,
    target_nodes: collect({
        symbol: n.Symbol,
        Market_Cap: n.Market_Cap,
        relationship: type(r),
        properties: properties(n)
    })
}

For some reason, I can no longer edit a post.  

Darn, another error.  I was toying with the idea of using an 'optional match' so you would still get a result if the source node (apple) was not related to any other node. In that case, the list will have one json object with all null values. It is up to you if you leave it as 'optional match' or just 'match'.

In your example does it ALWAYS return symbol and Market_Cap?

I would like them to return the properties that MEET criteria. So "Symbol" is only returned if it meets the WHERE criteria (symbol CONTAINS "A")

It is going to always return the symbol and market cap. It would be easy to change it to return null or false instead when the condition is not met. Or, I can add another property for each that is true or false, indicating if the condition was met. There is no direct way I know to do this in cypher. You could use a case statement to return a map with it or one without it, based on the condition. In your case, you have four possibilities, so it would be really awkward. 

You could use apoc methods to conditionally add each property to the map using apoc.when for the conditional part, and apoc.map.setKey to add the property when the condition is true.  This would be my preferred solution. Just a couple lines of code for each property. You need the apoc library installed though.

You are a LEGEND THANK YOU!

Thank you!

Do you have a patreon or something?

Thank you, but that is not necessary.  Let me know if you need help with changes....