Es対Dateタイプの処理

7767 ワード

ビジネスbeanを格納したい場合は、createTime、updateTimeフィールドが一般的に追加されます.データベースを使用する場合、時間フィールドはDateタイプとして一般的に設計されますが、esを使用する場合、下位メカニズムが分からない場合は、意図しない問題が発生します.
まず、esがDateタイプをどのように処理しているかを見てみましょう.コードは以下の通りです.
  private void writeValue(Object value) throws IOException {
        if (value == null) {
            generator.writeNull();
            return;
        }
        Class type = value.getClass();
        if (type == String.class) {
            generator.writeString((String) value);
        } else if (type == Integer.class) {
            generator.writeNumber(((Integer) value).intValue());
        } else if (type == Long.class) {
            generator.writeNumber(((Long) value).longValue());
        } else if (type == Float.class) {
            generator.writeNumber(((Float) value).floatValue());
        } else if (type == Double.class) {
            generator.writeNumber(((Double) value).doubleValue());
        } else if (type == Byte.class) {
            generator.writeNumber(((Byte)value).byteValue());
        } else if (type == Short.class) {
            generator.writeNumber(((Short) value).shortValue());
        } else if (type == Boolean.class) {
            generator.writeBoolean(((Boolean) value).booleanValue());
        } else if (type == GeoPoint.class) {
            generator.writeStartObject();
            generator.writeNumberField("lat", ((GeoPoint) value).lat());
            generator.writeNumberField("lon", ((GeoPoint) value).lon());
            generator.writeEndObject();
        } else if (value instanceof Map) {
            writeMap((Map) value);
        } else if (value instanceof Iterable) {
            generator.writeStartArray();
            for (Object v : (Iterable) value) {
                writeValue(v);
            }
            generator.writeEndArray();
        } else if (value instanceof Object[]) {
            generator.writeStartArray();
            for (Object v : (Object[]) value) {
                writeValue(v);
            }
            generator.writeEndArray();
        } else if (type == byte[].class) {
            generator.writeBinary((byte[]) value);
        } else if (value instanceof Date) {
            generator.writeString(XContentBuilder.defaultDatePrinter.print(((Date) value).getTime()));
        } else if (value instanceof Calendar) {
            generator.writeString(XContentBuilder.defaultDatePrinter.print((((Calendar) value)).getTimeInMillis()));
        } else if (value instanceof ReadableInstant) {
            generator.writeString(XContentBuilder.defaultDatePrinter.print((((ReadableInstant) value)).getMillis()));
        } else if (value instanceof BytesReference) {
            BytesReference bytes = (BytesReference) value;
            if (!bytes.hasArray()) {
                bytes = bytes.toBytesArray();
            }
            generator.writeBinary(bytes.array(), bytes.arrayOffset(), bytes.length());
        } else if (value instanceof BytesRef) {
            BytesRef bytes = (BytesRef) value;
            generator.writeBinary(bytes.bytes, bytes.offset, bytes.length);
        } else if (value instanceof Text) {
            Text text = (Text) value;
            if (text.hasBytes() && text.bytes().hasArray()) {
                generator.writeUTF8String(text.bytes().array(), text.bytes().arrayOffset(), text.bytes().length());
            } else if (text.hasString()) {
                generator.writeString(text.string());
            } else {
                BytesArray bytesArray = text.bytes().toBytesArray();
                generator.writeUTF8String(bytesArray.array(), bytesArray.arrayOffset(), bytesArray.length());
            }
        } else if (value instanceof ToXContent) {
            ((ToXContent) value).toXContent(this, ToXContent.EMPTY_PARAMS);
        } else if (value instanceof double[]) {
            generator.writeStartArray();
            for (double v : (double[]) value) {
                generator.writeNumber(v);
            }
            generator.writeEndArray();
        } else if (value instanceof long[]) {
            generator.writeStartArray();
            for (long v : (long[]) value) {
                generator.writeNumber(v);
            }
            generator.writeEndArray();
        } else if (value instanceof int[]) {
            generator.writeStartArray();
            for (int v : (int[]) value) {
                generator.writeNumber(v);
            }
            generator.writeEndArray();
        } else if (value instanceof float[]) {
            generator.writeStartArray();
            for (float v : (float[]) value) {
                generator.writeNumber(v);
            }
            generator.writeEndArray();
        } else if (value instanceof short[]) {
            generator.writeStartArray();
            for (short v : (short[]) value) {
                generator.writeNumber(v);
            }
            generator.writeEndArray();
        } else {
            // if this is a "value" object, like enum, DistanceUnit, ..., just toString it
            // yea, it can be misleading when toString a Java class, but really, jackson should be used in that case
            generator.writeString(value.toString());
            //throw new ElasticsearchIllegalArgumentException("type not supported for generic value conversion: " + type);
        }
    }
クラスにメンバー変数が定義されているDateTimeFormatter defaultDatePrinter=ISO DateTimeFormat.dateTime().withZone(DateTimeZone.UTC);    XContentBuilder.defaultDatePrinter.print(((Date) value).getTime())が入ってから
 /**
     * Prints a millisecond instant to a String.
     * 

* This method will use the override zone and the override chronology if * they are set. Otherwise it will use the ISO chronology and default zone. * * @param instant millis since 1970-01-01T00:00:00Z * @return the printed result */ public String print(long instant) { StringBuilder buf = new StringBuilder(requirePrinter().estimatePrintedLength()); try { printTo((Appendable) buf, instant); } catch (IOException ex) { // StringBuilder does not throw IOException } return buf.toString(); }


ここを見て、esが最終的にDateタイプをどのように格納しているか知っているはずです.はい、そうです.
最終的な出力は文字列で出力されますが、デフォルトのフォーマットは次のとおりです.1970-01-01T00:00:00Z
、つまりデフォルト
  UTC
を選択します.
そのメカニズムが分からない場合は、Dateタイプの時間フィールドformatをカスタムフォーマットにしてesを挿入すると、解析異常のエラーが報告されます.あるいは、yyyy-MM-ddのesフィールドを対応するDateタイプに逆シーケンス化するなど、typeをdate、フォーマットをカスタム化すると、次のようにエラーが表示されます.
Caused by: com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not construct instance of java.util.Date from String value '2017-06-30 15:41:24': not a valid representation (error: Failed to parse Date value '2017-06-30 15:41:24': Can not parse date "2017-06-30 15:41:24": not compatible with any of standard forms ("yyyy-MM-dd'T'HH:mm:ss.SSSZ", "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "EEE, dd MMM yyyy HH:mm:ss zzz", "yyyy-MM-dd"))  at [Source: java.io.StringReader@3d408487; line: 13, column: 24] (through reference chain: com.qunar.finance.nbdata.common.bean.vehicle.cargraph.CarUserResult["createTime"])
もちろん、上記の問題を理解したら、Dateタイプのストレージ時間を直接使用しても問題ありません.しかし、注意しなければならないのは、タイムゾーンが8時間も少なくなったので、私たちは自分から追加しなければなりません.通常、私たちが時間を使うとき、一般的にはタイムゾーンの要素を考慮しません.また、保存されたフォーマットも対応するタイムゾーンを保存しません.私たちの日常的な使用習慣に合いません.では、時間フィールドをよりよく保存するにはどうすればいいのでしょうか.
1 longタイプのタイムスタンプを直接使用する場合、直感的でないという欠点があります
2 Stringタイプに設定し、対応する時間にカスタムformatを作成します.mappingではフィールドがdateタイプに設定されていることを覚えておいてください.formatフォーマットはカスタムと一致しています.
実際の応用では、通常、上記の2つのいずれかを選択します.