Алгоритм сопоставления объектов в Java + Hibernate

Я пишу приложение с 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 один за другим. Вернуть объект с наибольшим совпадением.

Как лучше всего это сделать? Может есть какие-то готовые решения?

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
0
250
2

Ответы 2

Я бы выполнил только один запрос к базе данных, который будет искать, соответствует ли какой-либо атрибут.

Затем я бы написал функцию, которая вычисляет для каждого человека, сколько атрибутов соответствует, и возвращает значение наилучшего совпадения.

Затем вы можете заказать список по наиболее подходящему значению.

Конечно, есть бесконечные способы решения проблемы, и у каждого есть свои плюсы и минусы.

Итак, вот плюсы и минусы предлагаемого мной решения:

Плюсы:

  • У вас меньше взаимодействия с базой данных. => Это должно быть более быстрое решение, чем то, что указано в ответе.
  • Вы очень гибко подходите к расчету наилучшего совпадения. Это одна из функций, с помощью которой вы можете проверить и вычислить все, что захотите.

Минусы:

  • У вас есть два раздела в разных местах, которые должны быть синхронизированы. Запрос базы данных, выполняющий поиск по всем критериям, и функция вычисления, которая проверяет одни и те же атрибуты. => скрытый договор и связь

  • Если поиск по критериям вернет массивный набор записей, это решение не подходит. Затем вам нужно каким-то образом вычислить коэффициент внутри 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;
}

Другие вопросы по теме