Reputation: 119
I am having a problem in a Neo4j query. Suppose I have a Node type called App. The App nodes have the fields "m_id" and "info". I want to build a query to create a relationship between the nodes where the field "info" is equal.
This is the query:
MATCH (a:App {m_id:'SOME_VALUE' }),(b:App {info: a.info}) WHERE ID(a)<>ID(b) AND NOT (b)-[:INFO]->(a) MERGE (a)-[r:INFO]->(b) RETURN b.m_id;
I also have indexes for both fields:
CREATE CONSTRAINT ON (a:App) ASSERT a.m_id IS UNIQUE;
CREATE INDEX ON :App(info);
But the thing is I get very slow queries, with access in all the records of the App nodes.
This is the profile of the query:
+---------------+--------+---------+-----------------+--------------------------------------------------------------------------------------------------------------------------------+
| Operator | Rows | DB Hits | Identifiers | Other |
+---------------+--------+---------+-----------------+--------------------------------------------------------------------------------------------------------------------------------+
| +ColumnFilter | 0 | 0 | b.m_id | keep columns b.m_id |
| | +--------+---------+-----------------+--------------------------------------------------------------------------------------------------------------------------------+
| +Extract | 0 | 0 | a, b, b.m_id, r | b.m_id |
| | +--------+---------+-----------------+--------------------------------------------------------------------------------------------------------------------------------+
| +Merge(Into) | 0 | 1 | a, b, r | (a)-[r:INFO]->(b) |
| | +--------+---------+-----------------+--------------------------------------------------------------------------------------------------------------------------------+
| +Eager | 0 | 0 | a, b | |
| | +--------+---------+-----------------+--------------------------------------------------------------------------------------------------------------------------------+
| +Filter | 0 | 2000000 | a, b | Ands(b.info == a.info, NOT(IdFunction(a) == IdFunction(b)), NOT(nonEmpty(PathExpression((b)-[anon[104]:INFO]->(a), true)))) |
| | +--------+---------+-----------------+--------------------------------------------------------------------------------------------------------------------------------+
| +SchemaIndex | 184492 | 1000000 | a, b | { AUTOSTRING0}; :App(m_id) |
| | +--------+---------+-----------------+--------------------------------------------------------------------------------------------------------------------------------+
| +NodeByLabel | 184492 | 1000001 | b | :App |
+---------------+--------+---------+-----------------+--------------------------------------------------------------------------------------------------------------------------------+
Upvotes: 1
Views: 47
Reputation: 119
I figure out a solution using OPTIONAL MATCH:
MATCH (a:App {m_id:'SOME_VALUE' }) OPTIONAL MATCH (a),(b:App {info: a.info}) WHERE ID(a)<>ID(b) AND NOT (b)-[:INFO]->(a) MERGE (a)-[r:INFO]->(b) RETURN b.m_id;
This is the profile of the query:
+----------------+------+---------+-----------------+------------------------------------------------------------------------------------------------------------+
| Operator | Rows | DB Hits | Identifiers | Other |
+----------------+------+---------+-----------------+------------------------------------------------------------------------------------------------------------+
| +ColumnFilter | 0 | 0 | b.m_id | keep columns b.m_id |
| | +------+---------+-----------------+------------------------------------------------------------------------------------------------------------+
| +Extract | 0 | 0 | a, b, b.m_id, r | b.m_id |
| | +------+---------+-----------------+------------------------------------------------------------------------------------------------------------+
| +Merge(Into) | 0 | 1 | a, b, r | (a)-[r:INFO]->(b) |
| | +------+---------+-----------------+------------------------------------------------------------------------------------------------------------+
| +Eager | 0 | 0 | a, b | |
| | +------+---------+-----------------+------------------------------------------------------------------------------------------------------------+
| +OptionalMatch | 0 | 0 | a, b | |
| |\ +------+---------+-----------------+------------------------------------------------------------------------------------------------------------+
| | +Filter | 0 | 0 | a, b | Ands(NOT(IdFunction(a) == IdFunction(b)), NOT(nonEmpty(PathExpression((b)-[anon[109]:INFO]->(a), true)))) |
| | | +------+---------+-----------------+------------------------------------------------------------------------------------------------------------+
| | +SchemaIndex | 0 | 0 | a, b | a.info; :App(info) |
| | | +------+---------+-----------------+------------------------------------------------------------------------------------------------------------+
| | +Argument | 0 | 0 | a | |
| | +------+---------+-----------------+------------------------------------------------------------------------------------------------------------+
| +SchemaIndex | 0 | 1 | a | { AUTOSTRING0}; :App(m_id) |
+----------------+------+---------+-----------------+------------------------------------------------------------------------------------------------------------+
Upvotes: 0
Reputation: 67044
Try finding a
by itself, using a WITH
clause to put a.info
into a temporary variable that is used by a separate MATCH
clause for b
, as in:
MATCH (a:App { m_id:'SOME_VALUE' })
WITH a, a.info AS a_info
MATCH (b:App { info: a_info })
WHERE a <> b AND NOT (b)-[:INFO]->(a)
MERGE (a)-[r:INFO]->(b)
RETURN b.m_id;
It seems that indices tend not to be used when comparing the properties of 2 nodes. The use of a_info
removes that impediment.
If the profile of the above shows that one or both indices are not being used, you can try adding index hints:
MATCH (a:App { m_id:'SOME_VALUE' })
USING INDEX a:App(m_id)
WITH a, a.info AS a_info
MATCH (b:App { info: a_info })
USING INDEX b:App(info)
WHERE a <> b AND NOT (b)-[:INFO]->(a)
MERGE (a)-[r:INFO]->(b)
RETURN b.m_id;
Upvotes: 1