0

I have a business model in a Spring Application where Drugs exist. The theory behind this is, that the user has a global search function(like Google) with just one input field.

  • Drugs got a TradingName, expireDate.

  • Drugs are in physical Slots.

  • Drugs contain Substances.

  • (d:Drug), (s:Slot), (sub:Substances)

  • d.tradingName, d.expireDate, sub.substanceName are Strings.

  • s.number is a Long

Examples for search from a user in the input field.

  1. Depon (is a trading name for a Drug)

  2. Paracetamol (Substance Name)

  3. 355 (He wants to know which Drug is in Slot 355)

  4. 2021-02 (He wants to see what Drugs are expiring at February 2021)

The point is, that whatever the user searches for, the output should be the same. A list of the Drugs with all its information, but I am handling this in my frontend.

Now I have 3 queries.

1)

MATCH (d:Drug)
WHERE toLower(d.tradingName) CONTAINS toLower({0}) OR toLower(d.expire) CONTAINS toLower({0})
WITH d, datetime(d.expire) as expireDate
RETURN (d)-[]-()
ORDER BY expireDate

this one is working perfectly and is also ordering outputs, but its not searching in Slot und Substance.

2)

MATCH (s:Slot), (d:Drug) WHERE s.number = ({0}) return (d)-[]-(s)
MATCH (sub:Substances), (d:Drug)
WHERE toLower(sub.substanceName) CONTAINS toLower({0})
RETURN (sub)-[]-(d)

How is it possible to combine all this queries to one and in the end output the ordered to expire date Drugs with its relations to Slot and Substance?

Output in the end should look something like that.

d.tradingName - d.expire - sub.substanceName - s.number
Jason Aller
  • 3,541
  • 28
  • 38
  • 38

2 Answers2

1

if you have Neo4j 4, you can make a union query and post-process the result with your order by.

CALL {
  MATCH (d:Drug) 
  WHERE toLower(d.tradingName) CONTAINS toLower({0}) OR toLower(d.expire) CONTAINS  toLower({0}) 
  WITH d, datetime(d.expire) as expireDate 
  RETURN (d)-[]-() AS path, , datetime(d.expire) as expireDate
  UNION
  MATCH (s:Slot), (d:Drug) WHERE s.number = ({0}) 
  RETURN (d)-[]-(s) AS path, , datetime(d.expire) as expireDate
  UNION
  MATCH (sub:Substances), (d:Drug) 
  WHERE toLower(sub.substanceName) CONTAINS toLower({0}) 
  RETURN (sub)-[]-(d) AS path, , datetime(d.expire) as expireDate
} WITH path, expireDate
RETURN path
ORDER BY expireDate

This is a link that explain it ; https://graphaware.com/neo4j/2020/01/17/post-union-processing-explained.html

logisima
  • 7,340
  • 1
  • 18
  • 31
1

You can combine all 3 queries and order by expiredate this way (with empty lines inserted for clarity):

WITH '(?i).*' + {0} + '.*' AS regex

MATCH p1=(d:Drug)--()
WHERE d.tradingName =~ regex OR d.expire =~ regex
WITH regex, COLLECT(p1) AS ps

MATCH p2=(:Drug)--(s:Slot)
WHERE s.number = {0}
WITH regex, ps+COLLECT(p2) AS ps

MATCH p3=(:Drug)--(sub:Substances)
WHERE sub.substanceName =~ regex
WITH ps+COLLECT(p3) AS ps

UNWIND ps AS p
RETURN DISTINCT p
ORDER BY NODES(p)[0].expireDate;

The above query refines the case-insensitive string tests a bit by using a shared regular expression and the =~ operator.

Also, the query is a lot more efficient than the original ones, since it does not use MATCH clauses that cause the creation of cartesian products (e.g., MATCH (s:Slot), (d:Drug)). Also helping to avoid cartesian products is the use of COLLECT between each "subquery" to reduce the number of result rows to 1 before executing the subsequent MATCH clause.

Finally, the DISTINCT option is used to eliminate duplicate paths, since it seems theoretically possible for multiple subqueries to return the same path.

cybersam
  • 63,203
  • 6
  • 53
  • 76
  • thank you very much for ur help the only problem i see here is that on line WHERE s.number = {0} The input should be handled as a number and not as a string. For example if the user types tralala, the input is text and not a number, how can i solve this problem ? – Tsolakidis Konstantinos Mar 04 '20 at 11:32
  • That test comes directly from your original query. Why are you passing in invalid parameter values? Are you saying you want that `MATCH` to be ignored if the value is not numeric? Not sure what you are trying to do. – cybersam Mar 04 '20 at 16:34
  • The user should be able to type anything he wants. If he wants to find the Drug in the slot 50, he types 50 and the slot.number ist not a string but a number. that means that his output will be pasted in all {(0)} existing in my query. But if he types a string like 'Konstantin' , WHERE s.number = {0} this row will throw an error – Tsolakidis Konstantinos Mar 05 '20 at 09:28
  • No errors are thrown if a `WHERE` test fails. The `MATCH` (if not `OPTIONAL`) will just not succeed, and the rest of the query will be aborted for the current data row. If you replace my second `MATCH` with an `OPTIONAL MATCH`, the rest of the query will not be aborted -- which may satisfy your needs? – cybersam Mar 05 '20 at 18:19
  • Neo.ClientError.Statement.SyntaxError: Variable `Konstantinos` not defined (line 6, column 18 (offset: 186)) "WHERE s.number = Konstantinos" this is the error i get even if i replace it with an optional match. The query is expecting a pure Number if its not the case there is an error – Tsolakidis Konstantinos Mar 06 '20 at 14:01
  • Seems like your query did not define a variable named `Konstantinos`. That is different than a variable having a particular value -- in your case the variable does not exist at all. A variable needs to be defined before you use it in a `WHERE` clause. Maybe you meant to use a string instead of a variable, as in `WHERE s.number = "Konstantinos"`? – cybersam Mar 06 '20 at 18:31