Elasticsearch使用ElasticsearchTemplate实现字段值增加/拼接字符串

需求背景:当浏览文档时使此文档的浏览数+1;利用ElsaticSearch的脚本更新可以灵活的实现该需求

如果使用sql应对以上需求,我们应该很简单的就可以写出

update news_bak.newsBase set browseCount = browseCount + 1 where id="5bf29a4f086899caed65e6bc"

在es中对应以上sql逻辑的代码,如下

POST /news_bak/newsBase/5bf29a4f086899caed65e6bc/_update
 {
   "script": {
     "inline": "ctx._source.browseCount += params.incr",
     "params": {
       "incr":1
     }
   }
 }

其中,news_bak为对应的index,newsBase为对应的type. 另外,注意一点,在引用变量时,以前的版本直接引用变量名即可,而Elasticsearch5.0以上的版本取参数需要使用==params.{参数名}==,这是语法上的更新,如果跟以前一样,直接这样使用:

POST /news_bak/newsBase/5bf29a4f086899caed65e6bc/_update
 {
   "script": {
     "inline": "ctx._source.browseCount += incr",
     "params": {
       "incr":1
     }
   }
 }

则会报错,提示我们出现异常==illegal_argument_exception==,告诉我们原因是==Variable [incr] is not defined.==,参数 incr 未定义

{
  "error": {
    "root_cause": [
      {
        "type": "remote_transport_exception",
        "reason": "[Sn0hpRC][192.168.8.53:9300][indices:data/write/update[s]]"
      }
    ],
    "type": "illegal_argument_exception",
    "reason": "failed to execute script",
    "caused_by": {
      "type": "script_exception",
      "reason": "compile error",
      "script_stack": [
        "... x._source.browseCount += incr",
        "                             ^---- HERE"
      ],
      "script": "ctx._source.browseCount += incr",
      "lang": "painless",
      "caused_by": {
        "type": "illegal_argument_exception",
        "reason": "Variable [incr] is not defined."
      }
    }
  },
  "status": 400
}

所以我们需要注意一下自己的Elasticsearch==版本==

接下来是使用Spring的ElasticSearchTemplate来进行操作,方法以及进行了封装 主要代码如下: model:

@Document(indexName = "news_bak", type = "newsBase")
public class NewsBase implements Serializable {
    @Id
    private String id;

    private String author;

    private String browsingVolume;

    private String headImg;

    private String content;
    ... 下面省略

}

EsUtil里面封装了对应的方法

/**
 * 提供es的工具类
 *
 * @author neo_chen
 * @version Ver 1.0 2018/11/16 新增
 * @Package
 */
public class EsUtil {

    /**
     * 在es的对应字段上追加对应的值
     * @param id  要追加的文档的id
     * @param clazz 文档对应Class对象
     * @param addFiled 要追加的字段
     * @param addParam 要追加的值
     * @return UpdateQuery
     * @eg 给这个文档的browseCount字段值+1
     * POST /#{index}/#{type}/#{id}/_update
     *  {
     *    "script": {
     *      "inline": "ctx._source.browseCount += params.incr",
     *      "params": {
     *        "incr":1
     *      }
     *    }
     *  }
     */
    public static <T>UpdateQuery filedValueAdd(String id, Class<T> clazz, String addFiled, Object addParam) {
        //构建UpdataRequest对象
        UpdateRequest updateRequest = new UpdateRequest();
        //设置参数
        Map<String, Object> params = new HashMap<>();
        params.put("incr", addParam);
        //构架Script对象,这里注意是 org.elasticsearch.script.Script
        Script script = new Script(Script.DEFAULT_SCRIPT_TYPE, "painless", "ctx._source." + addFiled + " += params.incr", params);
        updateRequest.script(script);
        //构架UpdateQueryBuilder 对象
        UpdateQueryBuilder updateQueryBuilder = new UpdateQueryBuilder();
        //设置要修改的_id
        updateQueryBuilder.withId(id);
        //设置updateRequest对象
        updateQueryBuilder.withUpdateRequest(updateRequest);
        //设置 class 对象,其实这里用class对象就是要区实体类标注的 index 和 type 也可以直接设置index 和 type
        /*
        //可以替换成
        updateQueryBuilder.withIndexName("your index");
        updateQueryBuilder.withType("yourType");
        */
        updateQueryBuilder.withClass(clazz);
        UpdateQuery build = updateQueryBuilder.build();
        return build;
    }

}

调用:

@Service(value = "newsBaseService")
public class NewsBaseServiceImpl implements NewsBaseService {
    @Autowired
    private ElasticsearchTemplate esTemplate;
    @Override
    public Long incrBrowse(String id) throws Exception {
        // id 为要改变的资讯id,NewsBase.class类,要修改的字段为browseCount,在原来的基础上+1
        UpdateQuery build = EsUtil.filedValueAdd(id, NewsBase.class, "browseCount", 1);
        UpdateResponse update = esTemplate.update(build);
        return null;
    }

}

请使用浏览器的分享功能分享到微信等