}
}
}
if (script == null) {
throw new SearchParseException(context, "_script sorting requires setting the script to sort by");
}
if (type == null) {
throw new SearchParseException(context, "_script sorting requires setting the type of the script");
}
final SearchScript searchScript = context.scriptService().search(context.lookup(), scriptLang, script, scriptType, params);
if (STRING_SORT_TYPE.equals(type) && (sortMode == MultiValueMode.SUM || sortMode == MultiValueMode.AVG)) {
throw new SearchParseException(context, "type [string] doesn't support mode [" + sortMode + "]");
}
if (sortMode == null) {
sortMode = reverse ? MultiValueMode.MAX : MultiValueMode.MIN;
}
// If nested_path is specified, then wrap the `fieldComparatorSource` in a `NestedFieldComparatorSource`
ObjectMapper objectMapper;
final Nested nested;
if (nestedPath != null) {
ObjectMappers objectMappers = context.mapperService().objectMapper(nestedPath);
if (objectMappers == null) {
throw new ElasticsearchIllegalArgumentException("failed to find nested object mapping for explicit nested path [" + nestedPath + "]");
}
objectMapper = objectMappers.mapper();
if (!objectMapper.nested().isNested()) {
throw new ElasticsearchIllegalArgumentException("mapping for explicit nested path is not mapped as nested: [" + nestedPath + "]");
}
BitDocIdSetFilter rootDocumentsFilter = context.bitsetFilterCache().getBitDocIdSetFilter(NonNestedDocsFilter.INSTANCE);
BitDocIdSetFilter innerDocumentsFilter;
if (nestedFilter != null) {
innerDocumentsFilter = context.bitsetFilterCache().getBitDocIdSetFilter(nestedFilter);
} else {
innerDocumentsFilter = context.bitsetFilterCache().getBitDocIdSetFilter(objectMapper.nestedTypeFilter());
}
nested = new Nested(rootDocumentsFilter, innerDocumentsFilter);
} else {
nested = null;
}
final IndexFieldData.XFieldComparatorSource fieldComparatorSource;
switch (type) {
case STRING_SORT_TYPE:
fieldComparatorSource = new BytesRefFieldComparatorSource(null, null, sortMode, nested) {
@Override
protected SortedBinaryDocValues getValues(LeafReaderContext context) {
searchScript.setNextReader(context);
final BinaryDocValues values = new BinaryDocValues() {
final BytesRefBuilder spare = new BytesRefBuilder();
@Override
public BytesRef get(int docID) {
searchScript.setNextDocId(docID);
spare.copyChars(searchScript.run().toString());
return spare.get();
}
};
return FieldData.singleton(values, null);
}
@Override
protected void setScorer(Scorer scorer) {
searchScript.setScorer(scorer);
}
};
break;
case NUMBER_SORT_TYPE:
// TODO: should we rather sort missing values last?
fieldComparatorSource = new DoubleValuesComparatorSource(null, Double.MAX_VALUE, sortMode, nested) {
@Override
protected SortedNumericDoubleValues getValues(LeafReaderContext context) {
searchScript.setNextReader(context);
final NumericDoubleValues values = new NumericDoubleValues() {
@Override
public double get(int docID) {
searchScript.setNextDocId(docID);
return searchScript.runAsDouble();
}
};
return FieldData.singleton(values, null);
}
@Override
protected void setScorer(Scorer scorer) {
searchScript.setScorer(scorer);
}
};
break;
default:
throw new SearchParseException(context, "custom script sort type [" + type + "] not supported");
}
return new SortField("_script", fieldComparatorSource, reverse);
}