Multiple matches in a MATCH statement in Cypher / Neo4j / Spring - spring

I have a Spring Application and in my Repository I have a query where I match Drugs and only ONE Patient but it is not defined how many drugs will be selected from the user.
Actual Cypher Query:
MATCH (p:Patient) where ID(p) = ({0})
MATCH (d:Drug) where ID(d) in [({1}),({2}),({3})]
RETURN p, d
In this query, I am matching 3 Drugs but I would like this to be like a for statement depending on how many Drugs the user will select.
Something like that:
MATCH (p:Patient) where ID(p) = ({0})
MATCH (d:Drug) where ID(d) in [({1}),({2}),({3}),.....({10})] <-- size could be 1 or more
RETURN p, d
How can I solve this problem? Is it possible to loop in Cypher somehow? If so, how do I solve my problem?

You should be passing the values as parameters.
For example, if you use a p_id parameter to pass the ID of the Patient, and a d_ids parameter to pass a collection of Drug IDs:
MATCH (p:Patient) WHERE ID(p) = $p_id
MATCH (d:Drug) WHERE ID(d) IN $d_ids
RETURN p, d

Related

Why do the % operators in the query not work for strings containing numbers? (JPA)

I have the following query:
#Query("SELECT d FROM CarportLocationEntityView d WHERE d.externalId LIKE %:filterCriteria% OR lower(d.carportName) LIKE lower(concat('%',:filterCriteria,'%')) ")
List<CarportLocationEntityView> filterCarportList(#Param("filterCriteria") String filterCriteria);
This query searches two columns in the database:
externalId
carportName
Both columns are of type string, where in externalId always a number is specified as string. if I apply the query and enter a part of carportName then I get the correct results.
However, if I filter for an externalId and enter only a part of the externalId in the parameter then I get no data.
Ex: localhost:8080/filter?filterCriteria=100
Here I get no data back.
But if I filter for the whole externalId
localhost:8080/filter?filterCriteria=1000000
then I get the matching data set.
Why do the % operators in the query not work for strings containing numbers?
#Query("SELECT d FROM CarportLocationEntityView d WHERE d.externalId LIKE %:#{#filterCriteria}% OR lower(d.carportName) LIKE %:#{#filterCriteria.toLowerCase()}% ")
List<CarportLocationEntityView> filterCarportList(#Param("filterCriteria") String filterCriteria);
Ref:
Using SpEL Expressions - Spring Data JPA Reference Documentation
See Also:
How to log SQL statements in Spring Boot?

Criteria Query to retrieve data from DB using specification and predicate

I have two tables (user, vehicles) and i want to write criteria query to retrieve data from db using criteria query specification and predicate to both Join Tables.
select ur.id, count (ur.vehicle_FK) from user so
inner join VEHICLE vhe on vhe.user_id_FK = ur."ID"
group by ur.id, vhe.user_id_FK;
How to implement it using criteria query ??
Try something like this :
Criteria criteria = session.createCriteria(User.class, "user");
criteria.createAlias("user.vehicle_FK", "vehicle", Criteria.INNER_JOIN);
criteria.setProjection(
Projections.projectionList().add(Projections.groupProperty("user.id"))
.add(Projections.countDistinct("user.id")));
Parameters in Criteria Queries
The following query string represents a JPQL query with a parameter:
SELECT c FROM Country c WHERE c.population > :p
An equivalent query can be built using the JPA criteria API as follows:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Country> q = cb.createQuery(Country.class);
Root<Country> c = q.from(Country.class);
ParameterExpression<Integer> p = cb.parameter(Integer.class);
q.select(c).where(cb.gt(c.get("population"), p));
The ParameterExpression instance, p, is created to represent the query parameter. The where method sets the WHERE clause. As shown above, The CriteriaQuery interface supports method chaining. See the links in the next sections of this page for detailed explanations on how to set criteria query clauses and build criteria expressions.
You can find more examples here
https://www.objectdb.com/java/jpa/query/criteria
Remember to post an answer when you find one! :)

How to use the for loop over multiple criteria in mongodb from spring

I am trying to use the for loop over the criteria in the spring Dao to return a list of documents for each scenario matched in the list:
mongoDb contains multiple documents for the same studentId with different times.
I have a Array list with studentId's and times that I want to query on mongodb
The input looks like this
List <StudentAssociation> ={{studentId:st1234,assocstarttime=2019-08-16,assocendtime:2019-08-17},{studentId:st3456,assocstarttime=2019-07-09, assocendtime=2019-08-15}}
The query I wrote for a single student working without the for loop, but I want to send the list as the input
Working query
public List<Student> getListOfStudentRecords(String studentId, LocalDateTime assocstarttime, LocalDatetime assocendtime) {
Query query = new Query();
Criteria studentIdCriteria = Criteria
.where("studentId)
.is(studentId);
Criteria dateTimeCriteria = Criteria
.where(datetime)
.gte(assocstarttime.toString())
.lte(assocendtime.toString());
query.addCriteria(studentIdCriteria);
query.addCriteria(dateTimeCriteria);
return mongoTemplate.find(query, Student.class);
}
What I am trying is input the list, the following query
public List getListofStudentRecordsUsingList(List studentAssoc) {
Query query = new Query();
//for loop
for(StudentAssociation sa : studentAssoc) {
Criteria studentIdCriteria = Criteria
.where("studentId)
.is(studentId);
Criteria dateTimeCriteria = Criteria
.where(datetime)
.gte(sa.getAssocStarttime())
.lte(sa.getAssocEndtime());
query.addCriteria(studentIdCriteria);
query.addCriteria(dateTimeCriteria);
}
query.with(new Sort(Sort.Direction.ASC, datetime));
return mongoTemplate.find(query, Student.class);
}
Expected output is for each student in the list return list of documents and then merge the result for each student into a single list and sort based on time field to create a timeline
The error I am getting withe second query is
//this is solved when I moved the Query query = new Query(); inside the for Loop
InvalidMongoDbApiUsageException: Due to limitations of the com.mongodb.BasicDocument, you can't add a second 'studentId' criteria. Query already contains '{ "lStudentId" : "st1234" }' //this is resolved when I move the query inside the for loop
Your Query object definition is outside of loop, so the same object is used for your whole loop. At second pass, query variable already contains a 'studentId' criteria, as explained in your error.
Try to set query inside loop, or clear your object at start of loop.
//for loop
for(StudentAssociation sa : studentAssoc) {
Query query = new Query();
...
Query in a loop is such an expensive thing, i would recommend you to use and & in for reduce the number of queries.
Mongodb provides operator like :
The $and operator evaluates one or more expressions and returns true if all of the expressions are true or if evoked with no argument expressions. Otherwise, $and returns false.
The $in operator selects the documents where the value of a field equals any value in the specified array.
Query findStudent = new Query(Criteria.where("studentId").in('List of id')
.and('datetime').gte('you should filter min time')
.lte('max time'))
.with(new Sort(Sort.Direction.ASC, datetime));

Finding items from a list in an array stored in a DB field

I have a legacy database that has data elements stored as a comma delimited list in a single database field. (I didn't design that, I'm just stuck with it.)
I have a list of strings that I would like to match to any of the individual values in the "array" in the DB field and am not sure how to do this in Linq.
My list:
List<string> items= new List<string>();
items.Add("Item1");
items.Add("Item2");
The DB field "Products" would contain data something like:
"Item1,Item3,Item4"
"Item3,Item5,Item6"
"Item2,Item7,Item6"
"Item1,Item2"
"Item1"
My first pass at the Linq query was:
var results = (from o in Order
.Where(p=> items.Contains(p.Products)
But I know that won't work. because it will only return the records that contain only "Item1" or "Item2". So with the example data above it would return 0 records. I need to have it return two records.
Any suggestions?
There is a simple clever trick for searching comma-separated lists. First, add an extra , to the beginning and end of the target value (the product list), and the search value. Then search for that exact string. So for example, you would search ,Item1,Item3,Item4, for ,Item1,. The purpose of this is to prevent false positives, i.e., Item12,Item3 finding a match for Item1, while allowing items at the beginning/end of the list to be properly found.
Then, you can use the LINQ .Any method to check that any item in your list is a match to the product list, like the following:
var results = (from o in Order
.Where(o => items.Any(i => (","+o.Products+",").Contains(","+i+",")))
One way would be to parse the list in the Products field:
var results = (from o in Order
.Where(o => items.Any(i => o.Products.Split(',').Contains(i))
But that would parse the string multiple times for each record. You could try pulling back ALL of the records, parsing each record once, then doing the comparison:
var results = from o in Order
let prods = o.Products.Split(',')
where items.Any(i => prods.Contains(i))
select o;

querying a list - returns only one value

I have created a structure and list.
public struct CarMake
{
public string name;
public string id;
}
I added structure objects to this (carMakers) and am trying to query
string selCar = from c in carMakers
where c.name == selectedCarMfgName
select c.id;
I am getting an error near select statement- cannont implicity convert IEnumerable to string. I know that query returns only one value, that's why I have like that.
thanks !
string selCar = (from c in carMakers
where c.name == selectedCarMfgName
select c.id).SingleOrDefault();
Your query returns a collection (with one element). You should use Single() (or SingleOrDefault()) to get that one item. If the query can return more than one result, you should look into First() ( or FirstOrDefault())
Pay attention to the error message. It probably says something like
"cannot implicitly convert IEnumerable<string> to string."
The results of a query of a sequence is another sequence, an IEnumerable<T>. You may know that you expect only one result, but that's not what the query does. To obtain only one result, you can optionally include another extension method on the end.
yourQuery.First();
yourQuery.FirstOrDefault();
yourQuery.Single();
yourQuery.SingleOrDefault();
The difference in these is that the First* variations can work with sequenes with many elements, whereas the Single* variations will throw exceptions if more than one element is present. The *OrDefault variations support the concept of no matching elements, and returns the default value for the type (null in the case of classes, a default value (such as 0 for int) for structs).
Use the version that conforms to your expectation. If you expect one and only one match, prefer Single. If you only care about one out of arbitrarily many, prefer First.
carMakers.Add(new CarMake() { name = "Audi", id = "1234" });
string selCar =(from c in carMakers
where c.name == "Audi"
select c.id).FirstOrDefault();
Output- 1234
I would refactor my query slightly:
var selCar = carMakers.Single(c => c.name == selectedCarMfgName).id;
This assumes you know that the car is in the list. If not, use SingleOrDefault and check the return before getting the id.
I've not done too much with LINQ but because you are selecting into a string you may need to use FirstOrDefault as your statement could return back more than one value yet your string can only hold one.
First will return null value I think if nothing is found but FirstOrDefault will return you a blank string.

Resources