程序员ElasticSearch

ElasticSearch 怎样更新list中的元素

2018-03-06  本文已影响0人  追杀丘比特
{
  "_index": "school",
  "_type": "student",
  "_id": "id_4",
  "_version": 1,
  "_score": 1,
  "_source": {
    "name": "this name is name",
    "id": 4,
    "tags": [
      "班长",
      "学习委员",
      "文艺委员"
    ]
  }
}

可以看到在以上数据中tags为list类型,假设在一个业务场景中,我们需要对这个list中的值进行更新(删除或者添加),按照常规的操作方式,我们可能需要先取出tags的所有值,然后利用elastic search的Partial更新来更新tags的值。其实相比较与这种更新方式,es提供的构建Script来实现这种操作可能更加符合业务需求,如下:

1.建立一个TransPortClient

public class OldTransportClient {
    private Logger logger = Logger.getLogger(OldTransportClient.class);

    private TransportClient client;
    private IndicesAdminClient adminClient;

    @Value("${es.host}")
    private String host;

    @PostConstruct
    @SuppressWarnings({ "unchecked", "resource" })
    public synchronized TransportClient setupTransportClient() throws UnknownHostException {
        if (client == null) {
            Settings settings = Settings.builder().put("cluster.name", "ceiec-test")
                    .put("client.transport.sniff", true).build();
            client = new PreBuiltTransportClient(settings)
                    .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(host), 9300));
            logger.info("====== transport client set up! =======");
        }
        return client;
    }

    public synchronized IndicesAdminClient setupAdminClient() {
        if (client != null) {
            if (adminClient == null) {
                adminClient = client.admin().indices();
                logger.info("===== admin client set up ======");
            }
        }else {
            logger.error("transport client is not set up,please set up it!");
        }
        return adminClient;
    }
}

2.新建一个Script

 /**
     * update item with array in elasticSearch
     * @param index
     * @param type
     * @param id
     * @param value
     * @param delete : (default false)
     *          -----> true  delete an element from array
     *          -----> false add an element to array
     * @throws Exception
     */
public void updateExistList(String index,String type,String id,String field,String value,boolean isDelete) throws Exception{
        client = oldclient.setupTransportClient();
        Map<String,Object> params = new HashMap<>();
        params.put(field,value);
        String script_str;
        if (isDelete){
           script_str = "if (ctx._source."+field+".indexOf(params."+field+") != -1){" +
                    "ctx._source."+field+".remove(ctx._source."+field+".indexOf(params."+field+"))}";
        }else {
          //表示如果该doc不包含该字段,在该doc新建字段并赋值value,
          //如果存在该字段,会比较传入的值是否存在于list中,如果不存在就添加
            script_str = "if(!ctx._source.containsKey('"+field+"')){ctx._source."+field+"=[params."+field+"]}else{" +
                    "if (ctx._source."+field+".indexOf(params."+field+") == -1){ ctx._source."+field+".add(params."+field+")}}";
        }
        Script script = new Script(ScriptType.INLINE,Script.DEFAULT_SCRIPT_LANG,script_str,params);
        logger.info("script:\n"+script);
        UpdateResponse updateResponse = client.prepareUpdate(index,type,id)
                .setScript(script)
                .get();
        System.out.println(updateResponse.status());
    }

通过以上操作,我们就可以对es中的list数据进行增加和删除.

以上是针对es的list存储的是简单类型比如String,number等类型进行的操作,那么假如list里面存储的是Object类型该怎么做呢,现在有如下的数据:

{
  "_index": "school",
  "_type": "student",
  "_id": "id_2",
  "_version": 9,
  "_score": 1,
  "_source": {
    "name": "this name is name",
    "id": 2,
    "tags": [
      "班长",
      "学习委员",
      "文艺委员"
    ],
    "permission": [
      {
        "user": "user1",
        "name": "zhangsan2"
      },
      {
        "user": "user2",
        "name": "zhangsan2"
      },
      {
        "user": "user3",
        "name": "zhangsan3"
      },
      {
        "user": "user3",
        "name": "zhangsan3"
      },
      {
        "user": "user4",
        "name": "zhangsan3"
      }
    ]
  }
}

我们可以看到permission字段是存储着Object的list,那我们想更新其中的元素该怎么操作呢?
其实方法很类似,我们只需要构建不同的script就可以实现啦,如下:

/**
* @param index
* @param type
* @param id
* @param field 需更新的字段
* @param value
* @param delete : (default false)
*          -----> true  delete an element from array
*          -----> false add an element to array
* @throws Exception
*/
public void updateExistListObject(String index,String type,String doc_id,Object o,String field,boolean isDelete) throws Exception{
        client = oldclient.setupTransportClient();
        Map<String,String> map = (Map<String, String>) o;
        String property = (String) map.keySet().toArray()[0];
        Map<String,Object> params = new HashMap<>();
        params.put(field,o);
        String script_str;
        if (isDelete){
           script_str = "for (int i=0;i<ctx._source."+field +".size();i++)" +
                    "{ if(ctx._source."+field+"[i]['"+property+"'] == params."+field+"."+property+")"+
                    "{ctx._source."+field+".remove(i)}}";
        }else {
          //表示如果该doc不包含该字段,在该doc新建字段并赋值value,
          //如果存在该字段,会比较传入的对象是否存在于list中存在的对象相等,如果不相等就添加,相等就更新
           script_str = "if(!ctx._source.containsKey('"+field+"'))" + "{ctx._source."+field+"=[params."+field+"]} " +
                    "else { for(int i=0; i<ctx._source."+ field +".size(); i++)"+
                    "{ if (ctx._source."+field+"[i]['"+property+ "'] != params."+ field+"."+property+")
                     {ctx._source."+field+".add(params."+field+");break;}}}";
        }
        Script script = new Script(ScriptType.INLINE,Script.DEFAULT_SCRIPT_LANG,script_str,params);
        logger.info("script:\n"+script);
        UpdateResponse updateResponse = client.prepareUpdate(index,type,doc_id)
                .setScript(script)
                .get();
        System.out.println(updateResponse.status());
    }

script可以实现相当复杂的功能,因此需要大家在实践中去构建符合自己业务逻辑的script。

上一篇下一篇

猜你喜欢

热点阅读