I'm migrating to Neo4j 1.9 and updating the cypher queries according to the deprecations migration guide, specifically:
The ! property operator in Cypher Expressions like node.property! =
"value" have been deprecated, please use has(node.property) AND
node.property = "value" instead.
The problem is that when using the HAS clause in combination with NOT I can't get the expected result. For example:
When the status property exists and is set to something other than "DONE":
AND not(n.status! = "DONE")
evaluates true (as expected)
AND not(has(n.status) AND n.status = "DONE")
evaluates false???
When the status property doesn't exist
AND not(n.status! = "DONE")
evaluates false
AND not(has(n.status) AND n.status = "DONE")
throws exception because the node doesn't exist, surely HAS should have prevented this? It's as though wrapping the check in NOT prevents the HAS check from being executed.
This can be reproduced via the live query examples on the neo4j docs website using the following queries:
MATCH n
WHERE NOT (n.name! = 'Peter')
RETURN n
This returns all (3) nodes who either have no name or whose name is not "Peter". This is the result I want to reproduce but without using the now deprecated "!" operator.
MATCH n
WHERE NOT (HAS (n.name) AND n.name = 'Peter')
RETURN n
Throws a node not found exception because one node doesn't have a name property. :/
MATCH n
WHERE (HAS (n.name) AND n.name = 'Peter')
RETURN n
Correctly returns the node whose name is "Peter".
I've tried rewriting the query in a few alternative ways but can't seem to reliably get the result I want that matches the deprecated ! operator. Maybe it's just getting late and I'm missing something obvious? :)
Any Help is appreciated!
Thanks,
Mark.
I think the second query raising an exception is a bug.
What you can do is use de Morgan's law to transform the predicate:
MATCH n
WHERE NOT (HAS (n.name)) OR (n.name <> 'Peter')
RETURN n
Related
I am having a couple of issues to put this in a functional format.
select from tableName where i=fby[(last;i);([]column_one;column_two)]
This is what I got:
?[tableName;fby;enlist(=;`i;(enlist;last;`i);(+:;(!;enlist`column_one`column_two;(enlist;`column_one;`column_two))));0b;()]
but I get a type error.
Any suggestions?
Consider using the following function, adjust from the buildQuery function given in the whitepaper on Parse Trees. This is a pretty useful tool for quickly developing in q, this version is an improvement on that given in the linked whitepaper, having been extended to handle updates by reference (i.e., update x:3 from `tab)
\c 30 200
tidy:{ssr/[;("\"~~";"~~\"");("";"")] $[","=first x;1_x;x]};
strBrk:{y,(";" sv x),z};
//replace k representation with equivalent q keyword
kreplace:{[x] $[`=qval:.q?x;x;"~~",string[qval],"~~"]};
funcK:{$[0=t:type x;.z.s each x;t<100h;x;kreplace x]};
//replace eg ,`FD`ABC`DEF with "enlist`FD`ABC`DEF"
ereplace:{"~~enlist",(.Q.s1 first x),"~~"};
ereptest:{((0=type x) & (1=count x) & (11=type first x)) | ((11=type x)&(1=count x))};
funcEn:{$[ereptest x;ereplace x;0=type x;.z.s each x;x]};
basic:{tidy .Q.s1 funcK funcEn x};
addbraks:{"(",x,")"};
//where clause needs to be a list of where clauses, so if only one whereclause need to enlist.
stringify:{$[(0=type x) & 1=count x;"enlist ";""],basic x};
//if a dictionary apply to both, keys and values
ab:{$[(0=count x) | -1=type x;.Q.s1 x;99=type x;(addbraks stringify key x),"!",stringify value x;stringify x]};
inner:{[x]
idxs:2 3 4 5 6 inter ainds:til count x;
x:#[x;idxs;'[ab;eval]];
if[6 in idxs;x[6]:ssr/[;("hopen";"hclose");("iasc";"idesc")] x[6]];
//for select statements within select statements
//This line has been adjusted
x[1]:$[-11=type x 1;x 1;$[11h=type x 1;[idxs,:1;"`",string first x 1];[idxs,:1;.z.s x 1]]];
x:#[x;ainds except idxs;string];
x[0],strBrk[1_x;"[";"]"]
};
buildSelect:{[x]
inner parse x
};
We can use this to create the functional query that will work
q)n:1000
q)tab:([]sym:n?`3;col1:n?100.0;col2:n?10.0)
q)buildSelect "select from tab where i=fby[(last;i);([]col1;col2)]"
"?[tab;enlist (=;`i;(fby;(enlist;last;`i);(flip;(lsq;enlist`col1`col2;(enlist;`col1;`col2)))));0b;()]"
So we have the following as the functional form
?[tab;enlist (=;`i;(fby;(enlist;last;`i);(flip;(lsq;enlist`col1`col2;(enlist;`col1;`col2)))));0b;()]
// Applying this
q)?[tab;enlist (=;`i;(fby;(enlist;last;`i);(flip;(lsq;enlist`col1`col2;(enlist;`col1;`col2)))));0b;()]
sym col1 col2
----------------------
bah 18.70281 3.927524
jjb 35.95293 5.170911
ihm 48.09078 5.159796
...
Glad you were able to fix your problem with converting your query to functional form.
Generally it is the case that when you use parse with a fby in your statement, q will convert this function into its k definition. Usually you should just be able to replace this k code with the q function itself (i.e. change (k){stuff} to fby) and this should run properly when turning the query into functional form.
Additionally, if you check out https://code.kx.com/v2/wp/parse-trees/ it goes into more detail about parse trees and functional form. Additionally, it contains a script called buildQuery which will return the functional form of the query of interest as a string which can be quite handy and save time when a functional form is complex.
I actually got it myself ->
?[tableName;((=;`i;(fby;(enlist;last;`i);(+:;(!;enlist`column_one`column_two;(enlist;`column_one;`column_two)))));(in;`venue;enlist`venueone`venuetwo));0b;()]
The issues was a () missing from the statement. Works fine now.
**if someone wants to add a more detailed explanation on how manual parse trees are built and how the generic (k){} function can be replaced with the actual function in q feel free to add your answer and I'll accept and upvote it
I am currently programming in BCPL for an OS course and wanted to write a simple is_digit() function for validation in a program of mine.
A code snippet of my current code follows:
let is_digit(n) be {
if ((n >= '0') /\ (n <= '9')) then
resultis true;
}
I am aware that BCPL has no notion of types, but how would I be able to accomplish this sort of thing in the language?
Passing in a number yields a false result instead of the expected true.
is_digit() is a function returning a value, rather than a routine, so should use = VALOF rather than BE. Otherwise, the code is OK.
let is_digit(n) = valof {
.....
resultis true
}
Functions that return values should be using valof rather than be, the latter (a routine rather than a function) can be called as a function but the return value you get back from it will be undefined(a).
In addition, you should ensure you return a valid value for every code path. At the moment, a non-digit will not execute a RESULTIS statement, and I'm not entirely certain what happens in that case (so best to be safe).
That means something like this is what you're after, keeping in mind there can be implementation variations, such as & and /\ for and, or {...} and $(...$) for the block delimiters - I've used the ones documented in Martin's latest manual:
LET is_digit(n) = VALOF {
RESULTIS (n >= '0') & (n <= '9')
}
(a) Since Martin Richards is still doing stuff with BCPL, this manual may help in any future questions (or see his home page for a large selection of goodies).
here is the sample that i want to ask:
FOR EACH table-name.... NO LOCK BY (IF TRUE THEN sort-this ELSE sort-that + sort-that2).
END.
This would result in an error.
if it is just
FOR EACH .. NO LOCK BY (IF TRUE THEN sort-this ELSE sort-that).
END.
then there is no error. Progress would accept the code
What is need is if condition is true then sort by one field else sort by two or more fields
If you are in a modern enough version of Progress, then you could construct a dynamic query. This will be more efficient in terms of run time as well as getting you round your problem as having IF statements in your query predicate will make index selection hard.
QueryString = "for each table no-lock...".
if true then
QueryString = QueryString + " by sort-this".
else
QueryString = QueryString + " by sort-that by sort-other".
create query QueryHandle.
QueryHandle:add-buffer(buffer table:handle).
QueryHandle:query-prepare(QueryString).
QueryHandle:query-open.
do while QueryHandle:get-next():
/*some stuff*/
end.
QueryHandle:query-close.
delete object QueryHandle.
As per the previous reply, this is not supported.
Typically, you'd have to prepare the result into a temp-table first, using a logical field in the temp-table for the result of your IF THEN ELSE expression.
Unfortunately, this syntax is not supported. As per the documentation, BREAK/BY expects an expression following it but not a statement.
I need help in retrieving query results within when clause of drl file in drools.
Example rule file having query:
query getUsersForCard (Long ipCardNumber)
$listOfUsers : UsersList()
$listOfUserCards : User(cardNumber == ipCardNumber, $cardNum : cardNumber) from $listOfUsers.user_list
end
rule "matchUser"
when
getUsersForCard("4444333322221112L";)
then
System.out.println( "$$$card number in VisaMessage matched with card number in UsersList$$$" );
end
How to obtain $cardNum aftergetUsersForCard query call so that then clause gets printed? I don't want to retrieve $cardNum in Java code from Working Memory but rather should be able to do it within the drl file itself.
Help is much appreciated!
Going back to the original question: "Can query results be accessed from the LHS of a rule?", the answer is: "Yes, using unification (Sections 8.8.3.3.6 and 8.9)".
A query in Drools can have input and output parameters. Input parameters are those that have a value (are bound) when the query is invoked. Those parameters that don't have a value (unbound) are considered output parameters.
The first thing to do is to rewrite your query using unification:
query getUsersForCard (Long ipCardNumber, Long $cardNum)
$listOfUsers : UsersList()
$listOfUserCards : User(
cardNumber == ipCardNumber,
$cardNum := cardNumber
) from $listOfUsers.user_list
end
The important thing to notice is the := (unification) sign being used. This operator is basically saying: "If the variable has a value, then I'll act as an == operator. Otherwise I'll act as a variable binding.
When invoking your query from a rule, you need to make sure you don't provide any value for the second parameter in the query. Given that you are already using positional arguments, that's easy to do:
rule "matchUser"
when
getUsersForCard("4444333322221112L", $cardNum;)
then
System.out.println( "$$$card number in VisaMessage matched with card number in UsersList$$$: "+$cardNum );
end
When the query is invoked, $cardNum will not have a value and it will set by the query because of the unification mechanism.
Hope it helps,
You cannot retrieve query results within the when clause the way I think you think it can be done. But there is no need to do so, simply write the rule as
rule "match card number"
when
$listOfUsers: UsersList()
$user: User(cardNumber == "4444333322221112L") from $listOfUsers
then ... end
Assuming the text of the println indicates what you really want to do, you might use
rule "match card number"
when
VisaMessage( $cardNo: cardNumber )
$listOfUsers: UsersList()
$user: User(cardNumber == $cardNo) from $listOfUsers
then ... end
I am trying to get an MVEL expression to work but am having probelms. I am tyring to determine if date defined as a property falls between two other dates.
props['ExistingStartDate'] >= props['current_period_start_date'] && props['ExistingStartDate'] <= props['current_period_end_date']
So in this case, my ExistingStartDate = 3/6/14, current_period_start_date = 3/3/14 and current_period_end_date = 3/16/14
I am expecting this to be true. I feel like there must be something wrong with my syntax. Any help would be appreciated!
Use parentheses for each term for multiple conditions:
(props['ExistingStartDate'] >= props['current_period_start_date']) && (props['ExistingStartDate'] <= props['current_period_end_date'])
Also, the props references may not have the correct syntax depending on what type of Java object it is.