Replacement of RDBMS views in Neo4j

Hi Team,

We are in process to upgrade our current inventory system (in oracle) to neo4j. We have successfully migrated data from RDBMS to Graph database. But we have one implementation problem in terms of interface with other systems.
In some existing implementation, other systems direct access our inventory database and fetch data using oracle views . Once we have migrated our data in neo4j, we couldn't find any mechanism like views in neo4j.
Can anyone help us to find optimum and best solution by which we can replace views in neo4j with minimum changes on systems (using our data)?

Since the concept of tables doesn't exist in Neo4j, we don't really have the concept of views. That said, it might be a possibility for the future.

If you want someone to see some subset of the graph, then you can use our role based access controls to grant permission to view/match/traverse/edit only the subset of labels and/or relationships and/or properties you want them to see.

Thanks Andrew for you assistance . but my business use case is slightly different where right now i am not much concerned about the privileges or access but the mechanism of invocation .
In my current implementation, users or other systems (who are directly access our oracle data by using simple view name with where clause as below)
select * from inventory_data where type='ABC';

I have the definition of this view that i can convert into Neo4j cypher statement but my biggest challenges is how calling systems will use this cypher statements in place of oracle views.
In RDBMS we can save query definition in terms of views that gets easier in invocation but in Neo4j we couldn't found any such arrangement.

could you please help me out how to handle such requirements with minimal changes ?

Would Neo4j custom stored procedures work?

I have been exploring this recently and while I don't have exact same use case, its sort of similar. You can create a procedure to perform specific queries so users can call a simple procedure vs having to know the exact cypher logic.

CALL apoc.custom.asProcedure('inventory_data', '<cypher query / view logic>','read', [<..output format..>],[['type','STRING']])

CALL custom,inventory_data('ABC') YIELD <output_name> as result
RETURN result

Thanks Tom for guidance. Neo4j custom stored procedure really nice approach to deal my problem. But i couldn't found in my version . I am using enterprise 3.5.5 . Please let me know if custom stored procedure are available in 3.5.5 or any plugin by which we can use this feature in our version.

Hi Tom, Please ignore my previous communication. I have found apoc.custom.asProcedure and apoc.custom.asFunction in our 3.5.5 version. We will try to implement as suggested by you.

Hi Team,

I am trying to use apoc.custom.asProcedure with following syntax
CALL apoc.custom.asProcedure('test',
"MATCH (d:classes{name:'gEquipment'})-[:HAS_ATTRIBUTE]->(n) return d.name as name1",'read',[['name1','String']], 'Get tested');

but it is returning following error
Neo.ClientError.Statement.SyntaxError: Type mismatch: expected List<List> but was String (line 2, column 118 (offset: 154))
" "MATCH (d:classes{name:'gEquipment'})-[:HAS_ATTRIBUTE]->(n) return d.name as name1",'read',[['name1','String']], 'Get tested');"

We are using Enterprise 3.5.5 version.
Can anyone help me to resolve this issue?

So this is just an issue of supplying the right parameters to the apoc.custom.asProcedure() call.

You can view the expected signature with call apoc.help('asProcedure'). I'll paste the signature here for you:

"apoc.custom.asProcedure(name :: STRING?, statement :: STRING?, mode = read :: STRING?, outputs = null :: LIST? OF LIST? OF STRING?, inputs = null :: LIST? OF LIST? OF STRING?, description = null :: STRING?) :: VOID"

You supply correct parameters for name, statement, mode, and outputs, but the the next parameter, inputs, requires a list of list of strings, but you provided 'Get tested', which is a string, so that's the wrong type. I think you intended to have that as the description, and you didn't see that there is an inputs parameter that has to be provided first.

Hi Team,
We have successfully implemented Apoc procedure to replace views. But now we are facing performance issues while using apoc procedures in our cypher queries.

Let me detail out the implementation
I have created one Apoc procedure path_element_v with one imput parameter (path_type) , this apoc procedure returns all path details of this path type passing as input.
now we are using this apoc procedure in another Apoc procedure using apoc.cypher.run and execute it for each path name as below

call apoc.custom.asProcedure('RIL_OCH_PATH_TERMINATION_V','match (a:inventoryObjects{type : $param})-[r:INSTANCE_OF]->(c:classes{name:'gPath'})
with a
optional match (b:inventoryObjects{site_inst_id : a.a_side_site_id})-[r:INSTANCE_OF]->(c:classes{name : 'gFacility'})
with a,b
optional match (d:inventoryObjects{site_inst_id : a.z_side_site_id})-[r:INSTANCE_OF]->(c:classes{name : 'gFacility'})
with a,b,d
CALL apoc.cypher.run(
"call custom.path_element_v('gPath') yield path_element_parent_equip as path_element_parent_equip,
path_element_port as path_element_port,path_pkey as path_pkey,path_element_no as path_element_no
where path_pkey = "+ a.circ_path_inst_id +"
AND path_element_port is not null
RETURN path_element_parent_equip as path_a_equip_slte,path_element_port as path_a_port
ORDER BY path_element_no ASC
LIMIT 1",{}
) yield value
return a.name AS path_name,
a.type AS path_type,
a.status AS path_status,
a.bandwidth AS path_bandwidth,
b.name AS path_aside_site,
d.name AS path_zside_site,
value.path_a_equip_slte as path_a_equip_slte',
'read',[['path_name','String'],['path_type','String'],['path_status','String'],['path_bandwidth','String'],['path_aside_site','String'],
['path_zside_site','String'],['path_a_equip_slte','String']],[['param','String']],'OCh Path Termination');

the entire execution takes more than 20 mins. I ran the profile on statement
call custom.path_element_v('gPath') yield path_element_parent_equip as path_element_parent_equip,
path_element_port as path_element_port,path_pkey as path_pkey,path_element_no as path_element_no
where path_pkey = 3065 -- To hardcode the value to see the plan
AND path_element_port is not null
RETURN path_element_parent_equip as path_a_equip_slte,path_element_port as path_a_port
ORDER BY path_element_no ASC
LIMIT 1

and find the output in attached file
image

We need expert advise to optimize this solution .

Cypher's PROFILE and EXPLAIN cannot see into a procedure call, so tuning becomes tricky.

There's nothing wrong with that query plan, but then we have no idea what you are doing in custom.path_element_v('gPath').

Try also running a PROFILE or EXPLAIN on:

match (a:inventoryObjects{type : $param})-[r:INSTANCE_OF]->(c:classes{name:'gPath'})
with a
optional match (b:inventoryObjects{site_inst_id : a.a_side_site_id})-[r:INSTANCE_OF]->(c:classes{name : 'gFacility'})
with a,b
optional match (d:inventoryObjects{site_inst_id : a.z_side_site_id})-[r:INSTANCE_OF]->(c:classes{name : 'gFacility'})
with a,b,d
CALL apoc.cypher.run(
"call custom.path_element_v('gPath') yield path_element_parent_equip as path_element_parent_equip,
path_element_port as path_element_port,path_pkey as path_pkey,path_element_no as path_element_no
where path_pkey = "+ a.circ_path_inst_id +"
AND path_element_port is not null
RETURN path_element_parent_equip as path_a_equip_slte,path_element_port as path_a_port
ORDER BY path_element_no ASC
LIMIT 1",{}
) yield value
return a.name AS path_name,
a.type AS path_type,
a.status AS path_status,
a.bandwidth AS path_bandwidth,
b.name AS path_aside_site,
d.name AS path_zside_site,
value.path_a_equip_slte as path_a_equip_slte

We could use that to see if there's anything to tune in the Cypher surrounding the call.

Hi Team

I am trying to change the logic little bit and passing one additional optional input parameter in custom procedure execution that will limit the search inside the procedure to specific one path but i am not able to manage to calling custom procedure without optional input parameter.
I will explain it below suppose i have one custom procedure with 2 parameters , one is mandatory and other one is optional. I read it that we can define default value along with name and type for each input parameter.
so i create one custom procedure with below definition
For understandning , i am writing only that part of code that is related with my problem

definition
call apoc.custom.asProcedure('RIL_SERVICE_PATH_TERMINATION_V','match (a:inventoryObjects{type : $param1})-[r:INSTANCE_OF]->(c:classes{name:'gPath'})
where a.name = $param2
giving input parameter as [['param1','String'],['param2','String',null]]

when i call this custom procedure with both parameters it is working fine
call custom.RIL_SERVICE_PATH_TERMINATION_V('Service Path','TOT/EQX/SAT/100G/OTU4/000002_L')

but i run it without second input parameter like
call custom.RIL_SERVICE_PATH_TERMINATION_V('Service Path')

it doesn't return any value. Please guide me where i am going wrong.

Hi All,

Finally i resolved my optional parameter issue by changing the definition as

call apoc.custom.asProcedure('RIL_SERVICE_PATH_TERMINATION_V','match (a:inventoryObjects{type : $param1})-[r:INSTANCE_OF]->(c:classes{name:'gPath'})
where ((a.name = $param2 and $param2 is not null) or (1=1 and $param2 is null)).

By passing this optional parameter, it resolved the performance issue also.

1 Like