时髦的”大数据”一词来自 3 Vs:数量、种类和速度。卷是指数据的大小;多样性是指不同类型的数据;和速度是指数据处理的速度。为了处理持久性大数据,NoSQL 数据库的写入和读取速度比 SQL 数据库快。但是,由于海量数据固有的多样性数据,搜索引擎需要查找信息,而无需使用强大的计算机功能,并且不需要花太多时间。在这篇文章中,我们将讨论两个最流行的搜索引擎,弹性搜索和ApacheSolr,以及Platform.sh如何同时支持这两个。
基于ApacheLucene,两个搜索引擎都是开源的,用Java编写。它们都有美丽、丰富的文档:
为了让您了解全文搜索 (FTS) 的相关性和有用性,本文为音乐创建了两个直接的应用程序,它们同时使用 Spring MVC 和 CRUD 功能(在数据库中创建、读取、更新和删除)。唯一的区别在于数据库:一个使用弹性搜索,另一个使用ApacheSolr。音乐的歌词包含大量的单词序列,全文引擎包括定义索引的能力;与在 SQL 数据库中使用通配符使用 LIKE 相比,它将更快、更高效。
弹性搜索
如上所述,弹性搜索基于 Lucene 库。它提供了一个分布式、支持多租户的全文搜索引擎,带有 HTTP Web 界面和无架构的 JSON 文档。它在Java上运行。
在 Maven 应用程序中,我们可以轻松地定义应用程序所需的依赖项。在弹性搜索应用程序中,我们需要将 ES 库和Platform.sh配置读取器设置为 pom.xml。
<?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>
<groupId>sh.platform.start</groupId>
<artifactId>spring-mvc-maven-elasticsearch</artifactId>
<version>0.0.1</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
<elasticsearch.version>6.5.0</elasticsearch.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>${elasticsearch.version}</version>
</dependency>
<dependency>
<groupId>org
引导</组Id>
<工件Id>弹簧启动-开发工具</工件Id>
<可选>true</可选>
</依赖项>
<依赖性>
<groupId>org.springframework.boot</组Id>
<工件Id>弹簧启动-启动-测试</工件Id>
<范围>测试</范围>
</依赖项>
<依赖性>
<groupId>sh.平台</组Id>
<工件Id>配置</工件Id>
<版本>2.2.0</版本>
</依赖项>
</依赖项>
<build>
<最终名称>弹簧-mvc-maven-弹性搜索</最终名称>
<插件>
<插件>
<groupId>org.springframework.boot</组Id>
<工件Id>弹簧启动-母插件</工件Id>
</插件>
</插件>
</build>
</项目>
构建应用程序的第一步是音乐实体 – 由连续性线程及其标识定义的对象,而不是由其属性定义的对象。
public class Music {
private String id;
private String name;
private String singer;
private int year;
private String lyrics;
//getter and setter
}
将核心 Spring 概念应用于解决方案开发的 Spring 数据 ES 使用弹性搜索搜索引擎。但是,它使用已弃用为 Java 高级 REST 客户端的传输客户端,并将在 ElasticSearch 8.0 上删除。因此,此应用程序使用来自弹性搜索的 Java 高级 REST 客户端。使用 Bean 注释,我们将提供将在服务层上使用的 RestHighLevelClient 实例。此配置类将创建由Platform.sh提供的 RestHighLevel 客户端。
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import sh.platform.config.Config;
import sh.platform.config.Elasticsearch;
import java.io.IOException;
@Configuration
public class ElasticsearchConfig {
static final String INDEX = "musics";
static final String TYPE = "music";
@Bean
public RestHighLevelClient elasticsearchTemplate() throws IOException {
Config config = new Config();
final Elasticsearch credential = config.getCredential("elasticsearch", Elasticsearch::new);
final RestHighLevelClient client = credential.get();
CreateIndexRequest request = new CreateIndexRequest(INDEX);
GetIndexRequest exist = new GetIndexRequest();
exist.indices(INDEX);
if (!client.indices().exists(exist, RequestOptions.DEFAULT)) {
client.indices().create(request, RequestOptions.DEFAULT);
}
return client;
}
}
服务层位于 entities
类的顶部,用于处理业务需求,包括具有 CRUD 操作的数据库控制以及搜索音乐中的名称、歌词和歌手字段中的字词更快xml.jackson.databind.ObjectMapper;
导入 org.弹性搜索.action.delete.删除请求;
导入 org.弹性搜索.action.get.GetRequest;
导入 org.弹性搜索.action.get.Get 响应;
导入 org.弹性搜索.action.index.索引请求;
导入 org.弹性搜索.action.search.Search 请求;
导入 org.弹性搜索.action.search.搜索响应;
导入 org.弹性搜索.客户端.请求选项;
导入 org.弹性搜索.client.RestLevel 客户端;
导入 org.弹性搜索.index.query.BoolQueryBuilder;
导入 org.弹性搜索.search.SearchHit;
导入 org.弹性搜索.search.builder.搜索来源构建器;
导入 org.springframework.be.factory.注释.自动连线;
导入组织.springframework.构造型.存储库;
进口组织.蒂梅利夫.util.StringUtils;
导入 java.io.IO 异常;
导入 java.util.list;
导入 java.util.Map;
导入 java.util.可选;
导入 java.util.UUID;
导入 java.util.stream.收发器;
导入静态 java.util.stream.stream.stream.stream.stream.stream.stream;导入静态 java.util.stream.stream.stream
导入静态 org.弹性搜索.index.query.QueryBuilders.boolQuery;
导入静态 org.弹性搜索.index.query.QueryBuilders.术语查询;
导入静态 sh.platform.模板.弹性搜索Config.INDEX;
导入静态 sh.platform.模板.弹性搜索Config.TYPE;
@Repository
公共类音乐服务 |
@Autowired
私有对象映射器对象映射器;
@Autowired
私人 RestLevel 客户端;
公共列表\lt;音乐>查找所有(字符串搜索)抛出 IO 异常 |
搜索来源构建器生成器 = 新的搜索源构建器();
如果(!斯特林Utils.isempty(搜索)) |
最终 BoolQueryBuilder 查询构建器 = boolQuery(应)应(术语查询(“歌词”,搜索))
.应(术语查询(“名称”,搜索))
.应(术语查询(“歌手”,搜索);
源构建器.查询(查询构建器);
}
搜索请求搜索请求 = 新的搜索请求();
搜索请求.索引(INDEX);
搜索请求.源(源构建器);
搜索响应 = 客户端.搜索(搜索请求、请求选项.DEFAULT);
返回流(响应.getHits()。 拆分器(),错误)
.map(搜索命中:获取来源AsMap)
.map(s > 对象映射值(s,音乐类))
.collect(收集者.tolist());
}
公共列表\lt;音乐>findAll()抛出IO异常 |
返回查找 All(空);
}
公共可选_lt;音乐>查找ById(字符串ID)抛出IO异常 |
获取请求请求 = 新的获取请求(INDEX、TYPE、ID);
最终 Get 响应响应 = 客户端.get(请求、请求选项.DEFAULT);
最终映射\lt;String,对象>源 = 响应.getSource();
如果 (源.是空)) |
返回可选.empty();
* 其他 *
返回可选的可转换(对象映射器.转换值(源,音乐.类);”
}
}
公共空白保存(音乐)抛出 IO 异常 |
如果 (StringUtils.是空 (音乐.getId()) ]
音乐.setID(UUID.随机UUID()toString());
}
映射<String,对象>jsonMap = 对象Mapper.转换值(音乐,Map.class);
索引请求索引请求 = 新索引请求(INDEX、TYPE)
.id(音乐.getId))))源(jsonMap);
客户端.索引(索引请求、请求选项.DEFAULT);
}
公共无效删除(音乐)抛出 IO 异常 |
客户端.删除(新的删除请求(INDEX、TYPE、音乐.getId())、请求选项.DEFAULT);
}
}
在 Spring 构建网站的方法中,HTTP 请求由控制器处理。您可以通过注释快速识别这些 @Controller
请求。在下面的示例中, MusicController
处理 HTTP 请求。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org
web.bind.注释.路径变量;
导入 org.springframework.web.bind.注释.PostMap;
导入 org.springframework.web.bind.注释.请求Param;
导入 javax.验证.valid;
导入 java.io.IO 异常;
导入静态 java.util.stream.collectors.toList;
@Controller
公共类音乐控制器 |
@Autowired
私人音乐服务音乐服务;
@GetMapping(“/”)
公共字符串开始(@RequestParam(名称 = “搜索”,必需 = false) 字符串搜索,模型模型) 引发 IOException |
模型.add属性(“音乐”,音乐服务.findAll(搜索));
模型.add属性(“搜索”,搜索);
返回”索引”;
}
@GetMapping(“/添加”)
公共字符串添加用户(模型模型) |
模型.add属性(“音乐”,新音乐());
返回”附加音乐”;
}
@PostMapping(“/添加”)
公共字符串添加用户(@Valid音乐,绑定结果结果,模型模型) 引发 IO 异常 |
如果(结果.有错误)) |
返回”附加音乐”;
}
音乐服务.保存(音乐);
模型.add属性(“音乐”,音乐服务.findAll());
返回”索引”;
}
@GetMapping(“/编辑/[id])”
公共字符串显示UpdateForm(@PathVariable(“id”)字符串ID,模型模型)引发IO异常 |
音乐与音乐服务.findById(id).orElseThrow(() ->新的非法参数异常(“无效音乐 ID:”= id));
模型.add属性(“音乐”,音乐);
返回”附加音乐”;
}
@PostMapping(“/更新/[id])
公共字符串更新用户(@PathVariable(“id”)字符串 ID,@Valid音乐,绑定结果结果,模型模型) 引发 IO异常 |
如果(结果.有错误)) |
音乐.setId(id);
返回”附加音乐”;
}
音乐服务.保存(音乐);
模型.add属性(“音乐”,音乐服务.findAll());
返回”索引”;
}
@GetMapping(“/删除/[id]”
公共字符串删除用户(@PathVariable(“id”)字符串 ID,模型模型) 引发 IO 异常 |
音乐与音乐服务.findById(id).orElseThrow(() ->新的非法参数异常(“无效音乐 ID:”= id));
音乐服务.删除(音乐);
模型.add属性(“音乐”,音乐服务.findAll()
.stream()
.筛选器(m -> .m.getId() 等于(id))
.收集(到列表));
返回”索引”;
}
}
只需一个简单的步骤即可Platform.sh上配置弹性搜索。只需将此值追加到服务文件:
elasticsearch:
type: elasticsearch:6.5
disk: 256
size: S
要获取有关Platform.sh的配置文件的更多详细信息,请查看此帖子,其中介绍如何创建或移动到Platform.sh。
阿帕奇·索尔
Solr 是一个开源的企业搜索平台,以 Java 编写,是 Apache Lucene 项目的一部分。其主要功能包括全文搜索、命中突出显示、分面搜索、实时索引、动态群集、数据库集成、NoSQL 功能和昂贵的文档处理。
与弹性搜索应用程序一样,Apache Solr 示例代码需要定义一个具有其依赖项的”pom.xml”。
<?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>
<groupId>sh
开始</组Id>
<工件Id>弹簧-mvc-maven-solr</工件Id>
<版本>0.0.1</版本>
<父>
<groupId>org.springframework.boot</组Id>
<工件Id>弹簧启动-启动-父-lt;/工件Id>
<版本>2.1.5.RELEASE</版本>
</父>
<属性>
<java.版本>1.8</java.版本>
<弹性搜索.版本>6.5.0</弹性搜索.版本>
</属性>
<依赖关系>
<依赖性>
<groupId>org.springframework.boot</组Id>
<工件Id>弹簧启动-启动-网络</工件Id>
</依赖项>
<依赖性>
<groupId>org.springframework.boot</组Id>
<工件Id>弹簧启动-启动器-百利叶</工件Id>
</依赖项>
<依赖性>
<groupId>org.springframework.data</组Id>
<工件Id>弹簧数据-索lr</工件Id>
<版本>4.0.8.RELEASE</版本>
</依赖项>
<依赖性>
<groupId>org.springframework.boot</组Id>
<工件Id>弹簧启动-开发工具</工件Id>
<可选>true</可选>
</依赖项>
<依赖性>
<groupId>org.springframework.boot</组Id>
<工件Id>弹簧启动-启动-测试</工件Id>
<范围>测试</范围>
</依赖项>
<依赖性>
<groupId>sh.平台</组Id>
<工件Id>配置</工件Id>
<版本>2.2.0</版本>
</依赖项>
</依赖项>
<build>
<最终名称>弹簧-mvc-maven-solr</最终名称>
<插件>
<插件>
<groupId>org.springframework.boot</组Id>
<工件Id>弹簧启动-母插件</工件Id>
</插件>
</插件>
</build>
</项目>
得益于支持 Apache Solr 的 Spring 数据,我们可以轻松地从 Spring 应用程序配置和访问 Apache Solr 搜索服务器。Music
实体类具有注释来映射字段并执行 Java 和 Apache Solr 之间的转换过程。
import org.springframework.data.annotation.Id;
import org.springframework.data.solr.core.mapping.Indexed;
import org.springframework.data.solr.core.mapping.SolrDocument;
@SolrDocument(collection = "collection1")
public class Music {
@Id
@Indexed(name = "id", type = "string")
private String id;
@Indexed(name = "name", type = "string")
private String name;
@Indexed(name = "singer", type = "string")
private String singer;
@Indexed(name = "year", type = "string")
private int year;
@Indexed(name = "lyrics", type = "string")
private String lyrics;
//getter and setter
}
要使用 Spring Data 功能, SolrTemplate
需要 实例;这就是为什么我们具有使 Spring SolrConfig
SolrTemplate
符合条件的类的原因。然后,它返回由Platform.sh生成的 Apache Solr 客户端实例。
import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.solr.core.SolrTemplate;
import sh.platform.config.Config;
import sh.platform.config.Solr;
@Configuration
public class SolrConfig {
@Bean
public HttpSolrClient elasticsearchTemplate() {
Config config = new Config();
final Solr credential = config
get();
字符串 URL = httpSolrClient.getBaseURL();
httpSolrClient.setBaseURL(url.substring(0,url.lastIndexOf(‘/’));
返回 httpSolr 客户端;
}
@Bean
公共 SolrTemplate solTemplate(httpSolr客户端) |
返回新的 SolrTemplate(客户端);
}
}
Spring Data 的一个神奇优势是,我们有一个存储库接口,它是与任何数据库通信的中央接口。此存储库界面允许通过 Spring Data 将查询方法功能直接交给开发人员。
import org.springframework.data.solr.repository.Query;
import org.springframework.data.solr.repository.SolrCrudRepository;
import java.util.List;
public interface MusicRepository extends SolrCrudRepository<Music, String> {
@Query("lyrics:*?0* OR name:*?0* OR singer:*?0*")
List<Music> search(String searchTerm);
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import org.thymeleaf.util.StringUtils;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import static java.util.stream.Collectors.toList;
import static java.util.stream.StreamSupport.stream;
@Repository
public class MusicService {
@Autowired
private MusicRepository repository;
public List<Music> findAll(String search) {
if (repository.count() == 0) {
return Collections.emptyList();
}
if (StringUtils.isEmpty(search)) {
return stream(repository.findAll().spliterator(), false)
.collect(toList());
}
return repository.search(search);
}
public List<Music> findAll() {
return findAll(null);
}
public Optional<Music> findById(String id) {
return repository.findById(id);
}
@Transactional
public void save(Music music) {
if (StringUtils.isEmpty(music.getId())) {
music.setId(UUID.randomUUID().toString());
}
repository.save(music);
}
@Transactional
public void delete(Music music) {
repository.delete(music);
}
}
创建由Platform.sh提供的 Apache Solr 实例只需一个简单的步骤即可完成。只需将此值追加到服务文件:
solr:
type: solr:7.7
disk: 1024
size: S
configuration:
cores:
collection1:
conf_dir: !archive "core1-conf"
endpoints:
solr:
core: collection1
两个应用程序的控制器都相同,因此我们不需要复制源代码。
此外,由于 Thymeleaf 模板引擎,两者都使用相同的前端文件,如 HTML、CSS 和 JavaScript。它提供了一组 Spring 集成,使您能够在 Spring MVC 应用中将其用作 JSP 的全功能替代品。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<html>
<head>
<title>Music Store</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/css/bootstrap.min.css">
</head>
<body>
<div class="container">
<h1>Music Store</h1>
<form class="form-search" method="get" action="/">
<i class="icon-music"></i>
<input type="text" class="input-medium search-query" name="search" >
<button type="submit" class="btn">Search</button>
<a href="/add" role="button" class="btn" data-toggle="modal">Add music</a>
<table class="table table-bordered">
<thead>
<tr>
<th>Music</th>
<th>Year</th>
<th>Singer</th>
<th>Edit</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
<tr th:each="music : ${musics}">
<td th:text="${music
年份\”></td>
<td th:text=”${music.singer}”></td>
<td><lt;lt;lt;lt;lt;<lt;td><<<<<<<<<<<<<<<<<<</a<<<<lt;<lt;<<<<lt;/a_lt;<<lt;<</t;<<<_t/lt;/a<<<<<<gt;<gt;<<_lt;/a_lt;_lt;\lt;/a<lt;</a1>第三;\{3;lt;\\lt;</a_
<td><lt;lt;lt;lt;lt;lt;lt;lt;td-gt;<<<<<<<<<<<<<<<<<<lt;<<<<lt;/a_lt;<lt;<<<lt;/a_lt;<<lt;<</t;<<<_t/lt;/a<<<<<<gt;<gt;<<_lt;_lt;_lt;\lt;/a<gt;lt;\/但&3;</a_lt;\\\\\但</
</tr>
</tbody>
</tbody>
</表>
</form>
</div>
<脚本 src=”https://code.jquery.com/jquery.js”></script>
<脚本 src=”/js/boottrap.min.js”></脚本>
</body>
</html>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<html>
<head>
<title>Music Store</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/css/bootstrap.min.css">
</head>
<body>
<div class="container">
<h1>Music Store</h1>
<form th:action="@{/add}" th:object="${music}" method="post">
<input id="id" name="id" type="hidden" th:field="*{id}">
<div class="form-group">
<label for="musicNameId">Name</label>
<input type="text" class="form-control" th:field="*{name}" id="musicNameId" placeholder="Enter Music">
</div>
<div class="form-group">
<label for="musicYearId">Year</label>
<input type="number" class="form-control" th:field="*{year}" id="musicYearId" placeholder="Enter Year"
min="1000" max="2020">
</div>
<div class="form-group">
<label for="musicSingerId">Singer</label>
<input type="text" class="form-control" th:field="*{singer}" id="musicSingerId" placeholder="Enter Singer">
</div>
<div class="form-group">
<label for="musicLyricsId">Lyrics</label>
<textarea class="form-control" id="musicLyricsId" rows="3" th:field="*{lyrics}"></textarea>
</div>
<button type="submit" class="btn">Save</button>
</form>
</div>
<script src="https://code.jquery.com/jquery.js"></script>
<script src="/js/bootstrap.min.js"></script>
</body>
</html>
我希望这篇文章有助于解释全文搜索引擎(而不是类SQL数据库)的好处,有两个梦幻般的示例应用程序,比较最流行的FTS之间的代码:弹性搜索和ApacheSolr。两者都是开源的,有丰富的文档,并且都支持Platform.sh!
Platform.sh使您能够非常轻松地利用弹性搜索或 Apache Solr 来满足您的业务需求。
我们还有一个包含Java代码示例的存储库,除了Java模板。敬请关注:如果您是雅加达 EE 爱好者,请留意我们即将发布的帖子!