Neo4j cypher query fails with unknown syntax error - neo4j

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.

Related

Queries make Twitter-stream application too slow in saving data

I have an application which streams Twitter data which are stored in a Neo4j database. The data I store regard tweets, users, hashtag and their relationships (user posts tweet, tweet tags hashtags, user retweets tweet).
Now, each time I get a new tweet what I do is:
Check if the database already contains the tweet: if so, I update it with the new information (retweet count, like count), else I save it
Check if the database already contains the user: if so, I update it with the new infos, else I save it
Check if the database already contains the hashtag: if it doesn't, I add it
And so on, same process for saving the relationships.
Here are the queries:
static String cqlAddTweet = "merge (n:Tweet{tweet_id: {2}}) on create set n.text={1}, n.location={3}, n.likecount={4}, n.retweetcount={5}, n.topic={6}, n.created_at={7} on match set n.likecount={4}, n.retweetcount={5}";
static String cqlAddHT = "merge (n:Hashtag{text:{1}})";
static String cqlHTToTweet = "match (n:Tweet),(m:Hashtag) where n.tweet_id={1} and m.text={2} merge (n)-[:TAGS]->(m)";
static String cqlAddUser = "merge (n:User{user_id:{3}}) on create set n.name={1}, n.username={2}, n.followers={4}, n.following={5}, n.profilePic={6} on match set n.name={1}, n.username={2}, n.followers={4}, n.following={5}, n.profilePic={6}";
static String cqlUserToTweet = "match (n:User),(m:Tweet) where m.tweet_id={2} and n.user_id={1} merge (n)-[:POSTS]->(m)";
static String cqlUserRetweets = "match (n:Tweet{tweet_id:{1}}), (u:User{user_id:{2}}) create (u)-[:RETWEETS]->(n)";
Since it is very slow in saving data, I suppose that this system can have better performances if I didn't run all those queries which scan the data each time.
Do you have any suggestion to improve my application?
Thank you and excuse me in advance if this may seem silly.
Make sure you have indexes (or uniqueness constraints, if appropriate) on the following label/property pairs. That will allow your queries to avoid scanning through all nodes with the same label (when starting a query).
:Tweet(tweet_id)
:Hashtag(text)
:User(user_id)
By the way, a couple of your queries can be simplified (but this should not affect the performance):
static String cqlAddTweet = "MERGE (n:Tweet{tweet_id: {2}}) ON CREATE SET n.text={1}, n.location={3}, n.topic={6}, n.created_at={7} SET n.likecount={4}, n.retweetcount={5}";
static String cqlAddUser = "MERGE (n:User{user_id:{3}}) SET n.name={1}, n.username={2}, n.followers={4}, n.following={5}, n.profilePic={6}";

"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.

Numeric sort in Manual (Legacy) index in Neo4j 3 is not working correctly

I'm using Legacy indexing (now called Manual indexing). After migration from Neo4j 2 to version 3 I have some problems with numeric sorting.
Example of correct statement in Neo4j 2:
queryContext.sort(new Sort(new SortField(AGE, SortField.INT, false)));
This stament should be changed for Neo4j 3 (Lucene 5):
queryContext.sort(new Sort(new SortField(AGE, SortField.Type.INT, false)));
But if you use this sort statement you will get an exception:
java.lang.IllegalStateException: unexpected docvalues type SORTED_SET for field 'firstName' (expected=SORTED). Use UninvertingReader or index with docvalues.
at org.apache.lucene.index.DocValues.checkField(DocValues.java:208)
at org.apache.lucene.index.DocValues.getSorted(DocValues.java:264)
at org.apache.lucene.search.FieldComparator$TermOrdValComparator.getSortedDocValues(FieldComparator.java:762)
at org.apache.lucene.search.FieldComparator$TermOrdValComparator.getLeafComparator(FieldComparator.java:767)
at org.apache.lucene.search.FieldValueHitQueue.getComparators(FieldValueHitQueue.java:183)
at org.apache.lucene.search.TopFieldCollector$SimpleFieldCollector.getLeafCollector(TopFieldCollector.java:164)
at org.neo4j.kernel.api.impl.index.collector.DocValuesCollector.replayTo(DocValuesCollector.java:297)
at org.neo4j.kernel.api.impl.index.collector.DocValuesCollector.getTopDocs(DocValuesCollector.java:275)
at org.neo4j.kernel.api.impl.index.collector.DocValuesCollector.getIndexHits(DocValuesCollector.java:150)
at org.neo4j.index.impl.lucene.legacy.LuceneLegacyIndex.search(LuceneLegacyIndex.java:346)
at org.neo4j.index.impl.lucene.legacy.LuceneLegacyIndex.query(LuceneLegacyIndex.java:261)
at org.neo4j.index.impl.lucene.legacy.LuceneLegacyIndex.query(LuceneLegacyIndex.java:205)
at org.neo4j.index.impl.lucene.legacy.LuceneLegacyIndex.query(LuceneLegacyIndex.java:217)
at org.neo4j.kernel.impl.api.StateHandlingStatementOperations.nodeLegacyIndexQuery(StateHandlingStatementOperations.java:1440)
at org.neo4j.kernel.impl.api.OperationsFacade.nodeLegacyIndexQuery(OperationsFacade.java:1162)
at org.neo4j.kernel.impl.coreapi.LegacyIndexProxy$Type$1.query(LegacyIndexProxy.java:83)
at org.neo4j.kernel.impl.coreapi.LegacyIndexProxy.query(LegacyIndexProxy.java:365)
I think this is caused by new added statement in Neo4j indexer class (Neo4j is indexing field for sorting automatically now?). See in:
org.neo4j.index.impl.lucene.legacy.IndexType CustomType addToDocument( Document document, String key, Object value )
new line:
document.add( instantiateSortField( key, value ) );
and method instantiateSortField is creating SortedSetDocValuesField
So I changed my code to:
queryContext.sort(new Sort(new SortedSetSortField(AGE, false)));
This runs OK but sorting is not working because numbers are sorted as string. I see that "value" parameter is String every time in method "addToDocument". I think the root cause is explained it this old comment:
see comment in class org.neo4j.index.impl.lucene.legacy.IndexType CustomType
// TODO We should honor ValueContext instead of doing value.toString() here.
// if changing it, also change #get to honor ValueContext.
Am I missing some new way how to index, search and sort data in Neo4j 3 or this is really problem that values are indexed as string in Neo4j?
Simple unit test for Neo4j 2 and Neo4j 3 can be downloaded
Solution added by MishaDemianenko at GH issue

Neo4j+PopotoJS: filter graph based-on predefined constraints

I have a question about the query based on the predefined constraints in PopotoJs. In this example, the graph can be filtered based on the constraints defined in the search boxes. The sample file in this example visualizations folder, constraint is only defined for "Person" node. It is specified in the sample html file like the following:
"Person": {
"returnAttributes": ["name", "born"],
"constraintAttribute": "name",
// Return a predefined constraint that can be edited in the page.
"getPredefinedConstraints": function (node) {
return personPredefinedConstraints;
},
....
In my graph I would like to apply that query function for more than one node. For example I have 2 nodes: Contact (has "name" attribute) and Delivery (has "address" attribute)
I succeeded it by defining two functions for each nodes. However, I also had to put two search box forms with different input id (like constraint1 and constraint2). And I had to make the queries in the associated search boxes.
Is there a way to make queries which are defined for multiple nodes in one search box? For example searching Contact-name and/or Delivery-adress in the same search box?
Thanks
First I’d like to specify that the predefined constraints feature is still experimental (but fully functional) and doesn’t have any documentation yet.
It is intended to be used in configuration to filter data displayed in nodes and in the example the use of search boxes is just to show dynamically how it works.
A common use of this feature would be to add the list of predefined constraint you want in the configuration for every node types.
Let's take an example:
With the following configuration example the graph will be filtered to show only Person nodes having "born" attribute and only Movie nodes with title in the provided list:
"Person": {
"getPredefinedConstraints": function (node) {
return ["has($identifier.born)"];
},
...
}
"Movie": {
"getPredefinedConstraints": function (node) {
return ["$identifier.title IN [\"The Matrix\", \"The Matrix Reloaded\", \"The Matrix Revolutions\"]"];
},
...
}
The $identifier variable is then replaced during query generation with the corresponding node identifier. In this case the generated query would look like this:
MATCH (person:`Person`) WHERE has(person.born) RETURN person
In your case if I understood your question correctly you are trying to use this feature to implement a search box to filter the data. I'm still working on that feature but it won't be available soon :(
This is a workaround but maybe it could work in your use case, you could keep the search box value in a variable:
var value = d3.select("#constraint")[0][0].value;
inputValue = value;
Then use it in the predefined constraint of all the nodes type you want.
In this example Person will be filtered based on the name attribute and Movie on title:
"Person": {
"getPredefinedConstraints": function (node) {
if (inputValue) {
return ["$identifier.name =~ '(?i).*" + inputValue + ".*'"];
} else {
return [];
}
},
...
}
"Movie": {
"getPredefinedConstraints": function (node) {
if (inputValue) {
return ["$identifier.title =~ '(?i).*" + inputValue + ".*'"];
} else {
return [];
}
},
...
}
Everything is in the HTML page of this example so you can view the full source directly on the page.
#Popoto, thanks for the descriptive reply. I tried your suggestion and it worked pretty much well. With the actual codes, when I make a query it was showing only the queried node and make the other node amount zero. I wanted to make a query which queries only the related node while the number of other nodes are still same.
I tried a temporary solution for my problem. What I did is:
Export the all the node data to JSON file, search my query constraint in the exported JSONs, if the file is existing in JSON, then run the query in the related node; and if not, do nothing.
With that way, of course I needed to define many functions with different variable names (as much as the node amount). Anyhow, it is not a propoer way, bu it worked for now.

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