大家好,我是老坛。
本篇文章全部代码资源请关注同名公众号:老坛聊开发
回复:"7.x模板" 即可获取
Elasticsearch是一个分布式的RESTful 风格的搜索和数据分析引擎,它使用方便,查询速度快,因此也被越来越多的开发人员使用。
在Java项目中,使用ES的场景也十分常见。除了作为某些特定资源的存储之外也可以作为像ELK这样的日志收集系统里的存储引擎。总之,对于非关系型而查找需求较多的场景,ES的表现还是非常不错的。
那今天老坛就带大家看一看如何使用Java API来操作ES。
当前文档只限于ES的版本在7.x及其以下的情况,如果是8.x的可以去看我的另一篇文档:
【ES使用】Java API操作ES宝典(8.x版本)
https://blog.csdn.net/qq_34263207/article/details/127847033
目录
在真正使用es api之前,还有一些准备工作要去做,分别是引入依赖和写好配置文件
对于7.x及其以下的版本,spring data这边是支持的,所以可以根据自己ES的具体版本来引入匹配版本的spring data starter,具体如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
关于如何去查看spring data与ES的版本匹配关系,大家可以去看我的这篇文章,这里便不再赘述了。
【ES知识】es版本与java api版本对照一览
https://blog.csdn.net/qq_34263207/article/details/127790216
接下来就是去写配置文件了。我们要连接ES所要用到的ip和port,用户名和密码这些信息建议大家都写在自己的yml里,方便维护:
es:
address: 127.0.0.1
port: 9200
scheme: http
username: admin
password: admin
接下来我们要写一个config文件来使用这些配置,具体代码如下:
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ElasticSearchConfig {
@Value("${es.address}")
String address;
@Value("${es.port}")
Integer port;
@Value("${es.scheme}")
String scheme;
@Value("${es.username}")
String username;
@Value("${es.password}")
String password;
@Bean
public RestHighLevelClient esRestClient(){
RestClientBuilder builder = null;
builder = RestClient.builder(new HttpHost(address, port, scheme));
RestHighLevelClient client = new RestHighLevelClient(builder);
return client;
}
}
这个是无用户名和密码版本的配置文件,大家可以直接拿走使用。
下面贴一下需要配置用户名和密码的config文件:
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ElasticSearchConfig {
@Value("${es.address}")
String address;
@Value("${es.port}")
Integer port;
@Value("${es.scheme}")
String scheme;
@Value("${es.username}")
String username;
@Value("${es.password}")
String password;
@Bean
public RestHighLevelClient esRestClientWithCred(){
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
RestClientBuilder restClientBuilder = RestClient.builder(new HttpHost(address, port, scheme))
.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
@Override
public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpAsyncClientBuilder) {
return httpAsyncClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
}
});
RestHighLevelClient esClient = new RestHighLevelClient(restClientBuilder);
return esClient;
}
}
按需来取即可。
也就是最基本的CRUD了,但是这部分介绍的都是只针对一条数据的CRUD操作,都是通过id来去进行的,比较简单。而操作这些所使用到的类也比较有规律,这里我给大家画一张图方便理解:

RestHighLevelClient是我们所有操作的核心,它也是在我们config里面写好的,等着被注入就可以了。对于每一种操作都有其相应的request和response,我们在request里面填充需要做的操作,用response接收结果。下面举例来介绍这些操作。
更多优质文章资源请关注同名公众号:老坛聊开发
先介绍一下我用到的实体对象:
@Data
public class TextBook {
String bookName;
String author;
Integer num;
}
对应的索引数据为:
{
"_index": "textbook",
"_id": "kIwXeYQB8iTYJNkI986Y",
"_source": {
"bookName": "This is a test doc",
"author": "老坛",
"num": 20
}
}
@SpringBootTest
@RunWith(SpringRunner.class)
@Slf4j
public class ESTest {
@Resource
RestHighLevelClient restHighLevelClient;
String index = "index";
@Test
public void insertSingle(String id) throws IOException {
IndexRequest request = new IndexRequest(index);
request.id(id);
// 要插入的实体
TextBook textBook = new TextBook();
textBook.setBookName("老坛聊开发");
textBook.setAuthor("老坛");
textBook.setNum(20);
request.source(JSON.toJSONString(textBook), XContentType.JSON);
IndexResponse indexResponse = restHighLevelClient.index(request, RequestOptions.DEFAULT);
log.info("返回状态:" + indexResponse.status().getStatus());
}
}
我们需要将RestHighLevelClient注入进来,然后就可以操作了。
@SpringBootTest
@RunWith(SpringRunner.class)
@Slf4j
public class ESTest {
@Resource
RestHighLevelClient restHighLevelClient;
String index = "index";
@Test
public void grepSingle(String id) throws IOException {
GetRequest getRequest = new GetRequest(index);
getRequest.id(id);
GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
String json = getResponse.getSourceAsString();
TextBook textBook = JSONObject.parseObject(json, TextBook.class);
log.info("返回结果为:" + JSON.toJSONString(textBook));
}
}
这里是使用id查询单条数据,相当于sql的getByPrimaryKey。
@SpringBootTest
@RunWith(SpringRunner.class)
@Slf4j
public class ESTest {
@Resource
RestHighLevelClient restHighLevelClient;
String index = "index";
@Test
public void updateSingle(String id) throws IOException {
GetRequest getRequest = new GetRequest(index);
getRequest.id(id);
// 先查看是否包含该数据,以防报错
boolean exists = restHighLevelClient.exists(getRequest, RequestOptions.DEFAULT);
if(exists) {
UpdateRequest updateRequest = new UpdateRequest(index, id);
TextBook textBook = new TextBook();
// 要更新的实体
textBook.setBookName("老坛聊开发");
textBook.setAuthor("老坛");
updateRequest.doc(JSON.toJSONString(textBook), XContentType.JSON);
UpdateResponse updateResponse = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
log.info("返回状态:" + updateResponse.status().getStatus());
}
}
}
这里先用exist方法查询了一下该条数据是否存在,存在的话再去修改数据。
@SpringBootTest
@RunWith(SpringRunner.class)
@Slf4j
public class ESTest {
@Resource
RestHighLevelClient restHighLevelClient;
String index = "index";
@Test
public void deleteSingle(String id) throws IOException {
DeleteRequest deleteRequest = new DeleteRequest(index);
deleteRequest.id(id);
DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
log.info("返回状态:" + deleteResponse.status().getStatus());
}
}
下面介绍复杂查询,也是我们比较常用的ES查询方式,这里如果大家有一定的ES语法基础会更方便的理解下面的内容,想了解ES基础语法可以去看我的这篇文章:
【ES知识】ES基础查询语法一览
https://blog.csdn.net/qq_34263207/article/details/127849806
老规矩,先上图:

该图描绘了我们在复杂查询时会涉及到的类:一样是使用RestHighLevelClient,然后通过SearchRequest进行操作,SearchRequest又需要SearchSourceBuilder,而SearchSourceBuilder需要通过QueryBuilder完成构建,图中最下面一行列出了几种常用的QueryBuilder,基本上和我们ES查询的语法是直接相关的。最后SearchResponse返回结果。
更多优质文章资源请关注同名公众号:老坛聊开发
下面给出查询的模板代码:
@SpringBootTest
@RunWith(SpringRunner.class)
@Slf4j
public class ESTest {
@Resource
RestHighLevelClient restHighLevelClient;
String index = "index";
@Test
public List<TextBook> grepTextBook() throws IOException {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("author", "老坛");
searchSourceBuilder.query(matchQueryBuilder);
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices(index);
searchRequest.source(searchSourceBuilder);
List<TextBook> textBookList = new ArrayList<>();
//执行操作
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
if (Objects.nonNull(searchResponse)) {
SearchHits searchHits = searchResponse.getHits();
for (int i = 0; i < searchHits.getHits().length; i++) {
String str = searchHits.getHits()[i].getSourceAsString();
System.out.println(str);
textBookList.add(JSON.parseObject(str, TextBook.class));
}
}
return textBookList;
}
}
整个查询代码的核心实际上只有这一行:
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("author", "老坛");
因为只有QueryBuilder决定了你要怎么去进行查询,其它的都是模板,可以不动。
下面我们来一一介绍几种常用的QueryBuilder:
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("author", "老坛");
对应了ES的match查询,它等价的ES语法就是:
GET textbook/_search
{
"query": {
"match": {
"author":"老坛"
}
}
}
MatchPhraseQueryBuilder matchPhraseQueryBuilder = QueryBuilders.matchPhraseQuery("bookName", "老坛");
对应了ES的match_phrase查询,它等价的ES语法就是:
GET textbook/_search
{
"query": {
"match_phrase": {
"bookName":"老坛"
}
}
}
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery("老坛", "author","bookName");
对应了ES的multi_match查询,它等价的ES语法就是:
GET textbook/_search
{
"query": {
"multi_match": {
"query": "老坛",
"fields": ["author","bookName"]
}
}
}
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("author", "老坛");
对应了ES的term查询,它等价的ES语法就是:
GET textbook/_search
{
"query": {
"term": {
"author": {
"value": "老坛"
}
}
}
}
TermsQueryBuilder termsQueryBuilder = QueryBuilders.termsQuery("author", "老","坛");
对应了ES的terms查询,它等价的ES语法就是:
GET textbook/_search
{
"query": {
"terms": {
"author": ["老","坛"]
}
}
}
FuzzyQueryBuilder fuzzyQueryBuilder = QueryBuilders.fuzzyQuery("bookName", "老坛");
对应了ES的fuzzy查询,它等价的ES语法就是:
GET textbook/_search
{
"query":{
"fuzzy":{
"bookName":"老坛"
}
}
}
我们只要根据自己需要选择不同的builder去查询就可以了
RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("num")
.gt(10)
.lt(20);
对应了ES的range查询,它等价的ES语法就是:
GET textbook/_search
{
"query":{
"range":{
"num":{
"lt":20,
"gt":10
}
}
}
}
更多优质文章资源请关注同名公众号:老坛聊开发
bool查询也是我们使用es中比较常见的,当我们需要查询多个条件时,就会需要:
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("bookName", "老坛");
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("author", "老坛");
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()
.must(matchQueryBuilder)
.should(termQueryBuilder);
所对应的ES语法为:
GET textbook/_search
{
"query":{
"bool":{
"must":{
"match":{
"bookName":"老坛"
}
},
"should":{
"term":{
"author":"老坛"
}
}
}
}
}
还用最开始的match举例,这里在此基础上添加了排序和分页两个限制:
@SpringBootTest
@RunWith(SpringRunner.class)
@Slf4j
public class ESTest {
@Resource
RestHighLevelClient restHighLevelClient;
String index = "index";
@Test
public List<TextBook> grepTextBook() throws IOException {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("bookName", "老坛");
searchSourceBuilder.query(matchQueryBuilder);
// 分页参数
searchSourceBuilder.sort("num", SortOrder.DESC);
searchSourceBuilder.from(1);
searchSourceBuilder.size(100);
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices(index);
searchRequest.source(searchSourceBuilder);
List<TextBook> textBookList = new ArrayList<>();
//执行操作
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
if (Objects.nonNull(searchResponse)) {
SearchHits searchHits = searchResponse.getHits();
for (int i = 0; i < searchHits.getHits().length; i++) {
String str = searchHits.getHits()[i].getSourceAsString();
System.out.println(str);
textBookList.add(JSON.parseObject(str, TextBook.class));
}
}
return textBookList;
}
}
排序和分页主要体现在这几行:
// 分页参数
searchSourceBuilder.sort("num", SortOrder.DESC);
searchSourceBuilder.from(1);
searchSourceBuilder.size(100);
可以看到这是一个按照num字段的降序搜索,并且是按照页容量为100进行分页,取第二页。
所对应的ES语法为:
GET textbook/_search
{
"query":{
"match":{
"bookName":"老坛"
}
},
"from":0,
"size":100,
"sort":{
"num":{
"order":"desc"
}
}
}
本篇文章中介绍的全部es操作老坛已整理成模板项目了:

请关注同名公众号:老坛聊开发
并回复:"7.x模板" 获取代码模板
只要大家根据自己的实际环境修改配置即可直接跑起来啦,亲测有效!

到这里Java对ES的操作基本上聊的差不多了,题主这里介绍的未必详尽,只是一些我们通常会用到的操作,如果还想详细了解更多的内容请阅读官方文档:
https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/7.17/java-client-javadoc.html
另外,题主这里只是为了给大家讲明白如何使用举了几个例子,并不一定效率最高或者使用常见最为恰当,还是需要大家学习一下ES的语法根据自己的实际业务场景去选用,谢谢大家~
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po