如何使用JPA的MySQL全文搜索

时间:2012-02-08 01:59:21

标签: jpa full-text-search eclipselink

我想使用JPA使用MySQL's full text search功能,而不必使用本机查询。

我正在使用EclipseLink,它具有支持本机SQL命令的功能:FUNC。但是,帮助示例仅显示这与简单的MySQL函数一起使用。我尽最大的努力让它与MATCH&反对如下:

@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 个答案:

答案 0 :(得分:8)

改进了@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

答案 1 :(得分:5)

FUNC仅适用于普通打印功能,

即。 MATCH(arg1,arg2)

因为MATCH arg1 AGAINST arg2没有按照正常打印功能的方式打印,所以FUNC不能用来调用它。

EclipseLink ExpressionOperators确实支持这样的打印功能,因此您可以定义自己的ExpressionOperator,但ExpressionOperators目前仅通过EclipseLink Expression查询支持,而不是通过JPQL支持。您可以记录增强功能以​​在JPQL中获得操作员支持。

您也可以使用本机SQL查询。

答案 2 :(得分:4)

只是为了完成答案:我遇到了同样的问题,但是使用了标准构建器。如果您正在使用EclipseLink,那么就可以解决标准实现中的限制:

  1. 将JPA表达式转换为EclipseLink表达式
  2. 使用sql方法
  3. 如果您匹配复合索引,请使用函数方法
  4. 创建它

    示例:

        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));
    

答案 3 :(得分:2)

EclipseLink 4.0中的新SQL运算符怎么样?我认为它可以帮助您从JPQL进行全文搜索。但是你必须升级到EclipseLink 4.0。

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

修改
抱歉迟到了。
已正确使用EclispeLink 2.4.0“SQL”运算符和MySQL全文搜索

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

其中name是定义了全文索引的列。 :searchTerm是您用于搜索的字符串 工作没有问题。

答案 4 :(得分:1)

详细说明詹姆斯的答案:

似乎我有幸使用

扩展了mysql方言
registerFunction("match", new SQLFunctionTemplate(DoubleType.INSTANCE,     "match(?1) against  (?2 in boolean mode)")); 

并通过以下jpql片段调用该函数

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

我不得不猜测返回类型,但这应该让你开始。

答案 5 :(得分:0)

最终工作

如果您将表格列设置为索引全搜索

@NamedNativeQuery(name =“searchclient”, query =“SELECT * FROM client WHERE MATCH(clientFullName,lastname,secondname,firstphone,” +“第二部电话,工作电话,其他电话,其他电话1,” +“otherphone2,detailsFromClient,email,company,” +“address,contractType,paymantCondition)AGAINST(?)”,

列表list = em.createNamedQuery(“searchclient”)。setParameter(1,searchKey).getResultList();

答案 6 :(得分:0)

最简单的变体是使用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;
    }
}