Как использовать полнотекстовый поиск MySQL из JPA - PullRequest
4 голосов
/ 08 февраля 2012

Я хочу использовать Функции полнотекстового поиска MySQL с использованием JPA, без необходимости использовать собственный запрос.

Я использую EclipseLink, которая имеет функцию для поддержки собственных команд SQL: FUNC .Тем не менее, примеры помощи показывают только то, как это используется с простыми функциями MySQL.Моя лучшая попытка заставить его работать с MATCH & AGAINST заключается в следующем:

@PersistenceContext(name="test")
EntityManager em;

Query query = em.createQuery("SELECT person FROM People person WHERE FUNC('MATCH', person.name) FUNC('AGAINST', :searchTerm)");
...
query.getResultList();

, что дает следующее исключение:

Caused by: NoViableAltException(32@[()* loopback of 822:9: (m= MULTIPLY right= arithmeticFactor | d= DIVIDE right= arithmeticFactor )*])
    at org.eclipse.persistence.internal.libraries.antlr.runtime.DFA.noViableAlt(DFA.java:159)
    at org.eclipse.persistence.internal.libraries.antlr.runtime.DFA.predict(DFA.java:116)
    at org.eclipse.persistence.internal.jpa.parsing.jpql.antlr.JPQLParser.arithmeticTerm(JPQLParser.java:4557)
    ... 120 more

Я открыт для альтернатив, отличных от использованияМетод FUNC.

Я использую EJB 3 и EclipseLink 2.3.1.

Ответы [ 7 ]

9 голосов
/ 01 октября 2016

Улучшенный ответ @Markus Barthlen, который работает для Hibernate.

Создайте пользовательский диалект

public class MySQLDialectCustom extends MySQL5Dialect {
  public MySQLDialect() {
    super();
    registerFunction("match", new SQLFunctionTemplate(StandardBasicTypes.DOUBLE,
        "match(?1) against  (?2 in boolean mode)"));
  }
}

и зарегистрируйте его, установив свойство hibernate.dialect.

Использованиеэто

в JPQL:

Query query = entityManager
    .createQuery("select an from Animal an " +
             "where an.type = :animalTypeNo " +
             "and match(an.name, :animalName) > 0", Animal.class)
    .setParameter("animalType", "Mammal")
    .setParameter("animalName", "Tiger");
List<Animal> result = query.getResultList();
return result;

или с Criteria API:

CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Animal> criteriaQuery = criteriaBuilder.createQuery(Animal.class);
Root<Animal> root = criteriaQuery.from(Animal.class);

List<Predicate> predicates = new ArrayList<>();

Expression<Double> match = criteriaBuilder.function("match", Double.class, root.get("name"),
criteriaBuilder.parameter(String.class, "animalName"));

predicates.add(criteriaBuilder.equal(root.get("animalType"), "Mammal"));
predicates.add(criteriaBuilder.greaterThan(match, 0.));

criteriaQuery.where(predicates.toArray(new Predicate[]{}));

TypedQuery<Animal> query = entityManager.createQuery(criteriaQuery);
List<Animal> result = query.setParameter("animalName", "Tiger").getResultList();

return result;

Некоторые подробности в этом сообщении в блоге: http://pavelmakhov.com/2016/09/jpa-custom-function

5 голосов
/ 08 февраля 2012

FUNC работает только с обычными функциями печати,

т.е. МАТЧ (arg1, arg2)

поскольку MATCH arg1 ПРОТИВ arg2 не печатается так, как обычно печатается функция, FUNC не может использоваться для ее вызова.

EclipseLink ExpressionOperators поддерживают такие функции печати, что вы можете определить свой собственный ExpressionOperator, но ExpressionOperators в настоящее время поддерживаются только через запросы EclipseLink Expression, но не через JPQL. Вы можете зарегистрировать расширение для поддержки оператора в JPQL.

Вы также можете использовать собственный запрос SQL.

4 голосов
/ 21 января 2015

Просто для завершения ответа: у меня была та же проблема, но с использованием построителя критериев. Вот как вы можете обойти ограничения в стандартной реализации, если вы используете EclipseLink:

  1. Приведение выражения JPA к выражению EclipseLink
  2. Используйте метод sql
  3. Если вы сопоставляете с составным индексом, создайте его, используя метод функции

Пример:

    JpaCriteriaBuilder cb = (JpaCriteriaBuilder) cb;

    List<String> args = new ArrayList();
    args.add("Keyword");

    Expression<Boolean> expr = cb.fromExpression (
      cb.toExpression(
         cb.function("", String.class,
           table.get(Table_.text1), table.get(Table_.text2)) 
      )                          
       .sql("MATCH ? AGAINST (?)", args)
     );

    query.where(expr);

Если вам нужно привести выражение к предикату, используйте следующее:

query.where( cb.gt(expr, 0));
2 голосов
/ 08 июля 2012

А как насчет нового оператора SQL в EclipseLink 4.0? Я думаю, что это может помочь вам сделать полнотекстовый поиск из JPQL. Но вы должны перейти на EclipseLink 4.0.

http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/Support_for_Native_Database_Functions#SQL

Edit:
Извините за позднее обновление.
Проверено правильное использование оператора SQL SQL EclispeLink 2.4.0 с полнотекстовым поиском MySQL:

SELECT person FROM People person WHERE SQL('MATCH(name) AGAINST( ? )', :searchTerm)"

где name - столбец, для которого определен полнотекстовый индекс. :searchTerm - это строка, которую вы используете для поиска.
Работает без проблем.

1 голос
/ 30 апреля 2016

Чтобы уточнить ответ Джеймса:

Кажется, мне повезло, расширив диалект mysql с помощью

registerFunction("match", new SQLFunctionTemplate(DoubleType.INSTANCE,     "match(?1) against  (?2 in boolean mode)")); 

и вызов функции через следующий фрагмент jpql

match(" + binaryDataColumn + ",'" + StringUtils.join(words, " ") + "') > 0 

Мне нужно было угадать тип возвращаемого значения, но это должно помочь вам начать.

0 голосов
/ 15 июля 2017

Самый простой вариант - использовать NativeQuery

Пример использования его с сопоставлением с сущностью JPA (FiasAddress):

public class FiasServiceBean implements FiasService {

    @PersistenceContext(unitName = "fias")
    EntityManager entityManager;

    @Override
    public Collection<FiasAddress> search(String name, int limit, int aolevel) {
        Query query = entityManager.createNativeQuery(
                "SELECT fa.* FROM fias.addressobject fa" +
                        " WHERE MATCH(FORMALNAME) AGAINST (:name IN NATURAL LANGUAGE MODE)" +
                        " AND AOLEVEL = :AOLEVEL" +
                        " LIMIT :limit",
                FiasAddress.class
        );
        query.setParameter("name", name);
        query.setParameter("limit", limit);
        query.setParameter("AOLEVEL", aolevel);
        Iterator iterator = query.getResultList().iterator();
        ArrayList<FiasAddress> result = new ArrayList<>();
        while (iterator.hasNext()) {
            result.add((FiasAddress) iterator.next());
        }
        return result;
    }
}
0 голосов
/ 07 августа 2013

В конце концов работа

если вы установите столбцы таблицы с полным индексом поиска

@ NamedNativeQuery (name = "searchclient", query = "SELECT * FROM клиента WHERE MATCH (clientFullName, фамилия, второе имя, первый телефон," + "второй телефон, рабочий телефон, другой телефон, другой телефон1" + "otherphone2, detailsFromClient, электронная почта, компания" + "address, contractType, paymantCondition) ПРОТИВ (?)",

List list = em.createNamedQuery ("searchclient"). SetParameter (1, searchKey) .getResultList ();

...