Я пишу приложение с Spring MVC и Hibernate, я его изучаю. У меня есть объект Person с 12 переменными. Задача состоит в том, чтобы найти существующий объект Person из базы данных, который наилучшим образом соответствует сопоставимым. Например, inputPerson_1 соответствует databasePerson_423 для 9 переменных, что соответствует 75%.
У меня есть следующая идея, но она выглядит очень перегруженной для базы данных и, очевидно, слабым решением:
1) создайте 12 последовательных запросов к MySQL, чтобы собрать все возможные объекты соответствия и сохранить в HashSet, где первым параметром будет идентификатор человека
query = session.createQuery("from Person where lower(firstName) like :inputFirstName)
query = session.createQuery("from Person where lower(lastName) like :inputLastName)
...
2) создайте int max и сравните переменные объекта из HashSet один за другим. Вернуть объект с наибольшим совпадением.
Как лучше всего это сделать? Может есть какие-то готовые решения?




Я бы выполнил только один запрос к базе данных, который будет искать, соответствует ли какой-либо атрибут.
Затем я бы написал функцию, которая вычисляет для каждого человека, сколько атрибутов соответствует, и возвращает значение наилучшего совпадения.
Затем вы можете заказать список по наиболее подходящему значению.
Конечно, есть бесконечные способы решения проблемы, и у каждого есть свои плюсы и минусы.
Итак, вот плюсы и минусы предлагаемого мной решения:
Плюсы:
Минусы:
У вас есть два раздела в разных местах, которые должны быть синхронизированы. Запрос базы данных, выполняющий поиск по всем критериям, и функция вычисления, которая проверяет одни и те же атрибуты. => скрытый договор и связь
Если поиск по критериям вернет массивный набор записей, это решение не подходит. Затем вам нужно каким-то образом вычислить коэффициент внутри db, и вам нужно использовать индексы, которые работают с порядком и лимитом, чтобы вы не возвращали все строки и имели класс усилий O (log (n)).
Может быть, кто-то сочтет этот ответ полезным для собственных предложений ... Я решаю пока пойти по самому легкому пути. Для поиска похожих объектов:
public List<Person> searchSimilarPeople(Person person) {
Session session = sessionFactory.getCurrentSession();
Query query = null;
query = session.createQuery("from Person where lower(firstName) like :s_name or lower(lastName) like :f_name " +
"or lower(email) like :email or lower(address1) like :address1 or lower(address2) like :address2 " +
"or lower(city) like :city or lower(region_state) like :state or lower(zip) like :zip " +
"or lower(country) like :country", Person.class);
query.setParameter("s_name", "%" + person.getFirstName() + "%");
query.setParameter("f_name", "%" + person.getLastName() + "%");
query.setParameter("address1", "%" + person.getAddress1() + "%");
query.setParameter("address2", "%" + person.getAddress2() + "%");
query.setParameter("city", "%" + person.getCity() + "%");
query.setParameter("state", "%" + person.getRegion_state() + "%");
query.setParameter("zip", "%" + person.getZip() + "%");
query.setParameter("country", "%" + person.getCountry() + "%");
query.setParameter("email", "%" + person.getEmail() + "%");
return query.getResultList();
}
Я попытался погрузиться в процесс поиска Hibernate с помощью Lucene, но, на мой взгляд, это не лучший вариант для новичка в Hibernate. Но я буду признателен, если кто-то предоставит краткое объяснение того же процесса с использованием Lucene :)
Для сравнения объектов: Почти все вары в сравниваемых объектах являются String, поэтому я решил использовать расстояние Яро-Винклера, чтобы получить коэффициент соответствия строки, а затем умножить его на 0,11 (у меня есть 9 переменных для сравнения, поэтому максимально возможный результат совпадения равен 0,99)
@Override
public Double getMatch(Person inputPerson, Person dbPerson) {
double intermResult = 0;
double finalResult = 0;
// FirstName
intermResult = StringUtils.getJaroWinklerDistance(inputPerson.getFirstName().toLowerCase(), dbPerson.getFirstName().toLowerCase());
finalResult += (intermResult * rateCoefficient);
// LAST NAME
intermResult = StringUtils.getJaroWinklerDistance(inputPerson.getLastName().toLowerCase(), dbPerson.getLastName().toLowerCase());
finalResult += (intermResult * rateCoefficient);
// Email
intermResult = StringUtils.getJaroWinklerDistance(inputPerson.getEmail().toLowerCase(), dbPerson.getEmail().toLowerCase());
finalResult += (intermResult * rateCoefficient);
// ADDRESS 1
intermResult = StringUtils.getJaroWinklerDistance(inputPerson.getAddress1().toLowerCase(), dbPerson.getAddress1().toLowerCase());
finalResult += (intermResult * rateCoefficient);
// ADDRESS 2
if (inputPerson.getAddress2() != null && dbPerson.getAddress2() != null) {
intermResult = StringUtils.getJaroWinklerDistance(inputPerson.getAddress2().toLowerCase(), dbPerson.getAddress2().toLowerCase());
finalResult += (intermResult * rateCoefficient);
}
// CITY
intermResult = StringUtils.getJaroWinklerDistance(inputPerson.getCity().toLowerCase(), dbPerson.getCity().toLowerCase());
finalResult += (intermResult * rateCoefficient);
// STATE
if (inputPerson.getRegion_state() != null && dbPerson.getRegion_state() != null) {
intermResult = StringUtils.getJaroWinklerDistance(inputPerson.getRegion_state().toLowerCase(), dbPerson.getRegion_state().toLowerCase());
finalResult += (intermResult * rateCoefficient);
}
// ZIP
intermResult = StringUtils.getJaroWinklerDistance(inputPerson.getZip().toLowerCase(), dbPerson.getZip().toLowerCase());
finalResult += (intermResult * rateCoefficient);
// COUNTRY
intermResult = StringUtils.getJaroWinklerDistance(inputPerson.getCountry().toLowerCase(), dbPerson.getCountry().toLowerCase());
finalResult += (intermResult * rateCoefficient);
finalResult = Precision.round(finalResult, 3);
System.out.println("Final Result. ID : " + dbPerson.getId() + " " + finalResult);
return finalResult;
}