@Override
 public void checkCompatibility(
     MappedFieldType fieldType, List<String> conflicts, boolean strict) {
   super.checkCompatibility(fieldType, conflicts, strict);
   CompletionFieldType other = (CompletionFieldType) fieldType;
   if (analyzingSuggestLookupProvider.hasPayloads()
       != other.analyzingSuggestLookupProvider.hasPayloads()) {
     conflicts.add("mapper [" + names().fullName() + "] has different payload values");
   }
   if (analyzingSuggestLookupProvider.getPreservePositionsIncrements()
       != other.analyzingSuggestLookupProvider.getPreservePositionsIncrements()) {
     conflicts.add(
         "mapper ["
             + names().fullName()
             + "] has different 'preserve_position_increments' values");
   }
   if (analyzingSuggestLookupProvider.getPreserveSep()
       != other.analyzingSuggestLookupProvider.getPreserveSep()) {
     conflicts.add(
         "mapper [" + names().fullName() + "] has different 'preserve_separators' values");
   }
   if (!ContextMapping.mappingsAreEqual(getContextMapping(), other.getContextMapping())) {
     conflicts.add("mapper [" + names().fullName() + "] has different 'context_mapping' values");
   }
 }
  @Override
  public void parse(ParseContext context) throws IOException {
    XContentParser parser = context.parser();
    XContentParser.Token token = parser.currentToken();

    String surfaceForm = null;
    BytesRef payload = null;
    long weight = -1;
    List<String> inputs = Lists.newArrayListWithExpectedSize(4);

    if (token == XContentParser.Token.VALUE_STRING) {
      inputs.add(parser.text());
    } else {
      String currentFieldName = null;
      while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
        if (token == XContentParser.Token.FIELD_NAME) {
          currentFieldName = parser.currentName();
        } else if ("payload".equals(currentFieldName)) {
          if (!isStoringPayloads()) {
            throw new MapperException("Payloads disabled in mapping");
          }
          if (token == XContentParser.Token.START_OBJECT) {
            XContentBuilder payloadBuilder =
                XContentFactory.contentBuilder(parser.contentType()).copyCurrentStructure(parser);
            payload = payloadBuilder.bytes().toBytesRef();
            payloadBuilder.close();
          }
        } else if (token == XContentParser.Token.VALUE_STRING) {
          if ("output".equals(currentFieldName)) {
            surfaceForm = parser.text();
          }
          if ("input".equals(currentFieldName)) {
            inputs.add(parser.text());
          }
        } else if (token == XContentParser.Token.VALUE_NUMBER) {
          if ("weight".equals(currentFieldName)) {
            weight =
                parser.longValue(); // always parse a long to make sure we don't get the overflow
            // value
            if (weight < 0 || weight > Integer.MAX_VALUE) {
              throw new ElasticSearchIllegalArgumentException(
                  "Weight must be in the interval [0..2147483647] but was " + weight);
            }
          }
        } else if (token == XContentParser.Token.START_ARRAY) {
          if ("input".equals(currentFieldName)) {
            while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
              inputs.add(parser.text());
            }
          }
        }
      }
    }
    payload = payload == null ? EMPTY : payload;
    if (surfaceForm == null) { // no surface form use the input
      for (String input : inputs) {
        BytesRef suggestPayload =
            analyzingSuggestLookupProvider.buildPayload(new BytesRef(input), weight, payload);
        context.doc().add(getCompletionField(input, suggestPayload));
      }
    } else {
      BytesRef suggestPayload =
          analyzingSuggestLookupProvider.buildPayload(new BytesRef(surfaceForm), weight, payload);
      for (String input : inputs) {
        context.doc().add(getCompletionField(input, suggestPayload));
      }
    }
  }
 public BytesRef buildPayload(BytesRef surfaceForm, long weight, BytesRef payload)
     throws IOException {
   return analyzingSuggestLookupProvider.buildPayload(surfaceForm, weight, payload);
 }
  @Override
  public void parse(ParseContext context) throws IOException {
    XContentParser parser = context.parser();
    XContentParser.Token token = parser.currentToken();

    String surfaceForm = null;
    BytesRef payload = null;
    long weight = -1;
    List<String> inputs = Lists.newArrayListWithExpectedSize(4);

    SortedMap<String, ContextConfig> contextConfig = null;

    if (token == XContentParser.Token.VALUE_STRING) {
      inputs.add(parser.text());
      multiFields.parse(this, context);
    } else {
      String currentFieldName = null;
      while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
        if (token == XContentParser.Token.FIELD_NAME) {
          currentFieldName = parser.currentName();
          if (!ALLOWED_CONTENT_FIELD_NAMES.contains(currentFieldName)) {
            throw new ElasticsearchIllegalArgumentException(
                "Unknown field name["
                    + currentFieldName
                    + "], must be one of "
                    + ALLOWED_CONTENT_FIELD_NAMES);
          }
        } else if (Fields.CONTEXT.equals(currentFieldName)) {
          SortedMap<String, ContextConfig> configs = Maps.newTreeMap();

          if (token == Token.START_OBJECT) {
            while ((token = parser.nextToken()) != Token.END_OBJECT) {
              String name = parser.text();
              ContextMapping mapping = contextMapping.get(name);
              if (mapping == null) {
                throw new ElasticsearchParseException("context [" + name + "] is not defined");
              } else {
                token = parser.nextToken();
                configs.put(name, mapping.parseContext(context, parser));
              }
            }
            contextConfig = Maps.newTreeMap();
            for (ContextMapping mapping : contextMapping.values()) {
              ContextConfig config = configs.get(mapping.name());
              contextConfig.put(mapping.name(), config == null ? mapping.defaultConfig() : config);
            }
          } else {
            throw new ElasticsearchParseException("context must be an object");
          }
        } else if (Fields.CONTENT_FIELD_NAME_PAYLOAD.equals(currentFieldName)) {
          if (!isStoringPayloads()) {
            throw new MapperException("Payloads disabled in mapping");
          }
          if (token == XContentParser.Token.START_OBJECT) {
            XContentBuilder payloadBuilder =
                XContentFactory.contentBuilder(parser.contentType()).copyCurrentStructure(parser);
            payload = payloadBuilder.bytes().toBytesRef();
            payloadBuilder.close();
          } else if (token.isValue()) {
            payload = parser.utf8BytesOrNull();
          } else {
            throw new MapperException("payload doesn't support type " + token);
          }
        } else if (token == XContentParser.Token.VALUE_STRING) {
          if (Fields.CONTENT_FIELD_NAME_OUTPUT.equals(currentFieldName)) {
            surfaceForm = parser.text();
          }
          if (Fields.CONTENT_FIELD_NAME_INPUT.equals(currentFieldName)) {
            inputs.add(parser.text());
          }
          if (Fields.CONTENT_FIELD_NAME_WEIGHT.equals(currentFieldName)) {
            Number weightValue;
            try {
              weightValue = Long.parseLong(parser.text());
            } catch (NumberFormatException e) {
              throw new ElasticsearchIllegalArgumentException(
                  "Weight must be a string representing a numeric value, but was ["
                      + parser.text()
                      + "]");
            }
            weight =
                weightValue.longValue(); // always parse a long to make sure we don't get overflow
            checkWeight(weight);
          }
        } else if (token == XContentParser.Token.VALUE_NUMBER) {
          if (Fields.CONTENT_FIELD_NAME_WEIGHT.equals(currentFieldName)) {
            NumberType numberType = parser.numberType();
            if (NumberType.LONG != numberType && NumberType.INT != numberType) {
              throw new ElasticsearchIllegalArgumentException(
                  "Weight must be an integer, but was [" + parser.numberValue() + "]");
            }
            weight = parser.longValue(); // always parse a long to make sure we don't get overflow
            checkWeight(weight);
          }
        } else if (token == XContentParser.Token.START_ARRAY) {
          if (Fields.CONTENT_FIELD_NAME_INPUT.equals(currentFieldName)) {
            while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
              inputs.add(parser.text());
            }
          }
        }
      }
    }

    if (contextConfig == null) {
      contextConfig = Maps.newTreeMap();
      for (ContextMapping mapping : contextMapping.values()) {
        contextConfig.put(mapping.name(), mapping.defaultConfig());
      }
    }

    final ContextMapping.Context ctx = new ContextMapping.Context(contextConfig, context.doc());

    payload = payload == null ? EMPTY : payload;
    if (surfaceForm == null) { // no surface form use the input
      for (String input : inputs) {
        BytesRef suggestPayload =
            analyzingSuggestLookupProvider.buildPayload(new BytesRef(input), weight, payload);
        context.doc().add(getCompletionField(ctx, input, suggestPayload));
      }
    } else {
      BytesRef suggestPayload =
          analyzingSuggestLookupProvider.buildPayload(new BytesRef(surfaceForm), weight, payload);
      for (String input : inputs) {
        context.doc().add(getCompletionField(ctx, input, suggestPayload));
      }
    }
  }