|
17 | 17 | *******************************************************************************/ |
18 | 18 | package de.symeda.sormas.backend.person; |
19 | 19 |
|
| 20 | +import static de.symeda.sormas.backend.ExtendedPostgreSQL94Dialect.SIMILARITY_OPERATOR; |
| 21 | + |
20 | 22 | import java.sql.Timestamp; |
21 | 23 | import java.util.Collections; |
22 | 24 | import java.util.Comparator; |
23 | 25 | import java.util.Date; |
24 | | -import java.util.HashSet; |
25 | 26 | import java.util.List; |
26 | | -import java.util.Set; |
27 | 27 | import java.util.stream.Collectors; |
28 | 28 | import java.util.stream.Stream; |
29 | 29 |
|
30 | 30 | import javax.ejb.EJB; |
31 | 31 | import javax.ejb.LocalBean; |
32 | 32 | import javax.ejb.Stateless; |
| 33 | +import javax.persistence.Query; |
| 34 | +import javax.persistence.TypedQuery; |
33 | 35 | import javax.persistence.criteria.CriteriaBuilder; |
34 | 36 | import javax.persistence.criteria.CriteriaQuery; |
35 | 37 | import javax.persistence.criteria.Expression; |
@@ -283,64 +285,54 @@ private Predicate isInJurisdiction(CriteriaBuilder cb, CriteriaQuery<Long> cq, R |
283 | 285 | return cb.or(isCaseInJurisdiction, isContactInJurisdiction, isEventParticipantInJurisdiction); |
284 | 286 | } |
285 | 287 |
|
286 | | - public Set<PersonNameDto> getMatchingNameDtos(User user, PersonSimilarityCriteria criteria) { |
| 288 | + public List<PersonNameDto> getMatchingNameDtos(PersonSimilarityCriteria criteria, Integer limit) { |
| 289 | + |
| 290 | + setSimilarityThresholdQuery(); |
287 | 291 |
|
288 | 292 | CriteriaBuilder cb = em.getCriteriaBuilder(); |
289 | | - Set<PersonNameDto> persons = new HashSet<>(); |
| 293 | + Predicate caseContactEventParticipantLinkPredicate; |
290 | 294 |
|
291 | | - // Persons of active cases |
292 | | - CriteriaQuery<PersonNameDto> casePersonsQuery = cb.createQuery(PersonNameDto.class); |
293 | | - Root<Case> casePersonsRoot = casePersonsQuery.from(Case.class); |
294 | | - Join<Case, Person> casePersonsJoin = casePersonsRoot.join(Case.PERSON, JoinType.LEFT); |
| 295 | + CriteriaQuery<PersonNameDto> personQuery = cb.createQuery(PersonNameDto.class); |
| 296 | + Root<Person> personRoot = personQuery.from(Person.class); |
| 297 | + Join<Person, Case> personCaseJoin = personRoot.join(Person.PERSON_CASES, JoinType.LEFT); |
| 298 | + Join<Person, Contact> personContactJoin = personRoot.join(Person.PERSON_CONTACTS, JoinType.LEFT); |
| 299 | + Join<Person, EventParticipant> personEventParticipantJoin = personRoot.join(Person.PERSON_EVENT_PARTICIPANTS, JoinType.LEFT); |
295 | 300 |
|
296 | | - casePersonsQuery.multiselect(casePersonsJoin.get(Person.FIRST_NAME), casePersonsJoin.get(Person.LAST_NAME), casePersonsJoin.get(Person.UUID)); |
| 301 | + personQuery.multiselect(personRoot.get(Person.FIRST_NAME), personRoot.get(Person.LAST_NAME), personRoot.get(Person.UUID)); |
297 | 302 |
|
298 | | - Predicate casePersonsFilter = buildSimilarityCriteriaFilter(criteria, cb, casePersonsJoin); |
299 | | - Predicate activeCasesFilter = caseService.createActiveCasesFilter(cb, casePersonsRoot); |
300 | | - Predicate caseUserFilter = caseService.createUserFilter(cb, casePersonsQuery, casePersonsRoot); |
301 | | - casePersonsQuery.where( |
302 | | - caseUserFilter != null ? and(cb, casePersonsFilter, activeCasesFilter, caseUserFilter) : and(cb, casePersonsFilter, activeCasesFilter)); |
303 | | - casePersonsQuery.distinct(true); |
304 | | - persons.addAll(em.createQuery(casePersonsQuery).getResultList()); |
| 303 | + // Persons of active cases |
| 304 | + Predicate personSimilarityFilter = buildSimilarityCriteriaFilter(criteria, cb, personRoot); |
| 305 | + Predicate activeCasesFilter = caseService.createActiveCasesFilter(cb, personCaseJoin); |
| 306 | + Predicate caseUserFilter = caseService.createUserFilter(cb, personQuery, personCaseJoin); |
| 307 | + Predicate personCasePredicate = and(cb, personCaseJoin.get(Case.ID).isNotNull(), activeCasesFilter, caseUserFilter); |
305 | 308 |
|
306 | 309 | // Persons of active contacts |
307 | | - CriteriaQuery<PersonNameDto> contactPersonsQuery = cb.createQuery(PersonNameDto.class); |
308 | | - Root<Contact> contactPersonsRoot = contactPersonsQuery.from(Contact.class); |
309 | | - Join<Contact, Person> contactPersonsJoin = contactPersonsRoot.join(Contact.PERSON, JoinType.LEFT); |
310 | | - |
311 | | - contactPersonsQuery |
312 | | - .multiselect(contactPersonsJoin.get(Person.FIRST_NAME), contactPersonsJoin.get(Person.LAST_NAME), contactPersonsJoin.get(Person.UUID)); |
313 | | - |
314 | | - Predicate contactPersonsFilter = buildSimilarityCriteriaFilter(criteria, cb, contactPersonsRoot.join(Contact.PERSON, JoinType.LEFT)); |
315 | | - Predicate activeContactsFilter = contactService.createActiveContactsFilter(cb, contactPersonsRoot); |
316 | | - Predicate contactUserFilter = contactService.createUserFilter(cb, contactPersonsQuery, contactPersonsRoot); |
317 | | - contactPersonsQuery.where( |
318 | | - contactPersonsFilter != null |
319 | | - ? and(cb, contactPersonsFilter, activeContactsFilter, contactUserFilter) |
320 | | - : and(cb, contactPersonsFilter, activeContactsFilter)); |
321 | | - contactPersonsQuery.distinct(true); |
322 | | - persons.addAll(em.createQuery(contactPersonsQuery).getResultList()); |
| 310 | + Predicate activeContactsFilter = contactService.createActiveContactsFilter(cb, personContactJoin); |
| 311 | + Predicate contactUserFilter = contactService.createUserFilter(cb, personQuery, personContactJoin); |
| 312 | + Predicate personContactPredicate = and(cb, personContactJoin.get(Contact.ID).isNotNull(), contactUserFilter, activeContactsFilter); |
323 | 313 |
|
324 | 314 | // Persons of event participants in active events |
325 | | - CriteriaQuery<PersonNameDto> eventPersonsQuery = cb.createQuery(PersonNameDto.class); |
326 | | - Root<EventParticipant> eventPersonsRoot = eventPersonsQuery.from(EventParticipant.class); |
327 | | - Join<EventParticipant, Person> eventPersonsJoin = eventPersonsRoot.join(EventParticipant.PERSON, JoinType.LEFT); |
328 | | - |
329 | | - eventPersonsQuery |
330 | | - .multiselect(eventPersonsJoin.get(Person.FIRST_NAME), eventPersonsJoin.get(Person.LAST_NAME), eventPersonsJoin.get(Person.UUID)); |
331 | | - |
332 | | - Predicate eventParticipantPersonsFilter = |
333 | | - buildSimilarityCriteriaFilter(criteria, cb, eventPersonsRoot.join(EventParticipant.PERSON, JoinType.LEFT)); |
334 | | - Predicate activeEventParticipantsFilter = eventParticipantService.createActiveEventParticipantsFilter(cb, eventPersonsRoot); |
335 | | - Predicate eventParticipantUserFilter = eventParticipantService.createUserFilter(cb, eventPersonsQuery, eventPersonsRoot); |
336 | | - eventPersonsQuery.where( |
337 | | - eventParticipantUserFilter != null |
338 | | - ? and(cb, eventParticipantPersonsFilter, activeEventParticipantsFilter, eventParticipantUserFilter) |
339 | | - : and(cb, eventParticipantPersonsFilter, activeEventParticipantsFilter)); |
340 | | - eventPersonsQuery.distinct(true); |
341 | | - persons.addAll(em.createQuery(eventPersonsQuery).getResultList()); |
| 315 | + Predicate activeEventParticipantsFilter = eventParticipantService.createActiveEventParticipantsFilter(cb, personEventParticipantJoin); |
| 316 | + Predicate eventParticipantUserFilter = eventParticipantService.createUserFilter(cb, personQuery, personEventParticipantJoin); |
| 317 | + Predicate personEventParticipantPredicate = |
| 318 | + and(cb, personEventParticipantJoin.get(EventParticipant.ID).isNotNull(), activeEventParticipantsFilter, eventParticipantUserFilter); |
| 319 | + |
| 320 | + caseContactEventParticipantLinkPredicate = or(cb, personCasePredicate, personContactPredicate, personEventParticipantPredicate); |
| 321 | + |
| 322 | + personQuery.where(and(cb, personSimilarityFilter, caseContactEventParticipantLinkPredicate)); |
| 323 | + personQuery.distinct(true); |
| 324 | + |
| 325 | + TypedQuery<PersonNameDto> query = em.createQuery(personQuery); |
| 326 | + if (limit != null) { |
| 327 | + query.setMaxResults(limit); |
| 328 | + } |
| 329 | + return query.getResultList(); |
| 330 | + } |
342 | 331 |
|
343 | | - return persons; |
| 332 | + public void setSimilarityThresholdQuery() { |
| 333 | + double nameSimilarityThreshold = configFacade.getNameSimilarityThreshold(); |
| 334 | + Query q = em.createNativeQuery("select set_limit(" + nameSimilarityThreshold + ")"); |
| 335 | + q.getSingleResult(); |
344 | 336 | } |
345 | 337 |
|
346 | 338 | public List<Person> getDeathsBetween(Date fromDate, Date toDate, District district, Disease disease, User user) { |
@@ -411,8 +403,7 @@ public Predicate buildSimilarityCriteriaFilter(PersonSimilarityCriteria criteria |
411 | 403 |
|
412 | 404 | String name = criteria.getFirstName() + " " + criteria.getLastName(); |
413 | 405 |
|
414 | | - double nameSimilarityThreshold = configFacade.getNameSimilarityThreshold(); |
415 | | - filter = and(cb, filter, cb.gt(cb.function("similarity", double.class, nameExpr, cb.literal(name)), nameSimilarityThreshold)); |
| 406 | + filter = and(cb, filter, cb.isTrue(cb.function(SIMILARITY_OPERATOR, boolean.class, nameExpr, cb.literal(name)))); |
416 | 407 | } |
417 | 408 |
|
418 | 409 | if (criteria.getSex() != null) { |
|
0 commit comments