Интеграция ElasticSearch и SpringBoot и использование методов JPA

Elasticsearch

Полный пример кода можно найти в личном репозитории GitHub :(github.com/KimZing), Содержит контроллер/репозиторий и тестовый код.

Добро пожаловать, звезда, если есть какие-либо ошибки, добро пожаловать, чтобы исправить_

1. Знакомство с окружающей средой

  • idea 2016.3
  • jdk 1.8
  • ElasticSearch 2.4 (причина, по которой последняя версия не используется, заключается в том, что версии SpringBoot и ES должны совпадать, а SpringBoot Starter в настоящее время не поддерживает последнюю версию)

2. Введение в ЭС

Студенты, разработавшие поиск Java, должны знать поисковую систему lucene, но lucene — это просто поисковая система, такая же, как двигатель автомобиля, который важен, но не может использоваться напрямую. Позже появился известный поиск solr, который предоставлял соответствующий интерфейс веб-операций и работу java API, но параллелизм данных solr и производительность большого объема данных сравнивали с опоздавшим ES. Определенный пробел все же есть, и ES рождается для поддержки распределенных кластеров.

Что такое ЭС? Мы можем сравнить ES с базой данных Mysql, которая также используется для хранения данных, но предоставляет больше функций поиска, чем Mysql, таких как поиск по сегментации слов, поиск по релевантности и т. д., а скорость поиска не того же уровня. ES может достигать скорости запроса миллионов данных в секунду. Далее сравните концепции, используемые в ES с Mysql.

поле объяснять
index Индекс эквивалентен библиотеке в Mysql.Например, если есть библиотека под названием «jd», в ней можно создать много таблиц для хранения разных типов данных, а таблица — это тип в ES.
type Тип, эквивалентный таблице в Mysql, хранящий данные типа json
document Документ, документ эквивалентен строке данных в Mysql.
shards Фрагментация в обычном понимании означает, что данные разделены на несколько областей для хранения, что можно понимать как подбазу данных и подтаблицу в mysql (не очень уместно)
replicas Резервная копия — это количество резервных копий шардов, что эквивалентно резервной базе данных mysql.

ES использует данные json для передачи данных, например {username:king,age:12}, тогда все эти данные json являются документом, а имя пользователя и возраст — полями.

3. SpringBoot интегрирует зависимости ES

//ES的核心依赖Starter
compile('org.springframework.boot:spring-boot-starter-data-elasticsearch')
//jna依赖,否则项目启动时,会报classNotFound: native method disable的错误
compile("com.sun.jna:jna:3.0.9")
//添加web支持,方便测试
compile('org.springframework.boot:spring-boot-starter-web')
testCompile('org.springframework.boot:spring-boot-starter-test')

В-четвертых, файл конфигурации

Конфигурационный файл можно настроить или нет.Структура зависимостей data-elasticsearch уже содержит jars Lucene и ES.SpringBoot автоматически сгенерирует для нас репозиторий ES локально, а под проектом будет автоматически сгенерирована папка данных для его хранения , данные ЭС. Если мы не настроим экземпляр ES, Тогда SpringBoot автоматически сгенерирует этот инстанс ES, конечно, производительность точно нехорошая, поэтому мы все равно используем свой инстанс ES.

Структура зависимостей data-elasticsearch

data-elasticsearch的依赖结构
Конфигурация для подключения к отдельному экземпляру ES выглядит следующим образом

spring:
  data:
    #ElasticSearch的连接地址
    elasticsearch:
      cluster-name: elasticsearch
      cluster-nodes: localhost:9300

Для установки ES вы можете обратиться к моему другому сообщению в блоге CentOS6.5 Установка ES учебник

Пять, напишите класс объекта хранения

Следующие три аннотации в основном используются при написании классов сущностей.

1. Аннотация к классу: @Document (эквивалент @Entity/@Table объектов Hibernate) (обязательно)

тип Имя свойства По умолчанию инструкция
String indexName никто Имя индексной библиотеки, рекомендуется называть ее именем проекта
String type "" Тип, рекомендуется называть сущность именем сущности
short shards 5 Количество разделов по умолчанию
short replica 1 Количество резервных копий по умолчанию на раздел
String refreshInterval "1s" Интервал обновления
String indexStoreType "fs" Тип хранения индексного файла

2. Аннотация первичного ключа: @Id (эквивалентно аннотации @Id первичного ключа сущности Hibernate) (обязательно)

Просто личность, никаких атрибутов.

3. Аннотация свойства @Field (эквивалент аннотации @Column сущности Hibernate)

@Field по умолчанию можно не указывать, и все свойства будут добавлены в ES по умолчанию.

тип Имя свойства По умолчанию инструкция
FileType type FieldType.Auto Типы свойств определяются автоматически
FileType index FieldIndex.analyzed Сегментация слов по умолчанию
boolean store false Исходный текст не сохраняется по умолчанию
String searchAnalyzer "" Указывает токенизатор для использования при поиске в поле
String indexAnalyzer "" Токенизатор, указанный при индексации указанного поля
String[] ignoreFields {} Если поле нужно игнорировать

4. Пример класса сущностей

@Data  //lombok注解,会自动生成setter/getter,需要引入lombok的包才能使用。
@Document(indexName = "shop", type = "user", refreshInterval = "0s")
public class User {

    @Id
    private Long id;

    private String username;

    private String realname;

    private String password;

    private Integer age;

    //这三个注解是为了前台序列化java8 LocalDateTime使用的,需要引入jsr310的包才可以使用
    @JsonSerialize(using = LocalDateTimeSerializer.class)
    @JsonDeserialize(using = LocalDateTimeDeserializer.class)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
    private LocalDateTime birth;

}

Шесть, напишите склад

1. Написание кода

Чтобы написать класс, который наследует ElasticsearchRepository, вам нужно написать два дженерика, первый представляет тип объекта, который нужно сохранить, а второй представляет тип первичного ключа.Например, напишите репозиторий класса пользователя следующим образом:

/**
 * @author kingboy--KingBoyWorld@163.com
 * @date 2017/11/27 下午10:10
 * @desc 用户仓库.
 */
public interface UserRepository extends ElasticsearchRepository<User, Long>{

}

Давайте взглянем на структуру наследования ElasticsearchRepository (ниже), на самом деле мы можем обнаружить, что это все еще набор репозиториев JPA, тогда мы можем фактически использовать набор интерфейсных операций JPA для добавления, удаления, изменения и проверить данные. Spring автоматически сгенерирует соответствующие прокси-классы в соответствии с именами методов для реализации этих методов.

2. Основные операции CRUD

Давайте взглянем на некоторые из основных методов, которые реализованы в ElasticsearchRepository. Имена этих методов были хорошо объяснены, так что вы можете убедиться сами, их легко понять.

3. Немного сложная операция

Эти методы, поставляемые с jpa, конечно, не могут удовлетворить потребности нашего бизнеса, так как же нам настроить эти методы? Пока мы используем определенное слово для определения имени метода, Spring будет анализировать имя метода, которое мы написали, Генерировать соответствующие экземпляры для обработки данных, легко ли это? Затем используйте примеры из официальной документации Spring для демонстрации.

Сначала посмотрите на описание ключевых слов.


ключевые слова Пример использования ES-запрос эквивалентен
And findByNameAndPrice {"bool" : {"должен" : [ {"поле" : {"имя" : "?"}}, {"поле" : {"цена" : "?"}} ]}}
Or findByNameOrPrice {"bool" : {"следует" : [ {"поле" : {"имя" : "?"}}, {"поле" : {"цена" : "?"}} ]}}
Is findByName {"bool" : {"должен" : {"поле" : {"имя" : "?"}}}}
Not findByNameNot {"bool" : {"must_not" : {"поле" : {"имя" : "?"}}}}
Between findByPriceBetween {"bool" : {"должен" : {"диапазон" : {"цена" : {"от" : ?,"до" : ?,"include_lower" : true,"include_upper" : true}}}}}
LessThanEqual findByPriceLessThan {"bool" : {"должен" : {"диапазон" : {"цена" : {"от" : ноль, "до" : ?, "include_lower" : истина, "include_upper" : истина}}}}}
GreaterThanEqual findByPriceGreaterThan {"bool" : {"должен" : {"диапазон" : {"цена" : {"от" : ?, "до" : ноль, "include_lower" : правда, "include_upper" : правда}}}}}
Before findByPriceBefore {"bool" : {"должен" : {"диапазон" : {"цена" : {"от" : ноль, "до" : ?, "include_lower" : истина, "include_upper" : истина}}}}}
After findByPriceAfter {"bool" : {"должен" : {"диапазон" : {"цена" : {"от" : ?, "до" : ноль, "include_lower" : правда, "include_upper" : правда}}}}}
Like findByNameLike {"bool" : {"должен" : {"поле" : {"имя" : {"запрос" : "?*","analyze_wildcard" : правда}}}}}
StartingWith findByNameStartingWith {"bool" : {"должен" : {"поле" : {"имя" : {"запрос" : "?*","analyze_wildcard" : true}}}}}
EndingWith findByNameEndingWith {"bool" : {"должен" : {"поле" : {"имя" : {"запрос" : "*?", "analyze_wildcard" : правда}}}}}
Contains/Containing findByNameContaining {"bool" : {"должен" : {"поле" : {"имя" : {"запрос" : "?", "analyze_wildcard" : правда}}}}}
In findByNameIn(Collectionnames) {"bool" : {"должен" : {"bool" : {"должен" : [ {"поле" : {"имя" : "?"}}, {"поле" : {"имя" : "?" }} ]}}}}
NotIn findByNameNotIn(Collectionnames) {"bool" : {"must_not": {"bool" : {"следует" : {"поле" : {"имя" : "?"}}}}}}
True findByAvailableTrue {"bool" : {"должен" : {"поле" : {"доступно" : true}}}}
False findByAvailableFalse {"bool" : {"должен" : {"поле" : {"доступно" : false}}}}
OrderBy findByAvailableTrueOrderByNameDesc {"sort" : [{ "name" : {"order" : "desc"} }], "bool" : {"must" : {"поле" : {"доступно" : true}}}}

Давайте напишем несколько примеров для демонстрации. Перечислен только слой хранилища. Работа в целом проверена, проблем нет. Если вам нужен общий код, перейдите в репозиторий github в верхней части этой статьи.

/**
 * @author kingboy--KingBoyWorld@163.com
 * @date 2017/11/27 下午10:10
 * @desc 用户仓库.
 */
public interface UserRepository extends ElasticsearchRepository<User, Long>{

    /**
     * 查询用户名为username的用户
     * @param username
     * @return
     */
    List<User> findByUsername(String username);

    /**
     * 查询用户名为username并且真实姓名为realname的用户
     * @param username
     * @param realname
     */
    List<User> findByUsernameAndRealname(String username, String realname);

    /**
     * 查询用户名为username或者姓名为realname的用户
     */
    List<User> findByUsernameOrRealname(String username, String realname);

    /**
     * 查询用户名不是username的所有用户
     * @param username
     * @return
     */
    List<User> findByUsernameNot(String username);


    /**
     * 查询年龄段为ageFrom到ageTo的用户
     * @param ageFrom
     * @param ageTo
     * @return
     */
    List<User> findByAgeBetween(Integer ageFrom, Integer ageTo);

    /**
     * 查询生日小于birthTo的用户
     */
    List<User> findByBirthLessThan(LocalDateTime birthTo);


    /**
     * 查询生日段大于birthFrom的用户
     * @param birthFrom
     * @return
     */
    List<User> findByBirthGreaterThan(LocalDateTime birthFrom);

    /**
     * 查询年龄小于或等于ageTo的用户
     */
    List<User> findByAgeBefore(Integer ageTo);

    /**
     * 查询年龄大于或等于ageFrom的用户
     * @param ageFrom
     * @return
     */
    List<User> findByAgeAfter(Integer ageFrom);

    /**
     * 用户名模糊查询
     * @param username
     * @return
     */
    List<User> findByUsernameLike(String username);


    /**
     * 查询以start开头的用户
     * @param start
     * @return
     */
    List<User> findByUsernameStartingWith(String start);

    /**
     * 查询以end结尾的用户
     * @return
     */
    List<User> findByUsernameEndingWith(String end);

    /**
     * 查询用户名包含word的用户
     * @param word
     * @return
     */
    List<User> findByUsernameContaining(String word);

    /**
     * 查询名字属于usernames中的用户
     * @param usernames
     * @return
     */
    List<User> findByUsernameIn(Collection<String> usernames);

    /**
     * 查询名字不属于usernames中的用户
     * @param usernames
     * @return
     */
    List<User> findByUsernameNotIn(Collection<String> usernames);

    /**
     *最后来个复杂点的:查询年龄小于ageTo,姓名以start开头,id大于idTo的用户,并且按照年龄倒序
     * @return
     */
    List<User> findByAgeBeforeAndUsernameStartingWithAndIdGreaterThanOrderByAgeDesc(Integer ageTo, String start, Long idTo);

}

4. Более сложные операции

Мы можем использовать аннотацию @Query для запроса, что требует от нас написания операторов запроса ES самостоятельно, и нам нужно знать запросы ES, На самом деле это очень просто. Взгляните на официальный пример

public interface BookRepository extends ElasticsearchRepository<Book, String> {
        @Query("{\"bool\" : {\"must\" : {\"field\" : {\"name\" : \"?0\"}}}}")
        Page<Book> findByName(String name,Pageable pageable);
}

5. Мы также можем запросить по аналогии с критериями в Hibernate,

На данный момент нам нужно самим написать условия запроса, внедрить UserRepository в класс, использовать метод поиска для передачи параметров запроса, а затем получить результаты запроса. Пример выглядит следующим образом:

/**
 * @author kingboy--KingBoyWorld@163.com
 * @date 2017/11/28 下午12:53
 * @desc 用户服务.
 */
@Service
public class UserService {

    @Resource
    UserRepository userRepository;

    public Page<User> getUsers() {
        //创建builder
        BoolQueryBuilder builder = QueryBuilders.boolQuery();
        //builder下有must、should以及mustNot 相当于sql中的and、or以及not
        //设置模糊搜索,真实姓名中包含金的用户
        builder.must(QueryBuilders.fuzzyQuery("realname", "金"));
        //设置用户名为king
        builder.must(new QueryStringQueryBuilder("king").field("username"));

        //排序
        FieldSortBuilder sort = SortBuilders.fieldSort("age").order(SortOrder.DESC);

        //设置分页
        //====注意!es的分页和Hibernate一样api是从第0页开始的=========
        PageRequest page = new PageRequest(0, 2);

        //构建查询
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        //将搜索条件设置到构建中
        nativeSearchQueryBuilder.withQuery(builder);
        //将分页设置到构建中
        nativeSearchQueryBuilder.withPageable(page);
        //将排序设置到构建中
        nativeSearchQueryBuilder.withSort(sort);
        //生产NativeSearchQuery
        NativeSearchQuery query = nativeSearchQueryBuilder.build();

        //执行,返回包装结果的分页
        Page<User> resutlList = userRepository.search(query);

        return resutlList;
    }
}

Автор: Ким Зинг Источник: ЦСДН оригинал:blog.CSDN.net/king boy W или останься…