Elasticsearch--JAVA API索引的创建、增删改查

https://yq.aliyun.com/articles/368786

JAVA_API 增删改查

 public class es { 
    private Logger logger = LoggerFactory.getLogger(es.class);
    public final static String HOST = "192.168.1.140";
    public final static int PORT = 9300; //http请求的端口是9200,客户端是9300
    private TransportClient client = null;

    String index = "zhouls";//设置索引库
    String type = "emp";//设置类型
    //索引index(四种格式:json,map,bean,es helper)

    //    @Before
    public void getConnect() throws Exception {
        client = new PreBuiltTransportClient(Settings.EMPTY).addTransportAddresses(new InetSocketTransportAddress(InetAddress.getByName(HOST), PORT));
        logger.info("连接信息:" + client.toString());
    }

    /**
     * Index API
     * 3、Elasticsearch索引库的创建
     */

    @Test
    public void addIndex0() throws IOException {
/** 在一个main方法中,我们首先需要用TransportClient的子类PreBuiltTransportClient去连接我们的elasticsearch,注意java连接es的端口号是9300。
   文本添加的jsonBuilder是以对象的开始(startObject)和结束(endObject)为标志,在其中以属性field的方式添加。其中添加的需要有name和value。
 内容填写完毕后,需要增加到elasticsearch中,使用索引IndexResponse类型的prepareIndex方法,参数为地址索引和类型索引,将文件添加是setSource(对象).get()。
   这样就将文件添加到了elasticsearch中,并可以查询了。*/
        XContentBuilder builder = jsonBuilder()
                .startObject()
                .field("first_name", "gou gou")
                .field("last_name", "smith")
                .field("about", "trying out Elasticsearch")
                .field("interests", "music,act")
                .endObject();
        IndexResponse response = client.prepareIndex("megacorp", "employee").setSource(builder).get();
        System.out.println(response);
        client.close();
    }

    /**
     * 创建索引库
     * 需求:创建一个索引库为:msg消息队列,类型为:tweet,id为1
     * 索引库的名称必须为小写
     */
    @Test
    public void addIndex1() throws IOException {
        IndexResponse response = client.prepareIndex("msg", "tweet", "1")
                .setSource(XContentFactory.jsonBuilder()
                        .startObject().field("userName", "张三")
                        .field("sendDate", new Date())
                        .field("msg", "你好李四")
                        .endObject()).get();
        logger.info("索引名称:" + response.getIndex() + "\n类型:" + response.getType() + "\n文档ID:" + response.getId());
        client.close();
    }

    /**
     * 4、向索引库中添加json字符串
     */
    @Test
    public void addIndexJson() {
        String jsonStr = "{" +
                "\"userName\":\"张三\"," +
                "\"sendDate\":\"2017-11-30\"," +
                "\"msg\":\"你好李四\"" +
                "}";
        IndexResponse response = client.prepareIndex("weixin", "tweet").setSource(jsonStr, XContentType.JSON).get();
        logger.info("json索引名称:" + response.getIndex() + "\njson类型:" + response.getType() + "\njson文档ID:" + response.getId() + "\n当前实例json状态:" + response.getType());
    }

    /**
     * 5、向索引库添加一个Map集合
     */
    @Test
    public void addIndexMap() {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("userName", "张三");
        map.put("sendDate", new Date());
        map.put("msg", "你好李四");
        IndexResponse response = client.prepareIndex("momo", "tweet").setSource(map).get();
        logger.info("map索引名称:" + response.getIndex() + "\n map类型:" + response.getType() + "\n map文档ID:" + response.getId());
    }

    /**
     * 6、向索引库添加JsonObject
     * 传递json对象
     */
    @Test
    public void addIndexJsonObject() {
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("userName", "张三");
        jsonObject.addProperty("sendDate", "2017-11-23");
        jsonObject.addProperty("msg", "你好李四");
        IndexResponse response = client.prepareIndex("qq", "tweet").setSource(jsonObject, XContentType.JSON).get();
        logger.info("jsonObject索引名称:" + response.getIndex() + "\n jsonObject类型:" + response.getType() + "\n jsonObject文档ID:" + response.getId());
    }

    @Test
    public void test6() throws Exception {//传入一个对象到index索引库,这里是Person对象
         Person person = new Person();
         person.setName("mack");
         person.setAge(20);
 
         //如果直接传入一个person对象会报错,java.lang.IllegalArgumentException,必须把对象转换成一个Json字符串,使用jackson依赖
         //IndexResponse indexResponse = transportClient.prepareIndex(index, type, "9").setSource(person).get();
         ObjectMapper objectMapper = new ObjectMapper();
         String writeValueAsString = objectMapper.writeValueAsString(person);
         IndexResponse indexResponse = transportClient.prepareIndex(index, type, "3")
                 .setSource(writeValueAsString)
                 .get();
         //        IndexResponse indexResponse = transportClient.prepareIndex(index, type, "3").setSource(objectMapper.writeValueAsString(person)).get();
        System.out.println(indexResponse.getVersion());
    }

    /**
     * 7、从索引库获取数据
     */
    @Test
    public void getData1() {
        GetResponse getResponse = client.prepareGet("msg", "tweet", "1").get(); //查询id为1的数据
        logger.info("索引库的数据:" + getResponse.getSourceAsString());
    }

    /**
     * 全文查询
     */
    @Test
    public void getHits() {
        SearchResponse response = client.prepareSearch("megacorp").setTypes("employee").get();
        System.out.print(response);
        SearchHit[] hits = response.getHits().getHits();
        for (SearchHit sh : hits) {
            System.out.println(sh.getSourceAsString());
//            Map<String, Object> data = sh.getSourceAsMap();
//            String full_name = data.get("first_name") + "." + data.get("last_name");
//            String age = data.get("age").toString();
//            String about = data.get("about").toString();
//
//            List<String> interests = (List<String>) data.get("interests");
//            System.out.println(full_name + "\t" + age + "\t" + about);
//            for (String like : interests) {
//                System.out.println(like);
//            }
        }
        client.close();
    }

    /**
     * 模糊查询
     */
    @Test
    public void termsQuery() {
        SearchResponse response = client.prepareSearch("megacorp").setTypes("employee").setQuery(
                QueryBuilders.termsQuery("last_name", "smith")
                //单条信息的查询,不适合在聚合情况下的查询,需要知道vlaue包含的值
        ).get();
        System.out.println(response);
    }

    /**
     * 聚合查询
     */
    @Test
    public void Aggregation() {
        SearchResponse response = client.prepareSearch("megacorp").setTypes("employee").addAggregation(
                AggregationBuilders.terms("all_interests").field("interests").subAggregation(
                        AggregationBuilders.avg("avg_age").field("age")
                )
        ).execute().actionGet();
        System.out.println(response);
        client.close();
    }

    /**
     * 更新索引库数据
     */
    @Test
    public void updateData() {
        JsonObject jsonObject = new JsonObject();

        jsonObject.addProperty("userName", "王五");
        jsonObject.addProperty("sendDate", "2008-08-08");
        jsonObject.addProperty("msg", "你好,张三,好久不见");

        UpdateResponse updateResponse = client.prepareUpdate("msg", "tweet", "1").setDoc(jsonObject.toString(), XContentType.JSON).get();

        logger.info("updateResponse索引名称:" + updateResponse.getIndex() + "\n updateResponse类型:" + updateResponse.getType() + "\n updateResponse文档ID:" + updateResponse.getId());
    }

    /**
     * 根据索引名称,类别,文档ID 删除索引库的数据
     */
    @Test
    public void deleteData() {
        DeleteResponse deleteResponse = client.prepareDelete("msg", "tweet", "4").get(); //删除id为4的数据
        logger.info("deleteResponse索引名称:" + deleteResponse.getIndex() + "\n deleteResponse类型:" + deleteResponse.getType() + "\n deleteResponse文档ID:" + deleteResponse.getId());
    }

    /**
     * count 取总数  类似于sql中的 select count(1) from table;
     * 求总数
     * 类似于mysql中的select count(*)
     */
    @Test
    public void test11() throws Exception {
        long count = client.prepareCount(index)//查找索引库中的数据个数
                .setTypes(type).get().getCount();
        System.out.println(count);
    }

    /**
     * bulk 批量操作 适合初始化数据的时候使用,提高效率
     * 批量操作,执行多个命令
     */
    @Test
    public void bulk() throws Exception {
        BulkRequestBuilder prepareBulk = client.prepareBulk();

        //for循环执行----
        //index请求
        IndexRequest indexRequest = new IndexRequest(index, type, "10");
        indexRequest.source("{\"name\":\"zhangsan\",\"age\":17}");
        //delete请求
        DeleteRequest deleteRequest = new DeleteRequest(index, type, "1");
        //  bulkProcessor.add(/* Your requests */);
        prepareBulk.add(indexRequest);//bulkBuilder中可以添加多个操作,这里一个是建立索引的操作.
        prepareBulk.add(deleteRequest);//一个是删除的操作

        //执行 bulk
        BulkResponse bulkResponse = prepareBulk.get();
        if (bulkResponse.hasFailures()) {//批量操作中可能有的操作会出现问题,这个地方对操作失败的处理
            //有执行失败的
            BulkItemResponse[] items = bulkResponse.getItems();
            for (BulkItemResponse bulkItemResponse : items) {
                //获取失败信息,并打印
                System.out.println(bulkItemResponse.getFailureMessage());
            }
        } else {
            System.out.println("全部执行成功!");
        }
    }


}

 

Elasticsearch Java API 6.2(文档API)

文档API

本节描述以下CRUD API:

单文档的API

  • Index API
  • Get API
  • Delete API
  • Update API

多文档API

  • Multi Get API
  • Bulk API
  • Reindex API
  • Update By Query API
  • Delete By Query API
注意
所有 CRUD API都是单索引 API,索引参数接受单个索引名,或指向单个索引的别名

Index API

index API允许将类型化的JSON文档索引到特定的索引中,并使其可搜索。

生成JSON文档

生成JSON文档有几种不同的方法:

  • 手动的(也就是你自己)使用原生byte[]或作为String
  • 使用一个Map,该Map将自动转换为它的JSON等效项
  • 使用第三方库对bean(如Jackson)进行序列化
  • 使用内置的助手XContentFactory.jsonBuilder()

在内部,每个类型被转换为byte[](像String被转换为byte[]),因此,如果对象已经以这种形式存在,那么就使用它,jsonBuilder是高度优化的JSON生成器,它直接构造一个byte[]

自己动手

这里没有什么困难,但是请注意,您必须根据日期格式对日期进行编码。

String json = "{" +
        "\"user\":\"kimchy\"," +
        "\"postDate\":\"2013-01-30\"," +
        "\"message\":\"trying out Elasticsearch\"" +
    "}";

使用Map

Map是一个键:值对集合,它表示一个JSON结构:

Map<String, Object> json = new HashMap<String, Object>();
json.put("user","kimchy");
json.put("postDate",new Date());
json.put("message","trying out Elasticsearch");

bean序列化

可以使用Jacksonbean序列化为JSON,请将Jackson Databind添加到您的项目中,然后,您可以使用ObjectMapper来序列化您的bean:

import com.fasterxml.jackson.databind.*;

// instance a json mapper
ObjectMapper mapper = new ObjectMapper(); // create once, reuse

// generate json
byte[] json = mapper.writeValueAsBytes(yourbeaninstance);

使用Elasticsearch助手

Elasticsearch提供了内置的助手来生成JSON内容。

import static org.elasticsearch.common.xcontent.XContentFactory.*;

XContentBuilder builder = jsonBuilder()
    .startObject()
        .field("user", "kimchy")
        .field("postDate", new Date())
        .field("message", "trying out Elasticsearch")
    .endObject()

注意,您还可以使用startArray(String)endArray()方法添加数组,顺便说一下,field方法接受许多对象类型,您可以直接传递数字、日期甚至其他XContentBuilder对象。

如果需要查看生成的JSON内容,可以使用string()方法。

String json = builder.string();

索引文档

下面的示例将JSON文档索引为一个名为twitter的索引,其类型为tweetid值为1:

import static org.elasticsearch.common.xcontent.XContentFactory.*;

IndexResponse response = client.prepareIndex("twitter", "tweet", "1")
        .setSource(jsonBuilder()
                    .startObject()
                        .field("user", "kimchy")
                        .field("postDate", new Date())
                        .field("message", "trying out Elasticsearch")
                    .endObject()
                  )
        .get();

注意,您还可以将文档索引为JSON字符串,并且不需要提供ID:

String json = "{" +
        "\"user\":\"kimchy\"," +
        "\"postDate\":\"2013-01-30\"," +
        "\"message\":\"trying out Elasticsearch\"" +
    "}";

IndexResponse response = client.prepareIndex("twitter", "tweet")
        .setSource(json, XContentType.JSON)
        .get();

IndexResponse对象会给你一个响应:

// Index name
String _index = response.getIndex();
// Type name
String _type = response.getType();
// Document ID (generated or not)
String _id = response.getId();
// Version (if it's the first time you index this document, you will get: 1)
long _version = response.getVersion();
// status has stored current instance statement.
RestStatus status = response.status();

有关索引操作的更多信息,请查看REST索引文档

Get API

get API允许根据索引的id从索引中获取类型化的JSON文档,下面的示例从一个名为twitter的索引中获取JSON文档,该索引的类型名为tweetid值为1:

GetResponse response = client.prepareGet("twitter", "tweet", "1").get();

有关get操作的更多信息,请查看REST get文档。

Delete API

delete API允许基于id从特定索引中删除类型化的JSON文档,下面的示例从名为twitter的索引中删除JSON文档,该索引的类型名为tweetid值为1:

DeleteResponse response = client.prepareDelete("twitter", "tweet", "1").get();

Delete By Query API

通过查询删除的API可以根据查询结果删除给定的一组文档:

BulkByScrollResponse response = DeleteByQueryAction.INSTANCE.newRequestBuilder(client)
    .filter(QueryBuilders.matchQuery("gender", "male")) 
    .source("persons")                                  
    .get();                                             
long deleted = response.getDeleted();
  • QueryBuilders.matchQuery("gender", "male")(查询)
  • source("persons") (索引)
  • get()(执行操作)
  • response.getDeleted()(被删除的文档数)

由于这是一个长时间运行的操作,如果您希望异步执行,可以调用execute而不是get,并提供如下监听器:

DeleteByQueryAction.INSTANCE.newRequestBuilder(client)
    .filter(QueryBuilders.matchQuery("gender", "male"))     
    .source("persons")                                      
    .execute(new ActionListener<BulkByScrollResponse>() {   
        @Override
        public void onResponse(BulkByScrollResponse response) {
            long deleted = response.getDeleted();           
        }
        @Override
        public void onFailure(Exception e) {
            // Handle the exception
        }
    });

Update API

您可以创建一个UpdateRequest并将其发送给客户端:

UpdateRequest updateRequest = new UpdateRequest();
updateRequest.index("index");
updateRequest.type("type");
updateRequest.id("1");
updateRequest.doc(jsonBuilder()
        .startObject()
            .field("gender", "male")
        .endObject());
client.update(updateRequest).get();

也可以使用prepareUpdate()方法:

client.prepareUpdate("ttl", "doc", "1")
        .setScript(new Script("ctx._source.gender = \"male\""  , ScriptService.ScriptType.INLINE, null, null))
        .get();

client.prepareUpdate("ttl", "doc", "1")
        .setDoc(jsonBuilder()               
            .startObject()
                .field("gender", "male")
            .endObject())
        .get();
  • Script()(你的脚本,它也可以是本地存储的脚本名)
  • setDoc()(将合并到现有的文档)

注意,您不能同时提供脚本和doc

使用脚本更新

update API允许基于提供的脚本更新文档:

UpdateRequest updateRequest = new UpdateRequest("ttl", "doc", "1")
        .script(new Script("ctx._source.gender = \"male\""));
client.update(updateRequest).get();

通过合并文档更新

update API还支持传递一个部分文档合并到现有文档中(简单的递归合并,内部合并对象,取代核心的“键/值”和数组),例如:

UpdateRequest updateRequest = new UpdateRequest("index", "type", "1")
        .doc(jsonBuilder()
            .startObject()
                .field("gender", "male")
            .endObject());
client.update(updateRequest).get();

Upsert

也有对Upsert的支持,如果文档不存在,则使用upsert元素的内容索引新的doc:

IndexRequest indexRequest = new IndexRequest("index", "type", "1")
        .source(jsonBuilder()
            .startObject()
                .field("name", "Joe Smith")
                .field("gender", "male")
            .endObject());
UpdateRequest updateRequest = new UpdateRequest("index", "type", "1")
        .doc(jsonBuilder()
            .startObject()
                .field("gender", "male")
            .endObject())
        .upsert(indexRequest);              
client.update(updateRequest).get();

如果文档不存在,将添加indexRequest中的文档。

如果文件index/type/1已经存在,我们将在此操作后获得如下文件:

{
    "name"  : "Joe Dalton",
    "gender": "male"        
}
  • "gender": "male"(此字段由更新请求添加)

如果不存在,我们将有一份新文件:

{
    "name" : "Joe Smith",
    "gender": "male"
}

Multi Get API

multi get API允许根据文档的indextypeid获取文档列表:

MultiGetResponse multiGetItemResponses = client.prepareMultiGet()
    .add("twitter", "tweet", "1")           
    .add("twitter", "tweet", "2", "3", "4") 
    .add("another", "type", "foo")          
    .get();

for (MultiGetItemResponse itemResponse : multiGetItemResponses) { 
    GetResponse response = itemResponse.getResponse();
    if (response.isExists()) {                      
        String json = response.getSourceAsString(); 
    }
}
  • add("twitter", "tweet", "1")(通过单一id)
  • add("twitter", "tweet", "2", "3", "4")(或以相同index/type的id列表)
  • add("another", "type", "foo")(你也可以从另一个索引中得到)
  • MultiGetItemResponse itemResponse : multiGetItemResponses(迭代结果集)
  • response.isExists()(您可以检查文档是否存在)
  • response.getSourceAsString()(访问_source字段)

有关multi get操作的更多信息,请查看剩余的multi get文档

Bulk API

bulk API允许在一个请求中索引和删除多个文档,这里有一个示例用法:

import static org.elasticsearch.common.xcontent.XContentFactory.*;

BulkRequestBuilder bulkRequest = client.prepareBulk();

// either use client#prepare, or use Requests# to directly build index/delete requests
bulkRequest.add(client.prepareIndex("twitter", "tweet", "1")
        .setSource(jsonBuilder()
                    .startObject()
                        .field("user", "kimchy")
                        .field("postDate", new Date())
                        .field("message", "trying out Elasticsearch")
                    .endObject()
                  )
        );

bulkRequest.add(client.prepareIndex("twitter", "tweet", "2")
        .setSource(jsonBuilder()
                    .startObject()
                        .field("user", "kimchy")
                        .field("postDate", new Date())
                        .field("message", "another post")
                    .endObject()
                  )
        );

BulkResponse bulkResponse = bulkRequest.get();
if (bulkResponse.hasFailures()) {
    // process failures by iterating through each bulk response item
}

使用Bulk处理器

BulkProcessor类提供了一个简单的接口,可以根据请求的数量或大小自动刷新bulk操作,或者在给定的时间之后。

要使用它,首先创建一个BulkProcessor实例:

import org.elasticsearch.action.bulk.BackoffPolicy;
import org.elasticsearch.action.bulk.BulkProcessor;
import org.elasticsearch.common.unit.ByteSizeUnit;
import org.elasticsearch.common.unit.ByteSizeValue;
import org.elasticsearch.common.unit.TimeValue;

BulkProcessor bulkProcessor = BulkProcessor.builder(
        client,  
        new BulkProcessor.Listener() {
            @Override
            public void beforeBulk(long executionId,
                                   BulkRequest request) { ... } 

            @Override
            public void afterBulk(long executionId,
                                  BulkRequest request,
                                  BulkResponse response) { ... } 

            @Override
            public void afterBulk(long executionId,
                                  BulkRequest request,
                                  Throwable failure) { ... } 
        })
        .setBulkActions(10000) 
        .setBulkSize(new ByteSizeValue(5, ByteSizeUnit.MB)) 
        .setFlushInterval(TimeValue.timeValueSeconds(5)) 
        .setConcurrentRequests(1) 
        .setBackoffPolicy(
            BackoffPolicy.exponentialBackoff(TimeValue.timeValueMillis(100), 3)) 
        .build();
  • beforeBulk()

    • 此方法在执行bulk之前被调用,例如,您可以通过request.numberOfActions()查看numberOfActions
  • afterBulk(...BulkResponse response)

    • 此方法在执行bulk之后被调用,例如,您可以通过response.hasFailures()检查是否存在失败请求
  • afterBulk(...Throwable failure)

    • bulk失败并引发一个可抛出对象时,将调用此方法
  • setBulkActions(10000)

    • 我们希望每10,000个请求就执行一次bulk
  • setBulkSize(new ByteSizeValue(5, ByteSizeUnit.MB))

    • 我们希望每5MB就flush一次
  • setFlushInterval(TimeValue.timeValueSeconds(5))

    • 无论请求的数量是多少,我们都希望每5秒flush一次
  • setConcurrentRequests(1)

    • 设置并发请求的数量,值为0意味着只允许执行一个请求,在积累新的bulk请求时,允许执行一个值为1的并发请求
  • setBackoffPolicy()

    • 设置一个自定义的备份策略,该策略最初将等待100ms,以指数形式增加并重试三次,当一个或多个bulk项目请求以EsRejectedExecutionException失败时,将尝试重试,该异常表明用于处理请求的计算资源太少,要禁用backoff,请传递BackoffPolicy.noBackoff()

默认情况下,BulkProcessor:

  • bulkActions设置为1000
  • bulkSize设置为5mb
  • 不设置flushInterval
  • concurrentrequest设置为1,这意味着flush操作的异步执行
  • backoffPolicy设置为一个指数备份,8次重试,启动延时为50ms,总等待时间约为5.1秒

添加请求

然后您可以简单地将您的请求添加到BulkProcessor:

bulkProcessor.add(new IndexRequest("twitter", "tweet", "1").source(/* your doc here */));
bulkProcessor.add(new DeleteRequest("twitter", "tweet", "2"));

关闭Bulk Processor

当所有的文档都被加载到BulkProcessor,可以使用awaitCloseclose方法进行关闭:

bulkProcessor.awaitClose(10, TimeUnit.MINUTES);

bulkProcessor.close();

如果通过设置flushInterval来调度其他计划的flush,这两种方法都将flush所有剩余的文档,并禁用所有其他计划flush。如果并发请求被启用,那么awaitClose方法等待指定的超时以完成所有bulk请求,然后返回true,如果在所有bulk请求完成之前指定的等待时间已经过去,则返回falseclose方法不等待任何剩余的批量请求完成并立即退出。

在测试中使用Bulk Processor

如果您正在使用Elasticsearch运行测试,并且正在使用BulkProcessor来填充数据集,那么您最好将并发请求的数量设置为0,以便以同步方式执行批量的flush操作:

BulkProcessor bulkProcessor = BulkProcessor.builder(client, new BulkProcessor.Listener() { /* Listener methods */ })
        .setBulkActions(10000)
        .setConcurrentRequests(0)
        .build();

// Add your requests
bulkProcessor.add(/* Your requests */);

// Flush any remaining requests
bulkProcessor.flush();

// Or close the bulkProcessor if you don't need it anymore
bulkProcessor.close();

// Refresh your indices
client.admin().indices().prepareRefresh().get();

// Now you can start searching!
client.prepareSearch().get();

Update By Query API

updateByQuery最简单的用法是在不更改源的情况下更新索引中的每个文档,这种用法允许获取一个新属性或另一个在线映射更改。

UpdateByQueryRequestBuilder updateByQuery = UpdateByQueryAction.INSTANCE.newRequestBuilder(client);
updateByQuery.source("source_index").abortOnVersionConflict(false);
BulkByScrollResponse response = updateByQuery.get();

updateByQuery API的调用从获取索引快照开始,索引使用内部版本控制找到任何文档。

注意
当一个文档在快照的时间和索引请求过程之间发生变化时,会发生版本冲突。

当版本匹配时,updateByQuery更新文档并增加版本号。

所有更新和查询失败都会导致updateByQuery中止,这些故障可以从BulkByScrollResponse#getIndexingFailures方法中获得,任何成功的更新仍然存在,并且不会回滚,当第一次失败导致中止时,响应包含由失败的bulk请求生成的所有失败。

为了防止版本冲突导致updateByQuery中止,请设置abortOnVersionConflict(false),第一个示例之所以这样做,是因为它试图获取在线映射更改,而版本冲突意味着在相同时间开始updateByQuery和试图更新文档的冲突文档。这很好,因为该更新将获取在线映射更新。

UpdateByQueryRequestBuilder API支持过滤更新的文档,限制要更新的文档总数,并使用脚本更新文档:

UpdateByQueryRequestBuilder updateByQuery = UpdateByQueryAction.INSTANCE.newRequestBuilder(client);
updateByQuery.source("source_index")
    .filter(QueryBuilders.termQuery("level", "awesome"))
    .size(1000)
    .script(new Script(ScriptType.INLINE, "ctx._source.awesome = 'absolutely'", "painless", Collections.emptyMap()));
BulkByScrollResponse response = updateByQuery.get();

UpdateByQueryRequestBuilder还允许直接访问用于选择文档的查询,您可以使用此访问来更改默认的滚动大小,或者以其他方式修改对匹配文档的请求。

UpdateByQueryRequestBuilder updateByQuery = UpdateByQueryAction.INSTANCE.newRequestBuilder(client);
updateByQuery.source("source_index")
    .source().setSize(500);
BulkByScrollResponse response = updateByQuery.get();

您还可以将大小与排序相结合以限制文档的更新:

UpdateByQueryRequestBuilder updateByQuery = UpdateByQueryAction.INSTANCE.newRequestBuilder(client);
updateByQuery.source("source_index").size(100)
    .source().addSort("cat", SortOrder.DESC);
BulkByScrollResponse response = updateByQuery.get();

除了更改文档的_source字段外,还可以使用脚本更改操作,类似于Update API:

UpdateByQueryRequestBuilder updateByQuery = UpdateByQueryAction.INSTANCE.newRequestBuilder(client);
updateByQuery.source("source_index")
    .script(new Script(
        ScriptType.INLINE,
        "if (ctx._source.awesome == 'absolutely) {"
            + "  ctx.op='noop'"
            + "} else if (ctx._source.awesome == 'lame') {"
            + "  ctx.op='delete'"
            + "} else {"
            + "ctx._source.awesome = 'absolutely'}",
        "painless",
        Collections.emptyMap()));
BulkByScrollResponse response = updateByQuery.get();

Update API中,可以设置ctx.op的值来更改执行的操作:

  • noop

    • 如果您的脚本没有做任何更改,设置ctx.op = "noop"updateByQuery操作将从更新中省略该文档,这种行为增加了响应主体中的noop计数器。
  • delete

    • 如果您的脚本决定必须删除该文档,设置ctx.op = "delete",删除将在响应主体中已删除的计数器中报告。

ctx.op设置为任何其他值都会产生错误,在ctx中设置任何其他字段都会产生错误。

这个API不允许您移动它所接触的文档,只是修改它们的源,这是故意的!我们没有规定要把文件从原来的位置移走。

您也可以同时对多个索引和类型执行这些操作,类似于search API:

UpdateByQueryRequestBuilder updateByQuery = UpdateByQueryAction.INSTANCE.newRequestBuilder(client);
updateByQuery.source("foo", "bar").source().setTypes("a", "b");
BulkByScrollResponse response = updateByQuery.get();

如果提供路由值,则进程将路由值复制到滚动查询,将进程限制为与路由值匹配的碎片:

UpdateByQueryRequestBuilder updateByQuery = UpdateByQueryAction.INSTANCE.newRequestBuilder(client);
updateByQuery.source().setRouting("cat");
BulkByScrollResponse response = updateByQuery.get();

updateByQuery也可以通过指定这样的pipeline来使用ingest节点:

UpdateByQueryRequestBuilder updateByQuery = UpdateByQueryAction.INSTANCE.newRequestBuilder(client);
updateByQuery.setPipeline("hurray");
BulkByScrollResponse response = updateByQuery.get();

使用Task API

您可以使用Task API获取所有正在运行的update-by-query请求的状态:

ListTasksResponse tasksList = client.admin().cluster().prepareListTasks()
    .setActions(UpdateByQueryAction.NAME).setDetailed(true).get();
for (TaskInfo info: tasksList.getTasks()) {
    TaskId taskId = info.getTaskId();
    BulkByScrollTask.Status status = (BulkByScrollTask.Status) info.getStatus();
    // do stuff
}

使用上面所示的TaskId,您可以直接查找任务:

GetTaskResponse get = client.admin().cluster().prepareGetTask(taskId).get();

使用Cancel Task API

任何查询更新都可以使用Task Cancel API取消:

// Cancel all update-by-query requests
client.admin().cluster().prepareCancelTasks().setActions(UpdateByQueryAction.NAME).get().getTasks();
// Cancel a specific update-by-query request
client.admin().cluster().prepareCancelTasks().setTaskId(taskId).get().getTasks();

使用list tasks API查找taskId的值。

取消请求通常是一个非常快速的过程,但可能要花费几秒钟的时间,task status API继续列出任务,直到取消完成。

Rethrottling

在正在运行的更新中,使用_rethrottle API更改requests_per_second的值:

RethrottleAction.INSTANCE.newRequestBuilder(client)
    .setTaskId(taskId)
    .setRequestsPerSecond(2.0f)
    .get();

使用list tasks API查找taskId的值。

updateByQuery API一样,requests_per_second的值可以是任何正值的浮点值来设置节流的级别,或者Float.POSITIVE_INFINITY禁用节流。requests_per_second值加快进程立即生效,减慢查询的requests_per_second值在完成当前批处理后生效,以防止滚动超时。

Reindex API

详情见reindex API

BulkByScrollResponse response = ReindexAction.INSTANCE.newRequestBuilder(client)
    .destination("target_index")
    .filter(QueryBuilders.matchQuery("category", "xzy")) 
    .get();

还可以提供查询来筛选应该从源索引到目标索引的哪些文档

JestClient实现对ElasticSearch的操作

JestClient通过写json来实现对ElasticSearch的操作,
使用jestClient比较明显的一个优势就是,不用因为es的版本升级导致API发生改变而更改代码。

import com.andon.jestclientdemo.domain.BaseModel;
import com.google.gson.JsonObject;
import io.searchbox.client.JestClient;
import io.searchbox.client.JestResult;
import io.searchbox.core.*;
import io.searchbox.indices.CreateIndex;
import io.searchbox.indices.DeleteIndex;
import io.searchbox.indices.mapping.GetMapping;
import io.searchbox.indices.mapping.PutMapping;
import io.searchbox.indices.settings.GetSettings;
import io.searchbox.indices.settings.UpdateSettings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.List;

@Service
public class ESService {

    private static final Logger LOG = LoggerFactory.getLogger(ESService.class);

    @Resource
    JestClient jestClient;

    /**
     * 发送json查询
     */
    SearchResult jsonSearch(String json, String indexName, String typeName) {
        Search search = new Search.Builder(json).addIndex(indexName).addType(typeName).build();
        try {
            return jestClient.execute(search);
        } catch (Exception e) {
            LOG.warn("index:{}, type:{}, search again!! error = {}", indexName, typeName, e.getMessage());
            sleep(100);
            return jsonSearch(json, indexName, typeName);
        }
    }

    /**
     * 批量写入
     */
    public <T extends BaseModel> void bulkIndex(List<T> list, String indexName) {
        Bulk.Builder bulk = new Bulk.Builder();
        for (T o : list) {
            Index index = new Index.Builder(o).id(o.getPK()).index(indexName).type(o.getType()).build();
            bulk.addAction(index);
        }
        try {
            jestClient.execute(bulk.build());
        } catch (IOException e) {
            LOG.warn("bulkIndex again!! error={} index={}", e.getMessage(), indexName);
            sleep(100);
            bulkIndex(list, indexName);
        }
    }

    /**
     * 新增或者更新文档
     */
    public <T> void insertOrUpdateDocumentById(T o, String index, String type, String uniqueId) {
        Index.Builder builder = new Index.Builder(o);
        builder.id(uniqueId);
        builder.refresh(true);
        Index indexDoc = builder.index(index).type(type).build();
        try {
            jestClient.execute(indexDoc);
        } catch (IOException e) {
            LOG.warn("insertOrUpdateDocumentById again!! error={} id={}", e.getMessage(), uniqueId);
            sleep(100);
            insertOrUpdateDocumentById(o, index, type, uniqueId);
        }
    }

    /**
     * 根据主键id删除文档
     */
    public void deleteDocumentById(String index, String type, String id) {
        Delete delete = new Delete.Builder(id).index(index).type(type).build();
        try {
            jestClient.execute(delete);
        } catch (IOException e) {
            LOG.warn("deleteDocumentById again!! error={} id={}", e.getMessage(), id);
            sleep(100);
            deleteDocumentById(index, type, id);
        }
    }

    /**
     * 根据主键id获取文档
     */
    public <T> T getDocumentById(T object, String index, String id) {
        Get get = new Get.Builder(index, id).build();
        T o = null;
        try {
            JestResult result = jestClient.execute(get);
            o = (T) result.getSourceAsObject(object.getClass());
        } catch (IOException e) {
            LOG.warn("getDocumentById again!! error={} id=");
            sleep(100);
            getDocumentById(object, index, id);
        }
        return o;
    }

    private void sleep(long time) {
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            LOG.error("Thread sleep failure!! error={}", e.getMessage());
        }
    }

    /**
     * 创建index
     */
    public void createIndex(String index) {
        try {
            JestResult jestResult = jestClient.execute(new CreateIndex.Builder(index).build());
            System.out.println("createIndex:{}" + jestResult.isSucceeded());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 删除index
     */
    public void deleteIndex(String index) {
        try {
            JestResult jestResult = jestClient.execute(new DeleteIndex.Builder(index).build());
            System.out.println("deleteIndex result:{}" + jestResult.isSucceeded());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 设置index的mapping(设置数据类型和分词方式)
     */
    public void createIndexMapping(String index, String type, String mappingString) {
        // mappingString为拼接好的json格式的mapping串
        PutMapping.Builder builder = new PutMapping.Builder(index, type, mappingString);
        try {
            JestResult jestResult = jestClient.execute(builder.build());
            System.out.println("createIndexMapping result:{}" + jestResult.isSucceeded());
            if (!jestResult.isSucceeded()) {
                System.err.println("settingIndexMapping error:{}" + jestResult.getErrorMessage());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取index的mapping
     */
    public String getMapping(String indexName, String typeName) {
        GetMapping.Builder builder = new GetMapping.Builder();
        builder.addIndex(indexName).addType(typeName);
        try {
            JestResult result = jestClient.execute(builder.build());
            return result.getSourceAsObject(JsonObject.class).toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取索引index设置setting
     */
    public boolean getIndexSettings(String index) {
        try {
            JestResult jestResult = jestClient.execute(new GetSettings.Builder().addIndex(index).build());
            System.out.println(jestResult.getJsonString());
            return jestResult.isSucceeded();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 更改索引index设置setting
     */
    public boolean updateIndexSettings(String index) {
        String source;
        XContentBuilder mapBuilder = null;
        try {
            mapBuilder = XContentFactory.jsonBuilder();
            mapBuilder.startObject().startObject("index").field("max_result_window", "1000000").endObject().endObject();
            source = mapBuilder.string();
            JestResult jestResult = jestClient.execute(new UpdateSettings.Builder(source).build());
            System.out.println(jestResult.getJsonString());
            return jestResult.isSucceeded();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }
}

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

四月天03

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值