neo4j cypher parse and load XML - neo4j

I am trying to load the following Equipment.XML into neo4j database -
<NetworkEntity>
<Equipment>
<Name>IPTC.net/ABC12345</Name>
<Type>CHASSIS</Type>
<SubType>Nokia 7750</SubType>
</Equipment>
<Equipment>
<Name>IPTC.net/ABC12345/Shelf-1</Name>
<Type>SHELF</Type>
<SubType>Nokia 7750</SubType>
<Associations>
<Association>
<Target>
<EntityName>IPTC.net/ABC12345</EntityName>
<EntityType>Equipment</EntityType>
</Target>
<RelationshipType>RESIDES_ON</RelationshipType>
<Features>
<Feature>
<Name>Feat1</Name>
<Value>123</Value>
</Feature>
</Features>
</Association>
</Associations>
</Equipment>
</NetworkEntity>
I have written the following cypher procedure so far -
call apoc.load.xml("file:///opt/home/neo4j/NEO4JINSTANCE1/neo4j-enterprise-3.0.1/import/Equipment.xml")
yield value as ne UNWIND ne._children AS eqs
FOREACH( eq IN eqs |
FOREACH( child IN eq["_children"] |
CREATE (:SPAEquipment {name: child["_text"]})))
RETURN *;
But it is considering each property of the Equipment tag as a separate SPAEquipment. Hence Name, Type and SubType - each is getting created as a SPAEquipment instead of three properties of the same SPAEquipment. So instead of children I should use different tags. Also I am not able to create the association between the equipments quite correctly.
So, basically I need to create the different equipments in the database with the corresponding properties and also I need to create the relationships between them.

I'm not that familiar with load_xml but I think neo4j is doing exactly what you are asking.
You have the CREATE on the child of the Equipment.
You are saying:
For each get the children,
For each _child
CREATE SPEquipment ...
What I think you want is
For each <Equipment>
CREATE (s:SPAEquipment {id:<maybe with a simple ID here>})
For each _child
set s.propertyName=child["text"]
However, I am not sure how apoc.load.xml does that
The only way I have been able to get the children is as an array of pairs (_type,_text) and I can't seem to figure out how to do anything with that.
call apoc.load.xml("file:///Volumes/Data/neo4j-enterprise- 3.0.3/import/myXML.xml") yield value as ne UNWIND ne._children AS eqs
RETURN eqs, [attr IN eqs._children WHERE attr._type IN
['Name','Type','SubType'] | [attr._type, attr._text]] as pairs
╒══════════════════════════════╤══════════════════════════════╕
│eqs │pairs │
╞══════════════════════════════╪══════════════════════════════╡
│{_type: Equipment, id: 01, _ch│[[Name, IPTC.net/ABC12345], [T│
│ildren: [{_type: Name, _text: │ype, CHASSIS], [SubType, Nokia│
│IPTC.net/ABC12345}, {_type: Ty│ 7750]] │
│pe, _text: CHASSIS}, {_type: S│ │
│ubType, _text: Nokia 7750}]} │ │
├──────────────────────────────┼──────────────────────────────┤
[...]
Note: I created id attributes on my sample, you won't have those.

I couldn't solve this issue using apoc.load.xml. apoc.load.xml breaks the structure into tree and we need to write cypher on top of it. But it was a very complex query and I failed to write it properly.
So I wrote an XSL which would parse the Equipment.xml and build the cypher queries. This is just for our internal use purpose and it's working fine.
Thanks.

Related

Is it possible to apply [Rule Chain] after [Data Converter]?

I am currently working on a POC by using ThingsBoard PE.
Our raw data contains [Asset] [Attributes].
Data flow:
[IoT cloud] --https webhook carry raw data--> [ThingsBoard PE HTTP INTEGRATION] --uplink--> [ThingsBoard PE Data Converter]
My question is: is it possible to apply [Rule Chain] after [ThingsBoard PE Data Converter]? Therefore, the device can auto create relationship with [Asset] by the [Attribute], instead of [AssetName].
Example data after data converter process:
{
"deviceName": "ABC",
"deviceType": "temperature",
"attributes": {
"asset_id": 6 // <-- the id is used in asset attribute
},
"telemetry": {
"temperature": 39.43
}
}
Answering your two, separate questions:
is it possible to apply [Rule Chain] after [ThingsBoard PE Data Converter]?
Yes it is possible. Once your data is successfully integrated and you are receiving it, you can access it using the [Input] Rule Node (the default green one that is always there when you create a Rule) and route it to any other node you need.
Therefore, the device can auto create relationship with [Asset] by the [Attribute], instead of [AssetName].
So, you want the relationship to take your custom attribute and use that as the pattern that identifies the Asset you want to create the relationship from.
The PE edition already has the Create Relation Node. However, seems that as it is one is not able to do what you seek (has no option to specify custom Asset id).
However, two options you got are:
Create a Custom Rule Node that does what you want. For that try checking the Rule Node Development page from Thingsboard. You can use the Create Relation Node as base and work from there. This can be a longer solution than doing...
Enrich your incoming message's metadata, adding your desired attribute. The Create Relation Node allows you to use variables on your message's metadata in your Name and Type patterns, as seen from this screenshot I took from that node:
This allows us a workaround to what you want to do: Add a Script Transformation Node that adds attributes.asset_id to the metadata, for example as metadata.asset_id, so you can then use it as ${asset_id} on your Name and Type patterns.
For example, your Transform() method of such Script Transformation Node should look something like this:
function Transform(msg, metadata, msgType){
//assumming your desired id is msg.attributes.asset_id, add it to the metadata
metadata.asset_id = msg.attributes.asset_id;
//return the message, in your case to the Create Relation Node
return {msg: msg, metadata:metadata, msgType:msgType};
}
Finally, your Rule should be connected like this:
[Input] -> [Script Node] -> [Create Relation Node] -> [...whatever else you like]

How to exclude multiple values in OData call?

I am creating a SAPUI5 application. This application is connected to a backend SAP system via OData. In the SAPUI5 application I use a smart chart control. Out of the box the smart chart lets the user create filters for the underlying data. This works fine - except if you try to use multiple 'not equals' for one property. Is there a way to accomplish this?
I found out that all properties within an 'and_expression' (including nested or_expressions) must have unique name.
The reason why two parameters with the same property don't get parsed into the select options:
/IWCOR/CL_ODATA_EXPR_UTILS=>GET_FILTER_SELECT_OPTIONS takes the expression you pass and parses it into a table of select options.
The select option table returned is of type /IWCOR/IF_ODATA_TYPES=>EDM_SELECT_OPTION_T which is a HASHED TABLE .. WITH UNIQUE KEY property.
From: https://archive.sap.com/discussions/thread/3170195
The problem is that you cannot combine NE terms with OR. Because both parameters after the NE should not be shown in the result set.
So at the end the it_filter_select_options is empty and only the iv_filter_string is filled.
Is there a manual way of facing this problem (evaluation of the iv_filter_string) to handle multiple NE terms?
This would be an example request:
XYZ/SmartChartSet?$filter=(Category%20ne%20%27Smartphone%27%20and%20Category%20ne%20%27Notebook%27)%20and%20Purchaser%20eq%20%27CompanyABC%27%20and%20BuyDate%20eq%20datetime%272018-10-12T02%3a00%3a00%27&$inlinecount=allpages
Normally I want this to exclude items with the category 'Notebook' and 'Smartphone' from my result set that I retrieve from the backend.
If there is a bug inside /iwcor/cl_odata_expr_utils=>get_filter_select_options which makes it unable to treat multiple NE filters of the same component, and you cannot wait for an OSS. I would suggest to wrap it inside a new static method that will make the following logic (if you will be stuck with the ABAP implementation i would try to at least partially implement it when i get time):
Get all instances of <COMPONENT> ne '<VALUE>' inside a () (using REGEX).
Replace each <COMPONENT> with <COMPONENT>_<i> so there will be ( <COMPONENT>_1 ne '<VALUE_1>' and <COMPONENT>_2 ne '<VALUE_2>' and... <COMPONENT>_<n> ne '<VALUE_n>' ).
Call /iwcor/cl_odata_expr_utils=>get_filter_select_options with the modified query.
Modify the rt_select_options result by changing COMPONENT_<i> to <COMPONENT> again.
I can't find the source but I recall that multiple "ne" isn't supported. Isn't that the same thing that happens when you do multiple negatives in SE16, some warning is displayed?
I found this extract for Business ByDesign:
Excluding two values using the OR operator (for example: $filter=CACCDOCTYPE ne ‘1000’ or CACCDOCTYPE ne ‘4000’) is not possible.
The workaround I see is to select the Categories you actively want, not the ones you don't in the UI5 app.
I can also confirm that my code snippet I've used a long time for filtering also has the same problem...
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_MGW_ABS_DATA->FILTERING
* +-------------------------------------------------------------------------------------------------+
* | [--->] IO_TECH_REQUEST_CONTEXT TYPE REF TO /IWBEP/IF_MGW_REQ_ENTITYSET
* | [<-->] CR_ENTITYSET TYPE REF TO DATA
* | [!CX!] /IWBEP/CX_MGW_BUSI_EXCEPTION
* | [!CX!] /IWBEP/CX_MGW_TECH_EXCEPTION
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD FILTERING.
FIELD-SYMBOLS <lt_entityset> TYPE STANDARD TABLE.
ASSIGN cr_entityset->* TO <lt_entityset>.
CHECK: cr_entityset IS BOUND,
<lt_entityset> IS ASSIGNED.
DATA(lo_filter) = io_tech_request_context->get_filter( ).
/iwbep/cl_mgw_data_util=>filtering(
exporting it_select_options = lo_filter->get_filter_select_options( )
changing ct_data = <lt_entityset> ).
ENDMETHOD.

Neo4j cypher query fails with unknown syntax error

I have the following paramObj and dbQuery
paramObj = {
email: newUser.email,
mobilenumber: newUser.telephone,
password: newUser.password,
category: newUser.category,
name: newUser.name,
confirmuid: verificationHash,
confirmexpire: expiryDate.valueOf(),
rewardPoints: 0,
emailconfirmed: 'false',
paramVehicles: makeVehicleArray,
paramVehicleProps: vehiclePropsArray
}
dbQuery = `CREATE (user:Person:Owner {email:$email})
SET user += apoc.map.clean(paramObj,
['email','paramVehicles','paramVehiclesProps'],[])
WITH user, $paramVehicles AS vehicles
UNWIND vehicles AS vehicle
MATCH(v:Vehicles {name:vehicle})
CREATE UNIQUE (user)-[r:OWNS {since: timestamp()}]->(v)
RETURN user,r,v`;
Then I tried to execute
commons.session
.run(dbQuery, paramObj)
.then(newUser => {
commons.session.close();
if (!newUser.records[0]) {........
I am getting
Error: {"code":"Neo.ClientError.Statement.SyntaxError","name":"Neo4jError"}
which doesn't direct me anywhere. Can anyone tell me what am I doing wrong here?
This is actually the first time I am using the query format .run(dbQuery, paramObj) but this format is critical to my use case. I am using Neo4j 3.4.5 community with apoc plugin installed.
Ok...so I followed #inversFalcon suggestion to test in browser and came up with following parameters and query that closely match the ones above:
:params paramObj:[{ email:"xyz123#abc.com", mobilenumber:"8711231234",password:"password1", category:"Owner",name:"Michaell",vehicles:["Toyota","BMW","Nissan"],vehicleProps: [] }]
and query
PROFILE
CREATE (user:Person:Owner {email:$email})
SET user += apoc.map.clean($paramObj, ["email","vehicles","vehicleProps"],[])
WITH user, $vehicles AS vehicles
UNWIND vehicles AS vehicle
MATCH(v:Vehicles {name:vehicle})
MERGE (user)-[r:OWNS {since: timestamp()}]->(v)
RETURN user,r,v;
Now I get
Neo.ClientError.Statement.TypeError: Can't coerce `List{Map{name -> String("Michaell"), vehicles -> List{String("Toyota"), String("BMW"), String("Nissan")},.......
I also reverted to neo4j 3.2 (re: an earlier post by Mark Needham) and got the same error.
You should try doing an EXPLAIN of the query using the browser to troubleshoot it.
A few of the things I'm seeing here:
You're referring to paramObj, but it's not a parameter (rather, it's the map of parameters you're passing in, but it itself is not a parameter you can reference in the query). If you need to reference the entire set of parameters being passed in, then you need to use nested maps, and have paramObj be a key in the map that you pass as the parameter map (and when you do use it in the query, you'll need to use $paramObj)
CREATE UNIQUE is deprecated, you should use MERGE instead, though be aware that it does behave in a different manner (see the MERGE documentation as well as our knowledge base article explaining some of the easy-to-miss details of how MERGE works).
I am not sure what caused the coercion error to disappear but it did with the same query and I got a "expected parameter error" this was fixed by using $paramObj.email, etc. so the final query looks like this:
CREATE (user:Person:Owner {email: $paramObj.email})
SET user += apoc.map.clean($queryObj, ["email","vehicles","vehicleProps"],[])
WITH user, $paramObj.vehicles AS vehicles
UNWIND vehicles AS vehicle
MATCH(v:Vehicles {name:vehicle})
MERGE (user)-[r:OWNS {since: timestamp()}]->(v)
RETURN user,r,v;
which fixed my original problem of how to remove properties from a map when using SET += map.

"EntityNotFoundException: Unable to load RELATIONSHIP with id" when saving RelationshipEntity (with huge generated cypher query)

I am using spring-data-neo4j 4.2.0.M1 and neo4j-ogm 2.0.4 with neo4j 3.1.0-M04.
The application is generally working, except for one case where I try to save a collection of modified RelationshipEntities.
The code is sth. like this:
List<Relationship> updatedRelationships = new ArrayList<>();
for(Relationship relationship : modifiedRelationships)
{
relationship = relationshipRepository.load(relationship);
relationship.setValue("value");
updatedRelationships.add(relationship);
}
relationshipRepository.save(relationships);
The RelationshipEntity is annotated with #RelationshipEntity and has a few properties in addition to the #StartNode and #EndNode. Only the property mentioned above is changed though. The RelationshipEntity is loaded inside the loop because I previously noticed lost information (namely value of other properties) when executing this.
Note that the above mentioned code is executes for many RelationshipEntities in succession. Each relationship (probably) occurs only once, but start and end nodes probably occur several times. To my knowledge, no relationship is deleted though.
The exception I get is:
Caused by: org.neo4j.kernel.api.exceptions.EntityNotFoundException: Unable to load RELATIONSHIP with id 20683203.
at org.neo4j.kernel.impl.api.store.DiskLayer.relationshipVisit(DiskLayer.java:432)
at org.neo4j.kernel.impl.api.store.CacheLayer.relationshipVisit(CacheLayer.java:326)
at org.neo4j.kernel.impl.api.StateHandlingStatementOperations.relationshipVisit(StateHandlingStatementOperations.java:1409)
at org.neo4j.kernel.impl.api.ConstraintEnforcingEntityOperations.relationshipVisit(ConstraintEnforcingEntityOperations.java:416)
at org.neo4j.kernel.impl.api.OperationsFacade.relationshipVisit(OperationsFacade.java:493)
at org.neo4j.kernel.impl.factory.GraphDatabaseFacade.getRelationshipById(GraphDatabaseFacade.java:300)
... 104 common frames omitted
The query that is executed before (which is probably the "save" query) is huge and exceed the character limit here (sth. like 200k characters). Apparently the query touches where more relationships than necessary (from business logic point-of-view) since only about 30 entities are actually saved. I would assume that the resulting query (or queries if updates are done per entity) are rather brief.
2016-08-28 20:16:33,007 I [pool-4-thread-1 ] (EmbeddedRequest.java:155) Request: START r=rel({relIds}) FOREACH (row in filter(row in {rows} where row.relId = id(r)) | SET r += row.props) RETURN ID(r) as ref, ID(r) as id, {type} as type with params {relIds=[13744338, 19099951, 12570789, 12570785, 13744377, 13648126, 12570765, 20627727, 13744356, 20627724, 12570760, 19263773, 19257628, 20113678, 19099932, 19259756, 18796874, 13783174, 19097972, 19083644, 19099970, 19097921, 19077446, 19263810, 13744312, 20568405, 20904270, 19097937, 12570827, 20627779, 20648258, 12570816, 20683195, 19259812, 20683194, 20683193, 20683192, 19083690, 20683186, 20683191, 19259819, 18819471, 20683178, 20683177, 12570669, 20683176, 19276210, 19933607, 20683171, 18844038, 19100089, 20683174, 20683173, 20683163, 20683162, 20683161, 13744242, 19257729, 12570649, 20683165, 20683164, 19087754, 21703141, 12570641, 8341711, 19259796, 8704051, 19915155, 19261851, 13783062, 13783063, 19091955, 18182597, 19276276, 19276275, 20623852, 20607468, 20623853, 19100155, 19233277, 13783048, 19261946, 12570719, 21789101, 12570718, 19075526, 19259842, 19257807, 12570707, 13715516, 19098061, 19261908, 20683208, 20683215, 19100118, 20683212, 20683203, 19276254, 20683201, 20683207, 19091934, 20683206, 19261915, 19097639, 19101736, 19101749, 18821129, 19097659, 19124284, 13662709, 13744628, 19052549, 19089427, 13744612, 19265563, 19251300, 19089509, 19251298, 20631665, 19251305, 19265642, 13744513, 19261558, 19261511, 19265606, 19081291, 18903113, 18903114, 19251273, 8341775, 12597685, 13744548, 19081308, 18725021, 18725020, 19273892, 19099808, 19089572, 19097772, 13744449, 13683011, 18178177, 19273905, 19093694, 18178231, 19124358, 20633756, 13744502, 19081356, 18651311, 19093661, 20562171, 19263725, 20625639, 19099901, 20631774, 20676819, 18651383, 20676822, 20676821, 20676820, 19097811, 19099862, 13744428, 20631751, 18178280, 18668312, 19100453, 19088171, 20708148, 19143487, 19088184, 19094334, 18668349, 13744883, 19145485, 20607750, 19094301, 19086108, 13744792, 20611958, 19143528, 13662849, 13744829, 12571346, 20611918, 20611919, 18811753, 19100506, 13744813, 19084195, 13662806, 20708275, 19098546, 20612001, 13744752, 20708253, 12595823, 20611976, 19147673, 19258343, 19274725, 19084262, 19082212, 19096548, 20591606, 19086317, 13662720, 8348332, 19274738, 8348329, 19096571, 21703569, 19440630, 13744654, 21824427, 13744701, 19258320, 20612032, 19086296, 19080158, 19282466, 19145249, 19261996, 20607539, 12596170, 19282472, 18776588, 19100208, 12596183, 18182658, 19233341, 19278395, 19096126, 19098115, 20640284, 18844217, 19255810, 19259919, 19257864, 20623892, 19091980, 19933697, 19282450, 19100180, 19261981, 12596219, 12596113, 19255924, 20707949, 12596118, 19098228, 18704970, 12596122, 19278458, 19096190, 19278456, 19253826, 19278412, 13745087, 19100241, 13745066, 18704995, 19278500, 13744981, 5954519, 19094199, 19143356, 13744970, 12598116, 18840242, 13745006, 18676445, 18008789, 19096298, 18676426, 20607724, 13744906, 13755199, 19094227, 12596419, 19098918, 19256621, 19090736, 21075287, 19100929, 21851496, 20876568, 13681912, 12596463, 12596465, 19090704, 10951825, 12596471, 13681897, 13753581, 19094814, 12596352, 21703948, 21695756, 18699605, 19256693, 18818378, 12596376, 19090755, 19256647, 13681844, 19082583, 18836839, 18699621, 12596409, 20618681, 21544395, 19916202, 12596299, 12596310, 19436940, 19099014, 19094918, 19916170, 13681782, 12596335, 20680073, 13681762, 13681763, 19099028, 19094938, 21081473, 13681682, 20680177, 12596242, 19099126, 19500540, 21081496, 10492993, 19099087, 21081517, 19099094, 21704112, 19098665, 18680849, 12596685, 12596689, 19274804, 20648995, 19137597, 21048411, 19088387, 19262470, 20657183, 19086357, 19258397, 18680869, 12596731, 19088413, 19272807, 19274848, 19272811, 12596622, 18811984, 15797667, 19096694, 19082357, 19262579, 19274875, 19137604, 12596642, 19274830, 19098696, 13682107, 12596651, 19096655, 20632650, 19088474, 19274845, 19262555, 19100834, 13682007, 19098794, 19100851, 12596565, 20556972, 19254450, 20597926, 12598622, 20597925, 20649114, 19100800, 13682036, 19100806, 12596582, 18703539, 20638856, 20598010, 18703582, 19094763, 19100905, 19096808, 20634857, 20597991, 5877179, 5877178, 20597977, 5877181, 19098822, 12596527, 12596532, 19199781, 19265313, 19261228, 20625200, 19257134, 20625201, 18714376, 19085108, 19253054, 19253048, 19265339, 20637459, 20637456, 19085074, 21081974, 8316482, 20598534, 18714402, 19107685, 19253090, 20615029, 19097462, 19263346, 20621152, 19263352, 19259207, 13729470, 19085140, 20688830, 19251116, 19259304, 13678173, 20615087, 12596830, 19097474, 21082087, 12596840, 19263368, 19251093, 8701488, 19267475, 8349384, 12572165, 8349360, 12596751, 19077119, 12596765, 20625380, 19077057, 19089350, 21825447, 21702567, 13682208, 12596785, 8316559, 18178020, 19253207, 20688847, 12596788, 19267536, 20688838, 12570558, 19232295, 12570550, 13783001, 20643352, 20694547, 19095051, 20643338, 19232272, 12570505, 20641280, 20694529, 20641284, 19099164, 20821624, 20821626, 20631165, 20821619, 12570606, 19439229, 12570601, 18820674, 19232327, 12570588, 20694621, 20641362, 20119134, 20631115, 20680264, 20618831, 19093080, 18824862, 19256994, 7325670, 20821668, 19257017, 13782863, 16494427, 20620952, 19256967, 20637331, 18030271, 8267731, 19256977, 20670095, 19099360, 20637433, 19261170, 19265276, 20907749, 18822910, 20621021, 19099339, 19252938, 19936961, 19099345, 19109599, 19257048], rows=[omitted]
I've tried to load the relationship with that id directly, but none exists. The same code executes fine for other RelationshipEntities but repeatedly fails for either this or one of a handful other ones.
Any ideas as to what could cause this or how this can be better debugged?
I think I somehow solved this with the following steps:
Replaced saving the RelationshipEntity with saving the modified NodeEntity
Making such modifying operations sequential (previously this could happen in parallel)
Encapsulate the modifying operation in a transaction
Fixed a bug where in the same transaction the same entity was saved twice (without changing again in the meantime)
Fetching the entity again at the beginning of the transaction in order to have the latest state available
Since I was prettty much in the dark about this topic until it finally worked, I am not sure if all of the steps actually helped solving this. It may actually have been only a subset.
What I can see though now is, that the huge update queries are now smaller (albeit still quite big) but actually seem to contain "real" updates instead of mostly "null" properties. I assume that previously it didn't really contain an update and was instead overriding properties with "null". The fact that this is now working is probably related to the fact, that the entity is now updated before beginning to modify it and that no other modifying operation can run in parallel.
I had the same problem. For me it was simply the neo4j-ogm-embedded-driver
version I had to include in my pom. The one I defined overwrote the one
defined in spring-data-neo4j.
If you only save the relationshipEntity,you could only using next snippet:
List<Relationship> updatedRelationships = new ArrayList<>();
for(Relationship relationship : modifiedRelationships)
{
relationship = relationshipRepository.load(relationship);
relationship.setValue("value");
updatedRelationships.add(relationship);
}
relationshipRepository.save(updatedRelationships,0);
it would save the related properties on relationshipEntity and meanwhile ignore any related entities.

Cypher query for externalized property values

in our data model we have externalized some of our domain entities values into external nodes. The model for a service object looks like:
ref=node(0),
ref<-[:SERVICE]-subRefNode<-[:SERVICE]-aService-[:HAS_PROPERTY_VALUE]->propValueNode-[:IS_OF_TYPE]->propDefType,
ref<-[:SERVICE]-subRefNode-[:HAS_PROPERTY]->propDefType
The node subRefNode holds a relationship to all services. All possible properties for a service are defined through ref<-[:SERVICE]-subRefNode-[:HAS_PROPERTY]->propDefType. So it could be that a certain node doesn't have a propValueNode yet for a certain property (e.g. a comment) and others might have one but its empty (the user might have entered a comment and then cleared it).
So my question is how do I get the nodes with an empty propValueNode.value and also the ones that do not have a propertyValueNode yet? I thought about something like...
START ref=node(0) MATCH ref<-[:SERVICE]-subRef, aService-[pvRel?:HAS_PROPERTY_VALUE]->propValueNode-[pdRel:IS_OF_TYPE]->propDef<-[:HAS_PROPERTY]-subRef<-[:SERVICE]-aService WHERE (pvRel IS NOT NULL AND propDef.name = 'comment' AND propValueNode.value=~"^$") OR (pvRel IS NULL AND pdRel IS NULL AND propDef.name="comment") RETURN DISTINCT aService.debug
http://console.neo4j.org/r/7zeoay
...but this misses the ones without a propValueNode. Any hints are appreciated!
Regards,
Andi
If http://console.neo4j.org/r/7zeoay is not valid anymore, here's the initial graph setup:
start _0 = node(0) with _0
create
(_1 {type:"SubReferenceNode", name:"SubRef"}),
(_2 {type:"Service", debug:"S0 empty value"}),
(_3 {type:"Service", debug:"S1 missing value node"}),
(_4 {type:"Service", debug:"S2 with value"}),
(_5 {type:"PropertyDefintion", name:"comment"}),
(_6 {type:"PropertyDefintion", name:"name"}),
(_7 {type:"PropertyValue", value:"S0 empty value"}),
(_8 {type:"PropertyValue", value:"S1 missing value node"}),
(_9 {type:"PropertyValue", value:"S2 with value"}),
(_10 {type:"PropertyValue", value:""}),
(_11 {type:"PropertyValue", value:"This is a comment"}),
_0<-[:SERVICE]-_1,
_1<-[:SERVICE]-_2,
_1<-[:SERVICE]-_3,
_1<-[:SERVICE]-_4,
_1-[:HAS_PROPERTY]->_5,
_1-[:HAS_PROPERTY]->_6,
_2-[:HAS_PROPERTY_VALUE]->_7,
_7-[:IS_OF_TYPE]->_6,
_3-[:HAS_PROPERTY_VALUE]->_8,
_8-[:IS_OF_TYPE]->_6,
_4-[:HAS_PROPERTY_VALUE]->_9,
_9-[:IS_OF_TYPE]->_6,
_2-[:HAS_PROPERTY_VALUE]->_10,
_10-[:IS_OF_TYPE]->_5,
_4-[:HAS_PROPERTY_VALUE]->_11,
_11-[:IS_OF_TYPE]->_5
You can combine two queries, one is to return the service which has an empty value for the property "comment", and another returns the service which does not have a propery value that is of the type "comment", in other word, none of the service property values is of the the type "comment".
START ref=node(0)
MATCH ref<-[:SERVICE]-subRef, aService-[pvRel?:HAS_PROPERTY_VALUE]->propValueNode-[pdRel:IS_OF_TYPE]->propDef<-[:HAS_PROPERTY]-subRef<-[:SERVICE]-aService
WHERE (pvRel IS NOT NULL AND propDef.name = 'comment' AND propValueNode.value=~"^$")
RETURN DISTINCT aService.debug
UNION
START ref=node(0)
MATCH ref<-[:SERVICE]-subRef, aService-[pvRel:HAS_PROPERTY_VALUE]->propValueNode-[pdRel:IS_OF_TYPE]->propDef<-[:HAS_PROPERTY]-subRef<-[:SERVICE]-aService
with aService, collect(propDef.name) as propNames
WHERE NONE( propName in propNames where propName = 'comment')
RETURN DISTINCT aService.debug
The alternative solution without using the "UNION" clause is to get all value nodes for each service and returns only those service that either none of its values is of type "comment" or there is one value of the type "comment" and the value is empty, as shown in the "Where" clause below,
START ref=node(0)
MATCH ref<-[:SERVICE]-subRef, aService-[pvRel:HAS_PROPERTY_VALUE]->propValueNode-[pdRel?:IS_OF_TYPE]->propDef<-[:HAS_PROPERTY]-subRef<-[:SERVICE]-aService
WHERE propDef.name = 'comment'
WITH aService, collect(propValueNode) AS valueNodes, propDef
WHERE NONE (v IN valueNodes
WHERE v-[:IS_OF_TYPE]->propDef) OR SINGLE (v IN valueNodes
WHERE v.value=~"^$" AND v-[:IS_OF_TYPE]->propDef)
RETURN aService.debug

Resources