1. 程式人生 > >Elasticsearch - cannot write xcontent for unknown value of type class java.math.BigDecimal

Elasticsearch - cannot write xcontent for unknown value of type class java.math.BigDecimal

博文 helper rac ria long client 客戶端 last stringbu

問題與分析

在使用Elasticsearch進行index數據時,發現報錯如下:

java.lang.IllegalArgumentException: cannot write xcontent for unknown value of type class java.math.BigDecimal
    at org.elasticsearch.common.xcontent.XContentBuilder.unknownValue(XContentBuilder.java:755)
    at org.elasticsearch.common.xcontent.XContentBuilder.value(XContentBuilder.java:726)
    at org.elasticsearch.common.xcontent.XContentBuilder.field(XContentBuilder.java:711)
    at org.elasticsearch.index.query.BaseTermQueryBuilder.doXContent(BaseTermQueryBuilder.java:154)
    at org.elasticsearch.index.query.AbstractQueryBuilder.toXContent(AbstractQueryBuilder.java:82)
    at org.elasticsearch.index.query.BoolQueryBuilder.doXArrayContent(BoolQueryBuilder.java:275)
    at org.elasticsearch.index.query.BoolQueryBuilder.doXContent(BoolQueryBuilder.java:256)
    at org.elasticsearch.index.query.AbstractQueryBuilder.toXContent(AbstractQueryBuilder.java:82)
    at org.elasticsearch.common.xcontent.XContentBuilder.value(XContentBuilder.java:779)
    at org.elasticsearch.common.xcontent.XContentBuilder.value(XContentBuilder.java:772)
    at org.elasticsearch.common.xcontent.XContentBuilder.field(XContentBuilder.java:764)
    at org.elasticsearch.search.builder.SearchSourceBuilder.toXContent(SearchSourceBuilder.java:1184)
    at org.elasticsearch.common.xcontent.XContentHelper.toXContent(XContentHelper.java:349)
    at org.elasticsearch.search.builder.SearchSourceBuilder.toString(SearchSourceBuilder.java:1558)
    at org.elasticsearch.search.builder.SearchSourceBuilder.toString(SearchSourceBuilder.java:1553)
    at java.lang.String.valueOf(String.java:2994)
    at java.lang.StringBuilder.append(StringBuilder.java:131)
    at org.elasticsearch.action.search.SearchRequest.toString(SearchRequest.java:516)

從異常信息看,顯然ES無法接受BigDecimal類型,經過百度,也確實如此。在一篇博文評論中解釋如下:

應該是客戶端代碼裏將查詢的數值定義成了java.math.BigDecimal,而ES不支持這個類型。之所以2.2沒有問題,是因為之前的transport client發送數據之前將其序列化成了json,而在5.x以後,使用的內部的transport protocol,數據類型如果不匹配會拋錯誤。

所以數據類型的定義上,需要使用ES支持的類型。

解決方案

方案一:轉變成其他ES支持的數據類型

我使用的是6.4.2版本的Elasticsearch,該版本尚不支持BigDecimal或者BigInteger的數據類型,所以在index到Elasticsearch之前,需要轉換成其他數據類型,這裏要註意不要數據溢出了:

  1. BigDecimal要轉變成Double類型
  2. BigInteger要轉變成Long類型

方案二:使用更高版本的ES

我在看6.7.1版本的Elasticsearch源碼時發現已經可以支持BigDecimal或者BigInteger的數據類型了,所以直接使用該版本或更高版本的就行了。

下面附上兩個版本的支持的數據類型的源碼:

  • 6.4.2版本的Elasticsearch相關源碼
Map<Class<?>, Writer> writers = new HashMap<>();
writers.put(Boolean.class, (b, v) -> b.value((Boolean) v));
writers.put(Byte.class, (b, v) -> b.value((Byte) v));
writers.put(byte[].class, (b, v) -> b.value((byte[]) v));
writers.put(Date.class, XContentBuilder::timeValue);
writers.put(Double.class, (b, v) -> b.value((Double) v));
writers.put(double[].class, (b, v) -> b.values((double[]) v));
writers.put(Float.class, (b, v) -> b.value((Float) v));
writers.put(float[].class, (b, v) -> b.values((float[]) v));
writers.put(Integer.class, (b, v) -> b.value((Integer) v));
writers.put(int[].class, (b, v) -> b.values((int[]) v));
writers.put(Long.class, (b, v) -> b.value((Long) v));
writers.put(long[].class, (b, v) -> b.values((long[]) v));
writers.put(Short.class, (b, v) -> b.value((Short) v));
writers.put(short[].class, (b, v) -> b.values((short[]) v));
writers.put(String.class, (b, v) -> b.value((String) v));
writers.put(String[].class, (b, v) -> b.values((String[]) v));
writers.put(Locale.class, (b, v) -> b.value(v.toString()));
writers.put(Class.class, (b, v) -> b.value(v.toString()));
writers.put(ZonedDateTime.class, (b, v) -> b.value(v.toString()));
writers.put(Calendar.class, XContentBuilder::timeValue);
writers.put(GregorianCalendar.class, XContentBuilder::timeValue);
  • 6.7.1版本的Elasticsearch相關源碼
Map<Class<?>, Writer> writers = new HashMap<>();
writers.put(Boolean.class, (b, v) -> b.value((Boolean) v));
writers.put(Byte.class, (b, v) -> b.value((Byte) v));
writers.put(byte[].class, (b, v) -> b.value((byte[]) v));
writers.put(Date.class, XContentBuilder::timeValue);
writers.put(Double.class, (b, v) -> b.value((Double) v));
writers.put(double[].class, (b, v) -> b.values((double[]) v));
writers.put(Float.class, (b, v) -> b.value((Float) v));
writers.put(float[].class, (b, v) -> b.values((float[]) v));
writers.put(Integer.class, (b, v) -> b.value((Integer) v));
writers.put(int[].class, (b, v) -> b.values((int[]) v));
writers.put(Long.class, (b, v) -> b.value((Long) v));
writers.put(long[].class, (b, v) -> b.values((long[]) v));
writers.put(Short.class, (b, v) -> b.value((Short) v));
writers.put(short[].class, (b, v) -> b.values((short[]) v));
writers.put(String.class, (b, v) -> b.value((String) v));
writers.put(String[].class, (b, v) -> b.values((String[]) v));
writers.put(Locale.class, (b, v) -> b.value(v.toString()));
writers.put(Class.class, (b, v) -> b.value(v.toString()));
writers.put(ZonedDateTime.class, (b, v) -> b.value(v.toString()));
writers.put(Calendar.class, XContentBuilder::timeValue);
writers.put(GregorianCalendar.class, XContentBuilder::timeValue);
writers.put(BigInteger.class, (b, v) -> b.value((BigInteger) v));
writers.put(BigDecimal.class, (b, v) -> b.value((BigDecimal) v));

可以發現,在6.7.1版本的源碼裏,多出了最後的兩種數據類型的支持:BigInteger和BigDecimal。

參考鏈接

  • elastic search 5.4.版本,java api 調用出現:can not write type [class java.math.BigDecimal]

Elasticsearch - cannot write xcontent for unknown value of type class java.math.BigDecimal