2015-09-15 2 views
2

Я использую Hibernate и QueryDSL вместе с PostgreSQL в приложении Spring и сталкиваются с некоторыми проблемами производительности с моими фильтрованными списками. Используя класс StringPath, я вызываю либо startWithIgnoreCase, endWithIgnoreCase или containsIgnoreCase. Он появляется сгенерированный запрос имеет следующий ИНЕКЕ:QueryDSL не использует индексы Postgres

WHERE lower(person.firstname) LIKE ? ESCAPE '!' 

Используя ниже, запрос не пользуясь индексов Postgres. В базе данных разработчиков запросы занимают до 1 сек вместо 10 мс с ключевым словом ILIKE.

Есть ли способ получить предикат с помощью Postgres 'ILIKE, поскольку Ops, похоже, не обеспечивает его?

Благодаря

+3

Вы можете создать индекс на 'lower (firstname)', который должен использоваться запросом (но только если подстановочные знаки находятся в конце значения сравнения). Другим вариантом было бы создать индекс триграмм: http://www.depesz.com/index.php/2011/02/19/waiting-for-9-1-faster-likeilike/ –

+0

Спасибо, он отвечает на мои первоначальные вопрос, хотя я считаю, что борюсь с другими проблемами. Мы уже используем индексы триграмм, но похоже, что Postgre не использует ожидаемый индекс. Я попытаюсь выяснить, можем ли мы удалить конфликтный индекс (используя btree на двух столбцах), иначе я немного не буду разбираться в этой проблеме. Я также пробовал использовать тип данных citext, но анализатор запросов показывает, что индекс не используется, и поиск указывает на эту проблему, с более низким индексом в качестве решения: http://dba.stackexchange.com/questions/105244/index- on-column-with-data-type-citext-not-used/105250 # 105250 –

+0

Прочтите это: https://wiki.postgresql.org/wiki/Slow_Query_Questions, а затем добавьте недостающую информацию в свой вопрос. Это невозможно ответить без дополнительной информации (и: это либо Postgres, PostgreSQL, либо просто pg, но никогда не Postgre) –

ответ

2

Если бы обновить это:

Мы нашли способ создания необходимых операторов Postgres путем регистрации функции SQL с помощью ILIKE, в нашем обычае Hibernate Диалект.

Пример с ILIKE:

//Postgres Constants Operators 
public class PostgresOperators { 
    private static final String NS = PostgresOperators.class.getName(); 
    public static final Operator<Boolean> ILIKE = new OperatorImpl<>(NS, "ILIKE"); 
} 

//Custom JPQLTemplates 
public class PostgresTemplates extends HQLTemplates { 

    public static final PostgresTemplates DEFAULT = new PostgresTemplates(); 

    public PostgresTemplates() { 
     super(); 
     add(PostgresOperators.ILIKE, "my_ilike({0},{1})"); 
    } 
} 

Укажите JPQLTemplates при использовании jpaquery

new JPAQuery(entityManager, PostgresTemplates.DEFAULT); 

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

Наш application.yml задающий:

#SEE JPA http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html  
spring.data.jpa:com.example.customDialect.config.database.ExtendedPostgresDialect 

Тогда

public class ExtendedPostgresDialect extends org.hibernate.dialect.PostgreSQL82Dialect { 
    public ExtendedPostgresDialect() { 
     super(); 
     registerFunction("my_ilike", new PostgreSQLIlikeFunction()); 
    } 
} 

Мы пытались использовать registerKeyword ("ILIKE"), не работал, мы остались с нашей функцией и следующее реализация.

public class PostgreSQLIlikeFunction implements SQLFunction { 

    @Override 
    public Type getReturnType(Type columnType, Mapping mapping) 
     throws QueryException { 
     return new BooleanType(); 
    } 

    @SuppressWarnings("unchecked") 
    @Override 
    public String render(Type firstArgumentType, List args, SessionFactoryImplementor factory) throws QueryException { 
     if (args.size() != 2) { 
      throw new IllegalArgumentException(
       "The function must be passed 2 arguments"); 
     } 

     String str1 = (String) args.get(0); 
     String str2 = (String) args.get(1); 

     return str1 + " ilike " + str2; 
    } 

    @Override 
    public boolean hasArguments() { 
     return true; 
    } 

    @Override 
    public boolean hasParenthesesIfNoArguments() { 
     return false; 
    } 

} 

Это в значительной степени это, теперь мы можем использовать ILIKE следующим образом:

BooleanOperation.create(PostgresOperators.ILIKE, expression1, expression2).isTrue() 
0

Я получил точно такой же вопрос - lower(column) вызывает вычисление статистики неправильно пг и запрос планируется не эффективно, ilike решает проблему. Я не понял, какие части ответа OP имеют отношение к решению, поэтому заново изобрел тот же подход, но немного короче.

  1. Ввести новый диалект с my_ilike функции и его реализации:

    public class ExtendedPostgresDialect extends org.hibernate.dialect.PostgreSQL9Dialect { 
        public ExtendedPostgresDialect() { 
         super(); 
         registerFunction("my_ilike", new SQLFunctionTemplate(BooleanType.INSTANCE, "(?1 ilike ?2)")); 
        } 
    } 
    
  2. Укажите этот диалект для использования Hibernate (я использую Java конфигурации):

    Properties props = new Properties(); 
    props.setProperty("hibernate.dialect", "com.example.ExtendedPostgresDialect"); 
    factory.setJpaProperties(props); 
    
  3. Вот и все , теперь вы можете использовать его:

    BooleanTemplate.create("function('my_ilike', {0}, {%1%})", stringPath, value).isTrue();