@Override
  public void writeTo(StreamOutput out) throws IOException {
    super.writeTo(out);
    if (out.getVersion().before(Version.V_1_4_0)) {
      // term vector used to read & write the index twice, here and in the parent class
      out.writeString(index);
    }
    out.writeString(type);
    out.writeString(id);

    if (out.getVersion().onOrAfter(Version.V_1_4_0)) {
      out.writeBoolean(doc != null);
      if (doc != null) {
        out.writeBytesReference(doc);
      }
    }
    out.writeOptionalString(routing);
    out.writeOptionalString(preference);
    long longFlags = 0;
    for (Flag flag : flagsEnum) {
      longFlags |= (1 << flag.ordinal());
    }
    out.writeVLong(longFlags);
    if (selectedFields != null) {
      out.writeVInt(selectedFields.size());
      for (String selectedField : selectedFields) {
        out.writeString(selectedField);
      }
    } else {
      out.writeVInt(0);
    }
  }
  @Override
  public void readFrom(StreamInput in) throws IOException {
    super.readFrom(in);
    if (in.getVersion().before(Version.V_1_4_0)) {
      // term vector used to read & write the index twice, here and in the parent class
      in.readString();
    }
    type = in.readString();
    id = in.readString();

    if (in.getVersion().onOrAfter(Version.V_1_4_0)) {
      if (in.readBoolean()) {
        doc = in.readBytesReference();
      }
    }
    routing = in.readOptionalString();
    preference = in.readOptionalString();
    long flags = in.readVLong();

    flagsEnum.clear();
    for (Flag flag : Flag.values()) {
      if ((flags & (1 << flag.ordinal())) != 0) {
        flagsEnum.add(flag);
      }
    }
    int numSelectedFields = in.readVInt();
    if (numSelectedFields > 0) {
      selectedFields = new HashSet<>();
      for (int i = 0; i < numSelectedFields; i++) {
        selectedFields.add(in.readString());
      }
    }
  }