你好,伙计:)
我最近在思考:如果我有搜索前端数据和Java的逻辑以进行后端,则应使用哪种技术来查找数据?在这种情况下,一些搜索引擎将是一个好主意。但是实施容易吗?我想尝试几件事并比较,哪个选项是最好的。
通过这样做,我了解它的工作原理,我想分享这一经验。
爪哇
首先,我们需要创建Java应用程序。我使用了春季的“ initializr”来获得我的项目的基本配置。 Spring Initializr
剧透!: - D
我的最终配置看起来像这样(Java版本18):
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.tutorial</groupId>
<artifactId>search</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>search</name>
<description>Tutorial project for search engine</description>
<properties>
<java.version>18</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate.search</groupId>
<artifactId>hibernate-search-mapper-orm</artifactId>
<version>6.1.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.search</groupId>
<artifactId>hibernate-search-backend-lucene</artifactId>
<version>6.1.1.Final</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>8.3.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.3</version>
</dependency>
<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
然后只需安装所有依赖项,我们就可以继续前进。
mvn install
或mvn package
mysql
好的,现在我们需要我们的数据库和一些数据。要获取数据,我们需要将/安装MySQL到本地计算机。为了使其更轻松,我们可以安装Workbench
也要获得gui。我们需要在 application.properties 中放入有关数据库(用户名,密码,数据库URL)的信息。
spring.datasource.url={url_path}
spring.datasource.username={database_username}
spring.datasource.password={database_password}
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
现在只键入 book 模型。我还添加了搜索引擎所需的注释。
重要的
一个名为 book 的表格应该存在于您的数据库中,应包括一些测试数据。
@Entity
@Indexed
@Table(name = "book")
@Document(indexName = "books", type = "book")
public class Book {
@Id
private int id;
@FullTextField
@Field(type = FieldType.Text, name = "name")
private String name;
@FullTextField
@Field(type = FieldType.Text, name = "isbn")
private String isbn;
public int getId() {
return id;
}
public String getIsbn() {
return isbn;
}
public String getName() {
return name;
}
public void setIsbn(String isbn) {
this.isbn = isbn;
}
public void setName(String name) {
this.name = name;
}
}
现在要从请求获得响应,我们需要 repository 类。
public interface BookRepository extends PagingAndSortingRepository<Book, Integer>
{
List<Book> findByName(String name);
List<Book> findByIsbn(String isbn);
}
现在要从URL获取数据,我们需要一个控制器。我只是为了获取响应并检查是否加载数据非常简单。
@RestController
@RequestMapping(value = "book")
public class BookController {
@Autowired
BookRepository bookRepository;
@GetMapping("/database")
ResponseEntity<Iterable<Book>> getBooksFromDatabase(@RequestParam("query") String query)
{
Iterable<Book> booksFromDatabase = bookRepository.findByIsbn(query);
return ResponseEntity.ok(booksFromDatabase);
}
}
好的,它似乎是通过数据库完成的。
并不是那么难,但这不是与搜索流相互作用的最灵活的选择。
让我们看一下搜索引擎。我们将从Java中的非常强大的技术开始。
冬眠
首先我们需要创建配置文件。
@Component
public class BookIndexConfiguration implements CommandLineRunner
{
@PersistenceContext
EntityManager entityManager;
@Override
@Transactional(readOnly = true)
public void run(String ...args) throws Exception
{
SearchSession searchSession = Search.session(entityManager);
MassIndexer indexer = searchSession.massIndexer(Book.class);
indexer.startAndWait();
}
}
在此处,Hibernate的 indexer 将在应用程序启动后使用模型中的数据创建索引。要探索索引中的书籍,我们还需要服务类。好,让我们创建一个。
@Service
public class BookSearchService {
@PersistenceContext
EntityManager entityManager;
@Transactional(readOnly = true)
public Iterable<Book> search(Pageable pageable, String query)
{
SearchSession session = Search.session(entityManager);
SearchResult<Book> result = session.search(Book.class)
.where(
f -> f.match().
fields("name", "isbn").
matching(query)
)
.fetch((int) pageable.getOffset(), pageable.getPageSize())
;
return result.hits();
}
}
我们具有搜索函数,该函数加载了当前会话并在索引中检查索引,如果查询与 name 或 isbn name 或。如果找到了某些对象,它将以 book book 对象的 itoble 对象返回
那就是这样!典型的数据库调用更容易吗?
现在我们需要配置其他搜索引擎。
Elasticsearch
好吧,现在我们需要更多的配置或可能不是。就像MySQL的情况一样,我们应该安装Elasticsearch。我将使用Docker image获得客户。另外,您可以使用以下链接下的一种方法Elasticsearch install
version: '3.7'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.3.2
container_name: elasticsearch
environment:
- xpack.security.enabled=false
- discovery.type=single-node
ports:
- 9200:9200
- 9300:9300
重要
为了测试目的,我禁用了安全模式。如果将应用程序部署到实时系统中,则不应执行此操作。
在安装和启动Elasticsearch之后,我们可以使用 client 。
创建更多配置文件。
public class BookElasticsearchClient {
private ElasticsearchClient client;
public ElasticsearchClient getClient()
{
if (client == null) {
initClient();
}
return client;
}
private void initClient()
{
RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build();
ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
client = new ElasticsearchClient(transport);
}
}
如果您使用用户名和密码的身份验证,则需要配置这些验证。有关如何执行此操作的信息在以下链接下:Authentication
aaaand现在 service 搜索类。
@Configuration
public class BookElasticsearchService {
BookElasticsearchClient client = new BookElasticsearchClient();
private static final String BOOK_INDEX = "books";
public void createBooksIndexBulk(final List<Book> books)
{
BulkRequest.Builder builder = new BulkRequest.Builder();
for (Book book : books)
{
builder.operations(op -> op
.index(index -> index
.index(BOOK_INDEX)
.id(String.valueOf(book.getId()))
.document(book)
)
);
try {
client.getClient().bulk(builder.build());
} catch (ElasticsearchException exception) {
exception.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public List<Book> findBookByIsbn(String query)
{
List<Book> books = new ArrayList<>();
try {
SearchResponse<Book> search = client.getClient().search(s -> s
.index(BOOK_INDEX)
.query(q -> q
.match(t -> t
.field("isbn")
.query(query))),
Book.class);
List<Hit<Book>> hits = search.hits().hits();
for (Hit<Book> hit: hits) {
books.add(hit.source());
}
return books;
} catch (ElasticsearchException exception) {
exception.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
这是两个非常重要的功能。其中之一是创建索引并将数据放入文档中,另一个已用于从 isbn 。
获取有关书籍的信息。,最后但并非最不重要的一点是,我们需要一个存储库类来elasticsearch。
public interface BookElasticsearchRepository extends ElasticsearchRepository<Book, String> {}
最后,我们需要完成控制器以发送请求。
@RestController
@RequestMapping(value = "book")
public class BookController {
@Autowired
BookSearchService searchService;
@Autowired
BookRepository bookRepository;
@Autowired
BookElasticsearchService bookElasticsearchService;
@GetMapping("/database")
ResponseEntity<Iterable<Book>> getBooksFromDatabase(@RequestParam("query") String query)
{
Iterable<Book> booksFromDatabase = bookRepository.findByIsbn(query);
return ResponseEntity.ok(booksFromDatabase);
}
@GetMapping("/hibernate")
ResponseEntity<Iterable<Book>> getBooksFromHibernate(Pageable pageable, @RequestParam("query") String query)
{
Iterable<Book> booksFromHibernate = searchService.search(pageable, query);
return ResponseEntity.ok(booksFromHibernate);
}
@GetMapping("/elasticsearch")
ResponseEntity<Iterable<Book>> getBooksFromElasticsearch(@RequestParam("query") String query)
{
Iterable<Book> booksFromElasticSearch = bookElasticsearchService.findBookByIsbn(query);
return ResponseEntity.ok(booksFromElasticSearch);
}
}
启动应用程序之前的一件更重要的事情:
我们需要在Main ApplicationClass 中添加注释(见下文),以使其与一个应用程序中的多个搜索引擎技术一起使用。
@EnableElasticsearchRepositories(basePackageClasses = BookElasticsearchRepository.class)
@EnableJpaRepositories(excludeFilters = @ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE, value = BookElasticsearchRepository.class
))
@SpringBootApplication
public class SearchApplication {
public static void main(String[] args) {
SpringApplication.run(SearchApplication.class, args);
}
}
让我们一起测试所有事物。
我也必须在第一次创建一个索引。我从 bookelasticsearchservice 中称函数 createbooksindexbulk 。还有一个可以直接在Elasticsearch Console中调用索引的选项。在这里描述了如何做到这一点:Bulk Api
我发送夫妇请求(在我的数据库中,我有一个 isbn = 123 -test ):
http://localhost:8080/book/hibernate?query=123test
http://localhost:8080/book/database?query=123test
http://localhost:8080/book/elasticsearch?query=123test
对于每个请求,我都会得到正确的答复。任务完成。
非常感谢您的阅读。
我使用的其他资源:
您也可以检查我的github以获取整个代码
Yordaniss / compare-search-engine
存储库比较Java应用程序的搜索引擎
Compare search engine for JAVA
This repository was created to test implementation of popular search engines with directly MySQL loading for JAVA Spring application.
Sources for data loading:
- Hibernate Search
- Elasticsearch
- MySQL