Rename feature, feature_vector and feature_query (#37794)

Ranaming as follows:
feature -> rank_feature
feature_vector -> rank_features
feature query -> rank_feature query

Ranaming is done to distinguish from other vector types.

Closes #36723
This commit is contained in:
Mayya Sharipova 2019-01-24 19:18:48 -05:00 committed by GitHub
parent 9d87ca567a
commit a30ce6a00a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 243 additions and 241 deletions

View File

@ -43,9 +43,9 @@ string:: <<text,`text`>> and <<keyword,`keyword`>>
<<alias>>:: Defines an alias to an existing field.
<<feature>>:: Record numeric features to boost hits at query time.
<<rank-feature>>:: Record numeric feature to boost hits at query time.
<<feature-vector>>:: Record numeric feature vectors to boost hits at query time.
<<rank-features>>:: Record numeric features to boost hits at query time.
<<dense-vector>>:: Record dense vectors of float values.
@ -100,9 +100,9 @@ include::types/percolator.asciidoc[]
include::types/parent-join.asciidoc[]
include::types/feature.asciidoc[]
include::types/rank-feature.asciidoc[]
include::types/feature-vector.asciidoc[]
include::types/rank-features.asciidoc[]
include::types/dense-vector.asciidoc[]

View File

@ -1,57 +0,0 @@
[[feature]]
=== Feature datatype
A `feature` field can index numbers so that they can later be used to boost
documents in queries with a <<query-dsl-feature-query,`feature`>> query.
[source,js]
--------------------------------------------------
PUT my_index
{
"mappings": {
"properties": {
"pagerank": {
"type": "feature" <1>
},
"url_length": {
"type": "feature",
"positive_score_impact": false <2>
}
}
}
}
PUT my_index/_doc/1
{
"pagerank": 8,
"url_length": 22
}
GET my_index/_search
{
"query": {
"feature": {
"field": "pagerank"
}
}
}
--------------------------------------------------
// CONSOLE
<1> Feature fields must use the `feature` field type
<2> Features that correlate negatively with the score need to declare it
NOTE: `feature` fields only support single-valued fields and strictly positive
values. Multi-valued fields and negative values will be rejected.
NOTE: `feature` fields do not support querying, sorting or aggregating. They may
only be used within <<query-dsl-feature-query,`feature`>> queries.
NOTE: `feature` fields only preserve 9 significant bits for the precision, which
translates to a relative error of about 0.4%.
Features that correlate negatively with the score should set
`positive_score_impact` to `false` (defaults to `true`). This will be used by
the <<query-dsl-feature-query,`feature`>> query to modify the scoring formula
in such a way that the score decreases with the value of the feature instead of
increasing. For instance in web search, the url length is a commonly used
feature which correlates negatively with scores.

View File

@ -0,0 +1,57 @@
[[rank-feature]]
=== Rank feature datatype
A `rank_feature` field can index numbers so that they can later be used to boost
documents in queries with a <<query-dsl-rank-feature-query,`rank_feature`>> query.
[source,js]
--------------------------------------------------
PUT my_index
{
"mappings": {
"properties": {
"pagerank": {
"type": "rank_feature" <1>
},
"url_length": {
"type": "rank_feature",
"positive_score_impact": false <2>
}
}
}
}
PUT my_index/_doc/1
{
"pagerank": 8,
"url_length": 22
}
GET my_index/_search
{
"query": {
"rank_feature": {
"field": "pagerank"
}
}
}
--------------------------------------------------
// CONSOLE
<1> Rank feature fields must use the `rank_feature` field type
<2> Rank features that correlate negatively with the score need to declare it
NOTE: `rank_feature` fields only support single-valued fields and strictly positive
values. Multi-valued fields and negative values will be rejected.
NOTE: `rank_feature` fields do not support querying, sorting or aggregating. They may
only be used within <<query-dsl-rank-feature-query,`rank_feature`>> queries.
NOTE: `rank_feature` fields only preserve 9 significant bits for the precision, which
translates to a relative error of about 0.4%.
Rank features that correlate negatively with the score should set
`positive_score_impact` to `false` (defaults to `true`). This will be used by
the <<query-dsl-rank-feature-query,`rank_feature`>> query to modify the scoring formula
in such a way that the score decreases with the value of the feature instead of
increasing. For instance in web search, the url length is a commonly used
feature which correlates negatively with scores.

View File

@ -1,11 +1,11 @@
[[feature-vector]]
=== Feature vector datatype
[[rank-features]]
=== Rank features datatype
A `feature_vector` field can index numeric feature vectors, so that they can
A `rank_features` field can index numeric feature vectors, so that they can
later be used to boost documents in queries with a
<<query-dsl-feature-query,`feature`>> query.
<<query-dsl-rank-feature-query,`rank_feature`>> query.
It is analogous to the <<feature,`feature`>> datatype but is better suited
It is analogous to the <<rank-feature,`rank_feature`>> datatype but is better suited
when the list of features is sparse so that it wouldn't be reasonable to add
one field to the mappings for each of them.
@ -16,7 +16,7 @@ PUT my_index
"mappings": {
"properties": {
"topics": {
"type": "feature_vector" <1>
"type": "rank_features" <1>
}
}
}
@ -41,22 +41,22 @@ PUT my_index/_doc/2
GET my_index/_search
{
"query": {
"feature": {
"rank_feature": {
"field": "topics.politics"
}
}
}
--------------------------------------------------
// CONSOLE
<1> Feature vector fields must use the `feature_vector` field type
<2> Feature vector fields must be a hash with string keys and strictly positive numeric values
<1> Rank features fields must use the `rank_features` field type
<2> Rank features fields must be a hash with string keys and strictly positive numeric values
NOTE: `feature_vector` fields only support single-valued features and strictly
NOTE: `rank_features` fields only support single-valued features and strictly
positive values. Multi-valued fields and zero or negative values will be rejected.
NOTE: `feature_vector` fields do not support sorting or aggregating and may
only be queried using <<query-dsl-feature-query,`feature`>> queries.
NOTE: `rank_features` fields do not support sorting or aggregating and may
only be queried using <<query-dsl-rank-feature-query,`rank_feature`>> queries.
NOTE: `feature_vector` fields only preserve 9 significant bits for the
NOTE: `rank_features` fields only preserve 9 significant bits for the
precision, which translates to a relative error of about 0.4%.

View File

@ -1,8 +1,8 @@
[[query-dsl-feature-query]]
=== Feature Query
[[query-dsl-rank-feature-query]]
=== Rank Feature Query
The `feature` query is a specialized query that only works on
<<feature,`feature`>> fields and <<feature-vector,`feature_vector`>> fields.
The `rank_feature` query is a specialized query that only works on
<<rank-feature,`rank_feature`>> fields and <<rank-features,`rank_features`>> fields.
Its goal is to boost the score of documents based on the values of numeric
features. It is typically put in a `should` clause of a
<<query-dsl-bool-query,`bool`>> query so that its score is added to the score
@ -32,14 +32,14 @@ PUT test
"mappings": {
"properties": {
"pagerank": {
"type": "feature"
"type": "rank_feature"
},
"url_length": {
"type": "feature",
"type": "rank_feature",
"positive_score_impact": false
},
"topics": {
"type": "feature_vector"
"type": "rank_features"
}
}
}
@ -97,18 +97,18 @@ GET test/_search
],
"should": [
{
"feature": {
"rank_feature": {
"field": "pagerank"
}
},
{
"feature": {
"rank_feature": {
"field": "url_length",
"boost": 0.1
}
},
{
"feature": {
"rank_feature": {
"field": "topics.sports",
"boost": 0.4
}
@ -123,8 +123,8 @@ GET test/_search
[float]
=== Supported functions
The `feature` query supports 3 functions in order to boost scores using the
values of features. If you do not know where to start, we recommend that you
The `rank_feature` query supports 3 functions in order to boost scores using the
values of rank features. If you do not know where to start, we recommend that you
start with the `saturation` function, which is the default when no function is
provided.
@ -132,11 +132,11 @@ provided.
==== Saturation
This function gives a score that is equal to `S / (S + pivot)` where `S` is the
value of the feature and `pivot` is a configurable pivot value so that the
value of the rank feature and `pivot` is a configurable pivot value so that the
result will be less than +0.5+ if `S` is less than pivot and greater than +0.5+
otherwise. Scores are always is +(0, 1)+.
If the feature has a negative score impact then the function will be computed as
If the rank feature has a negative score impact then the function will be computed as
`pivot / (S + pivot)`, which decreases when `S` increases.
[source,js]
@ -144,7 +144,7 @@ If the feature has a negative score impact then the function will be computed as
GET test/_search
{
"query": {
"feature": {
"rank_feature": {
"field": "pagerank",
"saturation": {
"pivot": 8
@ -166,7 +166,7 @@ train a good pivot value.
GET test/_search
{
"query": {
"feature": {
"rank_feature": {
"field": "pagerank",
"saturation": {}
}
@ -180,17 +180,17 @@ GET test/_search
==== Logarithm
This function gives a score that is equal to `log(scaling_factor + S)` where
`S` is the value of the feature and `scaling_factor` is a configurable scaling
`S` is the value of the rank feature and `scaling_factor` is a configurable scaling
factor. Scores are unbounded.
This function only supports features that have a positive score impact.
This function only supports rank features that have a positive score impact.
[source,js]
--------------------------------------------------
GET test/_search
{
"query": {
"feature": {
"rank_feature": {
"field": "pagerank",
"log": {
"scaling_factor": 4
@ -219,7 +219,7 @@ that you stick to the `saturation` function instead.
GET test/_search
{
"query": {
"feature": {
"rank_feature": {
"field": "pagerank",
"sigmoid": {
"pivot": 7,

View File

@ -211,7 +211,7 @@ There are faster alternative query types that can efficiently skip
non-competitive hits:
* If you want to boost documents on some static fields, use
<<query-dsl-feature-query, Feature Query>>.
<<query-dsl-rank-feature-query, Rank Feature Query>>.
==== Transition from Function Score Query

View File

@ -23,7 +23,7 @@ A query that allows to modify the score of a sub-query with a script.
This query finds queries that are stored as documents that match with
the specified document.
<<query-dsl-feature-query,`feature` query>>::
<<query-dsl-rank-feature-query,`rank_feature` query>>::
A query that computes scores based on the values of numeric features and is
able to efficiently skip non-competitive hits.
@ -40,6 +40,6 @@ include::script-score-query.asciidoc[]
include::percolate-query.asciidoc[]
include::feature-query.asciidoc[]
include::rank-feature-query.asciidoc[]
include::wrapper-query.asciidoc[]

View File

@ -20,7 +20,7 @@
package org.elasticsearch.index.mapper;
import org.elasticsearch.index.mapper.MetadataFieldMapper.TypeParser;
import org.elasticsearch.index.query.FeatureQueryBuilder;
import org.elasticsearch.index.query.RankFeatureQueryBuilder;
import org.elasticsearch.plugins.MapperPlugin;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.plugins.SearchPlugin;
@ -37,8 +37,8 @@ public class MapperExtrasPlugin extends Plugin implements MapperPlugin, SearchPl
Map<String, Mapper.TypeParser> mappers = new LinkedHashMap<>();
mappers.put(ScaledFloatFieldMapper.CONTENT_TYPE, new ScaledFloatFieldMapper.TypeParser());
mappers.put(TokenCountFieldMapper.CONTENT_TYPE, new TokenCountFieldMapper.TypeParser());
mappers.put(FeatureFieldMapper.CONTENT_TYPE, new FeatureFieldMapper.TypeParser());
mappers.put(FeatureVectorFieldMapper.CONTENT_TYPE, new FeatureVectorFieldMapper.TypeParser());
mappers.put(RankFeatureFieldMapper.CONTENT_TYPE, new RankFeatureFieldMapper.TypeParser());
mappers.put(RankFeaturesFieldMapper.CONTENT_TYPE, new RankFeaturesFieldMapper.TypeParser());
mappers.put(DenseVectorFieldMapper.CONTENT_TYPE, new DenseVectorFieldMapper.TypeParser());
mappers.put(SparseVectorFieldMapper.CONTENT_TYPE, new SparseVectorFieldMapper.TypeParser());
return Collections.unmodifiableMap(mappers);
@ -46,13 +46,14 @@ public class MapperExtrasPlugin extends Plugin implements MapperPlugin, SearchPl
@Override
public Map<String, TypeParser> getMetadataMappers() {
return Collections.singletonMap(FeatureMetaFieldMapper.CONTENT_TYPE, new FeatureMetaFieldMapper.TypeParser());
return Collections.singletonMap(RankFeatureMetaFieldMapper.CONTENT_TYPE, new RankFeatureMetaFieldMapper.TypeParser());
}
@Override
public List<QuerySpec<?>> getQueries() {
return Collections.singletonList(
new QuerySpec<>(FeatureQueryBuilder.NAME, FeatureQueryBuilder::new, p -> FeatureQueryBuilder.PARSER.parse(p, null)));
new QuerySpec<>(RankFeatureQueryBuilder.NAME, RankFeatureQueryBuilder::new,
p -> RankFeatureQueryBuilder.PARSER.parse(p, null)));
}
}

View File

@ -42,12 +42,12 @@ import java.util.Objects;
/**
* A {@link FieldMapper} that exposes Lucene's {@link FeatureField}.
*/
public class FeatureFieldMapper extends FieldMapper {
public class RankFeatureFieldMapper extends FieldMapper {
public static final String CONTENT_TYPE = "feature";
public static final String CONTENT_TYPE = "rank_feature";
public static class Defaults {
public static final MappedFieldType FIELD_TYPE = new FeatureFieldType();
public static final MappedFieldType FIELD_TYPE = new RankFeatureFieldType();
static {
FIELD_TYPE.setTokenized(false);
@ -58,7 +58,7 @@ public class FeatureFieldMapper extends FieldMapper {
}
}
public static class Builder extends FieldMapper.Builder<Builder, FeatureFieldMapper> {
public static class Builder extends FieldMapper.Builder<Builder, RankFeatureFieldMapper> {
public Builder(String name) {
super(name, Defaults.FIELD_TYPE, Defaults.FIELD_TYPE);
@ -66,8 +66,8 @@ public class FeatureFieldMapper extends FieldMapper {
}
@Override
public FeatureFieldType fieldType() {
return (FeatureFieldType) super.fieldType();
public RankFeatureFieldType fieldType() {
return (RankFeatureFieldType) super.fieldType();
}
public Builder positiveScoreImpact(boolean v) {
@ -76,9 +76,9 @@ public class FeatureFieldMapper extends FieldMapper {
}
@Override
public FeatureFieldMapper build(BuilderContext context) {
public RankFeatureFieldMapper build(BuilderContext context) {
setupFieldType(context);
return new FeatureFieldMapper(
return new RankFeatureFieldMapper(
name, fieldType, defaultFieldType,
context.indexSettings(), multiFieldsBuilder.build(this, context), copyTo);
}
@ -87,7 +87,7 @@ public class FeatureFieldMapper extends FieldMapper {
public static class TypeParser implements Mapper.TypeParser {
@Override
public Mapper.Builder<?,?> parse(String name, Map<String, Object> node, ParserContext parserContext) throws MapperParsingException {
FeatureFieldMapper.Builder builder = new FeatureFieldMapper.Builder(name);
RankFeatureFieldMapper.Builder builder = new RankFeatureFieldMapper.Builder(name);
for (Iterator<Map.Entry<String, Object>> iterator = node.entrySet().iterator(); iterator.hasNext();) {
Map.Entry<String, Object> entry = iterator.next();
String propName = entry.getKey();
@ -101,22 +101,22 @@ public class FeatureFieldMapper extends FieldMapper {
}
}
public static final class FeatureFieldType extends MappedFieldType {
public static final class RankFeatureFieldType extends MappedFieldType {
private boolean positiveScoreImpact = true;
public FeatureFieldType() {
public RankFeatureFieldType() {
setIndexAnalyzer(Lucene.KEYWORD_ANALYZER);
setSearchAnalyzer(Lucene.KEYWORD_ANALYZER);
}
protected FeatureFieldType(FeatureFieldType ref) {
protected RankFeatureFieldType(RankFeatureFieldType ref) {
super(ref);
this.positiveScoreImpact = ref.positiveScoreImpact;
}
public FeatureFieldType clone() {
return new FeatureFieldType(this);
public RankFeatureFieldType clone() {
return new RankFeatureFieldType(this);
}
@Override
@ -124,7 +124,7 @@ public class FeatureFieldMapper extends FieldMapper {
if (super.equals(o) == false) {
return false;
}
FeatureFieldType other = (FeatureFieldType) o;
RankFeatureFieldType other = (RankFeatureFieldType) o;
return Objects.equals(positiveScoreImpact, other.positiveScoreImpact);
}
@ -138,7 +138,7 @@ public class FeatureFieldMapper extends FieldMapper {
@Override
public void checkCompatibility(MappedFieldType other, List<String> conflicts) {
super.checkCompatibility(other, conflicts);
if (positiveScoreImpact != ((FeatureFieldType) other).positiveScoreImpact()) {
if (positiveScoreImpact != ((RankFeatureFieldType) other).positiveScoreImpact()) {
conflicts.add("mapper [" + name() + "] has different [positive_score_impact] values");
}
}
@ -164,29 +164,29 @@ public class FeatureFieldMapper extends FieldMapper {
@Override
public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName) {
throw new UnsupportedOperationException("[feature] fields do not support sorting, scripting or aggregating");
throw new UnsupportedOperationException("[rank_feature] fields do not support sorting, scripting or aggregating");
}
@Override
public Query termQuery(Object value, QueryShardContext context) {
throw new UnsupportedOperationException("Queries on [feature] fields are not supported");
throw new UnsupportedOperationException("Queries on [rank_feature] fields are not supported");
}
}
private FeatureFieldMapper(String simpleName, MappedFieldType fieldType, MappedFieldType defaultFieldType,
private RankFeatureFieldMapper(String simpleName, MappedFieldType fieldType, MappedFieldType defaultFieldType,
Settings indexSettings, MultiFields multiFields, CopyTo copyTo) {
super(simpleName, fieldType, defaultFieldType, indexSettings, multiFields, copyTo);
assert fieldType.indexOptions().compareTo(IndexOptions.DOCS_AND_FREQS) <= 0;
}
@Override
protected FeatureFieldMapper clone() {
return (FeatureFieldMapper) super.clone();
protected RankFeatureFieldMapper clone() {
return (RankFeatureFieldMapper) super.clone();
}
@Override
public FeatureFieldType fieldType() {
return (FeatureFieldType) super.fieldType();
public RankFeatureFieldType fieldType() {
return (RankFeatureFieldType) super.fieldType();
}
@Override
@ -207,8 +207,8 @@ public class FeatureFieldMapper extends FieldMapper {
}
if (context.doc().getByKey(name()) != null) {
throw new IllegalArgumentException("[feature] fields do not support indexing multiple values for the same field [" + name() +
"] in the same document");
throw new IllegalArgumentException("[rank_feature] fields do not support indexing multiple values for the same field [" +
name() + "] in the same document");
}
if (fieldType().positiveScoreImpact() == false) {

View File

@ -33,18 +33,18 @@ import java.util.List;
import java.util.Map;
/**
* This meta field only exists because feature fields index everything into a
* This meta field only exists because rank feature fields index everything into a
* common _feature field and Elasticsearch has a custom codec that complains
* when fields exist in the index and not in mappings.
*/
public class FeatureMetaFieldMapper extends MetadataFieldMapper {
public class RankFeatureMetaFieldMapper extends MetadataFieldMapper {
public static final String NAME = "_feature";
public static final String CONTENT_TYPE = "_feature";
public static class Defaults {
public static final MappedFieldType FIELD_TYPE = new FeatureMetaFieldType();
public static final MappedFieldType FIELD_TYPE = new RankFeatureMetaFieldType();
static {
FIELD_TYPE.setIndexOptions(IndexOptions.DOCS_AND_FREQS);
@ -58,16 +58,16 @@ public class FeatureMetaFieldMapper extends MetadataFieldMapper {
}
}
public static class Builder extends MetadataFieldMapper.Builder<Builder, FeatureMetaFieldMapper> {
public static class Builder extends MetadataFieldMapper.Builder<Builder, RankFeatureMetaFieldMapper> {
public Builder(MappedFieldType existing) {
super(NAME, existing == null ? Defaults.FIELD_TYPE : existing, Defaults.FIELD_TYPE);
}
@Override
public FeatureMetaFieldMapper build(BuilderContext context) {
public RankFeatureMetaFieldMapper build(BuilderContext context) {
setupFieldType(context);
return new FeatureMetaFieldMapper(fieldType, context.indexSettings());
return new RankFeatureMetaFieldMapper(fieldType, context.indexSettings());
}
}
@ -82,7 +82,7 @@ public class FeatureMetaFieldMapper extends MetadataFieldMapper {
public MetadataFieldMapper getDefault(MappedFieldType fieldType, ParserContext context) {
final Settings indexSettings = context.mapperService().getIndexSettings().getSettings();
if (fieldType != null) {
return new FeatureMetaFieldMapper(indexSettings, fieldType);
return new RankFeatureMetaFieldMapper(indexSettings, fieldType);
} else {
return parse(NAME, Collections.emptyMap(), context)
.build(new BuilderContext(indexSettings, new ContentPath(1)));
@ -90,18 +90,18 @@ public class FeatureMetaFieldMapper extends MetadataFieldMapper {
}
}
public static final class FeatureMetaFieldType extends MappedFieldType {
public static final class RankFeatureMetaFieldType extends MappedFieldType {
public FeatureMetaFieldType() {
public RankFeatureMetaFieldType() {
}
protected FeatureMetaFieldType(FeatureMetaFieldType ref) {
protected RankFeatureMetaFieldType(RankFeatureMetaFieldType ref) {
super(ref);
}
@Override
public FeatureMetaFieldType clone() {
return new FeatureMetaFieldType(this);
public RankFeatureMetaFieldType clone() {
return new RankFeatureMetaFieldType(this);
}
@Override
@ -120,11 +120,11 @@ public class FeatureMetaFieldMapper extends MetadataFieldMapper {
}
}
private FeatureMetaFieldMapper(Settings indexSettings, MappedFieldType existing) {
private RankFeatureMetaFieldMapper(Settings indexSettings, MappedFieldType existing) {
this(existing.clone(), indexSettings);
}
private FeatureMetaFieldMapper(MappedFieldType fieldType, Settings indexSettings) {
private RankFeatureMetaFieldMapper(MappedFieldType fieldType, Settings indexSettings) {
super(NAME, fieldType, Defaults.FIELD_TYPE, indexSettings);
}

View File

@ -37,12 +37,12 @@ import java.util.Map;
* A {@link FieldMapper} that exposes Lucene's {@link FeatureField} as a sparse
* vector of features.
*/
public class FeatureVectorFieldMapper extends FieldMapper {
public class RankFeaturesFieldMapper extends FieldMapper {
public static final String CONTENT_TYPE = "feature_vector";
public static final String CONTENT_TYPE = "rank_features";
public static class Defaults {
public static final MappedFieldType FIELD_TYPE = new FeatureVectorFieldType();
public static final MappedFieldType FIELD_TYPE = new RankFeaturesFieldType();
static {
FIELD_TYPE.setTokenized(false);
@ -53,7 +53,7 @@ public class FeatureVectorFieldMapper extends FieldMapper {
}
}
public static class Builder extends FieldMapper.Builder<Builder, FeatureVectorFieldMapper> {
public static class Builder extends FieldMapper.Builder<Builder, RankFeaturesFieldMapper> {
public Builder(String name) {
super(name, Defaults.FIELD_TYPE, Defaults.FIELD_TYPE);
@ -61,14 +61,14 @@ public class FeatureVectorFieldMapper extends FieldMapper {
}
@Override
public FeatureVectorFieldType fieldType() {
return (FeatureVectorFieldType) super.fieldType();
public RankFeaturesFieldType fieldType() {
return (RankFeaturesFieldType) super.fieldType();
}
@Override
public FeatureVectorFieldMapper build(BuilderContext context) {
public RankFeaturesFieldMapper build(BuilderContext context) {
setupFieldType(context);
return new FeatureVectorFieldMapper(
return new RankFeaturesFieldMapper(
name, fieldType, defaultFieldType,
context.indexSettings(), multiFieldsBuilder.build(this, context), copyTo);
}
@ -77,24 +77,24 @@ public class FeatureVectorFieldMapper extends FieldMapper {
public static class TypeParser implements Mapper.TypeParser {
@Override
public Mapper.Builder<?,?> parse(String name, Map<String, Object> node, ParserContext parserContext) throws MapperParsingException {
FeatureVectorFieldMapper.Builder builder = new FeatureVectorFieldMapper.Builder(name);
RankFeaturesFieldMapper.Builder builder = new RankFeaturesFieldMapper.Builder(name);
return builder;
}
}
public static final class FeatureVectorFieldType extends MappedFieldType {
public static final class RankFeaturesFieldType extends MappedFieldType {
public FeatureVectorFieldType() {
public RankFeaturesFieldType() {
setIndexAnalyzer(Lucene.KEYWORD_ANALYZER);
setSearchAnalyzer(Lucene.KEYWORD_ANALYZER);
}
protected FeatureVectorFieldType(FeatureVectorFieldType ref) {
protected RankFeaturesFieldType(RankFeaturesFieldType ref) {
super(ref);
}
public FeatureVectorFieldType clone() {
return new FeatureVectorFieldType(this);
public RankFeaturesFieldType clone() {
return new RankFeaturesFieldType(this);
}
@Override
@ -104,44 +104,44 @@ public class FeatureVectorFieldMapper extends FieldMapper {
@Override
public Query existsQuery(QueryShardContext context) {
throw new UnsupportedOperationException("[feature_vector] fields do not support [exists] queries");
throw new UnsupportedOperationException("[rank_features] fields do not support [exists] queries");
}
@Override
public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName) {
throw new UnsupportedOperationException("[feature_vector] fields do not support sorting, scripting or aggregating");
throw new UnsupportedOperationException("[rank_features] fields do not support sorting, scripting or aggregating");
}
@Override
public Query termQuery(Object value, QueryShardContext context) {
throw new UnsupportedOperationException("Queries on [feature_vector] fields are not supported");
throw new UnsupportedOperationException("Queries on [rank_features] fields are not supported");
}
}
private FeatureVectorFieldMapper(String simpleName, MappedFieldType fieldType, MappedFieldType defaultFieldType,
private RankFeaturesFieldMapper(String simpleName, MappedFieldType fieldType, MappedFieldType defaultFieldType,
Settings indexSettings, MultiFields multiFields, CopyTo copyTo) {
super(simpleName, fieldType, defaultFieldType, indexSettings, multiFields, copyTo);
assert fieldType.indexOptions().compareTo(IndexOptions.DOCS_AND_FREQS) <= 0;
}
@Override
protected FeatureVectorFieldMapper clone() {
return (FeatureVectorFieldMapper) super.clone();
protected RankFeaturesFieldMapper clone() {
return (RankFeaturesFieldMapper) super.clone();
}
@Override
public FeatureVectorFieldType fieldType() {
return (FeatureVectorFieldType) super.fieldType();
public RankFeaturesFieldType fieldType() {
return (RankFeaturesFieldType) super.fieldType();
}
@Override
public void parse(ParseContext context) throws IOException {
if (context.externalValueSet()) {
throw new IllegalArgumentException("[feature_vector] fields can't be used in multi-fields");
throw new IllegalArgumentException("[rank_features] fields can't be used in multi-fields");
}
if (context.parser().currentToken() != Token.START_OBJECT) {
throw new IllegalArgumentException("[feature_vector] fields must be json objects, expected a START_OBJECT but got: " +
throw new IllegalArgumentException("[rank_features] fields must be json objects, expected a START_OBJECT but got: " +
context.parser().currentToken());
}
@ -155,12 +155,12 @@ public class FeatureVectorFieldMapper extends FieldMapper {
final String key = name() + "." + feature;
float value = context.parser().floatValue(true);
if (context.doc().getByKey(key) != null) {
throw new IllegalArgumentException("[feature_vector] fields do not support indexing multiple values for the same " +
"feature [" + key + "] in the same document");
throw new IllegalArgumentException("[rank_features] fields do not support indexing multiple values for the same " +
"rank feature [" + key + "] in the same document");
}
context.doc().addWithKey(key, new FeatureField(name(), feature, value));
} else {
throw new IllegalArgumentException("[feature_vector] fields take hashes that map a feature to a strictly positive " +
throw new IllegalArgumentException("[rank_features] fields take hashes that map a feature to a strictly positive " +
"float, but got unexpected token " + token);
}
}

View File

@ -27,9 +27,9 @@ import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.mapper.FeatureFieldMapper.FeatureFieldType;
import org.elasticsearch.index.mapper.FeatureMetaFieldMapper;
import org.elasticsearch.index.mapper.FeatureVectorFieldMapper.FeatureVectorFieldType;
import org.elasticsearch.index.mapper.RankFeatureFieldMapper.RankFeatureFieldType;
import org.elasticsearch.index.mapper.RankFeatureMetaFieldMapper;
import org.elasticsearch.index.mapper.RankFeaturesFieldMapper.RankFeaturesFieldType;
import org.elasticsearch.index.mapper.MappedFieldType;
import java.io.IOException;
@ -37,12 +37,12 @@ import java.util.Arrays;
import java.util.Objects;
/**
* Query to run on a [feature] field.
* Query to run on a [rank_feature] field.
*/
public final class FeatureQueryBuilder extends AbstractQueryBuilder<FeatureQueryBuilder> {
public final class RankFeatureQueryBuilder extends AbstractQueryBuilder<RankFeatureQueryBuilder> {
/**
* Scoring function for a [feature] field.
* Scoring function for a [rank_feature] field.
*/
public abstract static class ScoreFunction {
@ -260,23 +260,23 @@ public final class FeatureQueryBuilder extends AbstractQueryBuilder<FeatureQuery
}
}
public static ConstructingObjectParser<FeatureQueryBuilder, Void> PARSER = new ConstructingObjectParser<>(
public static ConstructingObjectParser<RankFeatureQueryBuilder, Void> PARSER = new ConstructingObjectParser<>(
"feature", args -> {
final String field = (String) args[0];
final float boost = args[1] == null ? DEFAULT_BOOST : (Float) args[1];
final String queryName = (String) args[2];
long numNonNulls = Arrays.stream(args, 3, args.length).filter(Objects::nonNull).count();
final FeatureQueryBuilder query;
final RankFeatureQueryBuilder query;
if (numNonNulls > 1) {
throw new IllegalArgumentException("Can only specify one of [log], [saturation] and [sigmoid]");
} else if (numNonNulls == 0) {
query = new FeatureQueryBuilder(field, new ScoreFunction.Saturation());
query = new RankFeatureQueryBuilder(field, new ScoreFunction.Saturation());
} else {
ScoreFunction scoreFunction = (ScoreFunction) Arrays.stream(args, 3, args.length)
.filter(Objects::nonNull)
.findAny()
.get();
query = new FeatureQueryBuilder(field, scoreFunction);
query = new RankFeatureQueryBuilder(field, scoreFunction);
}
query.boost(boost);
query.queryName(queryName);
@ -294,17 +294,17 @@ public final class FeatureQueryBuilder extends AbstractQueryBuilder<FeatureQuery
ScoreFunction.Sigmoid.PARSER, new ParseField("sigmoid"));
}
public static final String NAME = "feature";
public static final String NAME = "rank_feature";
private final String field;
private final ScoreFunction scoreFunction;
public FeatureQueryBuilder(String field, ScoreFunction scoreFunction) {
public RankFeatureQueryBuilder(String field, ScoreFunction scoreFunction) {
this.field = Objects.requireNonNull(field);
this.scoreFunction = Objects.requireNonNull(scoreFunction);
}
public FeatureQueryBuilder(StreamInput in) throws IOException {
public RankFeatureQueryBuilder(StreamInput in) throws IOException {
super(in);
this.field = in.readString();
this.scoreFunction = readScoreFunction(in);
@ -334,27 +334,27 @@ public final class FeatureQueryBuilder extends AbstractQueryBuilder<FeatureQuery
protected Query doToQuery(QueryShardContext context) throws IOException {
final MappedFieldType ft = context.fieldMapper(field);
if (ft instanceof FeatureFieldType) {
final FeatureFieldType fft = (FeatureFieldType) ft;
return scoreFunction.toQuery(FeatureMetaFieldMapper.NAME, field, fft.positiveScoreImpact());
if (ft instanceof RankFeatureFieldType) {
final RankFeatureFieldType fft = (RankFeatureFieldType) ft;
return scoreFunction.toQuery(RankFeatureMetaFieldMapper.NAME, field, fft.positiveScoreImpact());
} else if (ft == null) {
final int lastDotIndex = field.lastIndexOf('.');
if (lastDotIndex != -1) {
final String parentField = field.substring(0, lastDotIndex);
final MappedFieldType parentFt = context.fieldMapper(parentField);
if (parentFt instanceof FeatureVectorFieldType) {
if (parentFt instanceof RankFeaturesFieldType) {
return scoreFunction.toQuery(parentField, field.substring(lastDotIndex + 1), true);
}
}
return new MatchNoDocsQuery(); // unmapped field
} else {
throw new IllegalArgumentException("[feature] query only works on [feature] fields and features of [feature_vector] fields, " +
"not [" + ft.typeName() + "]");
throw new IllegalArgumentException("[rank_feature] query only works on [rank_feature] fields and " +
"features of [rank_features] fields, not [" + ft.typeName() + "]");
}
}
@Override
protected boolean doEquals(FeatureQueryBuilder other) {
protected boolean doEquals(RankFeatureQueryBuilder other) {
return Objects.equals(field, other.field) && Objects.equals(scoreFunction, other.scoreFunction);
}

View File

@ -38,7 +38,7 @@ import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
public class FeatureFieldMapperTests extends ESSingleNodeTestCase {
public class RankFeatureFieldMapperTests extends ESSingleNodeTestCase {
IndexService indexService;
DocumentMapperParser parser;
@ -65,7 +65,7 @@ public class FeatureFieldMapperTests extends ESSingleNodeTestCase {
public void testDefaults() throws Exception {
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
.startObject("properties").startObject("field").field("type", "feature").endObject().endObject()
.startObject("properties").startObject("field").field("type", "rank_feature").endObject().endObject()
.endObject().endObject());
DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
@ -100,7 +100,7 @@ public class FeatureFieldMapperTests extends ESSingleNodeTestCase {
public void testNegativeScoreImpact() throws Exception {
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
.startObject("properties").startObject("field").field("type", "feature")
.startObject("properties").startObject("field").field("type", "rank_feature")
.field("positive_score_impact", false).endObject().endObject()
.endObject().endObject());
@ -136,8 +136,8 @@ public class FeatureFieldMapperTests extends ESSingleNodeTestCase {
public void testRejectMultiValuedFields() throws MapperParsingException, IOException {
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
.startObject("properties").startObject("field").field("type", "feature").endObject().startObject("foo")
.startObject("properties").startObject("field").field("type", "feature").endObject().endObject()
.startObject("properties").startObject("field").field("type", "rank_feature").endObject().startObject("foo")
.startObject("properties").startObject("field").field("type", "rank_feature").endObject().endObject()
.endObject().endObject().endObject().endObject());
DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
@ -151,7 +151,7 @@ public class FeatureFieldMapperTests extends ESSingleNodeTestCase {
.field("field", Arrays.asList(10, 20))
.endObject()),
XContentType.JSON)));
assertEquals("[feature] fields do not support indexing multiple values for the same field [field] in the same document",
assertEquals("[rank_feature] fields do not support indexing multiple values for the same field [field] in the same document",
e.getCause().getMessage());
e = expectThrows(MapperParsingException.class,
@ -168,7 +168,7 @@ public class FeatureFieldMapperTests extends ESSingleNodeTestCase {
.endArray()
.endObject()),
XContentType.JSON)));
assertEquals("[feature] fields do not support indexing multiple values for the same field [foo.field] in the same document",
assertEquals("[rank_feature] fields do not support indexing multiple values for the same field [foo.field] in the same document",
e.getCause().getMessage());
}
}

View File

@ -21,11 +21,11 @@ package org.elasticsearch.index.mapper;
import org.junit.Before;
public class FeatureFieldTypeTests extends FieldTypeTestCase {
public class RankFeatureFieldTypeTests extends FieldTypeTestCase {
@Override
protected MappedFieldType createDefaultFieldType() {
return new FeatureFieldMapper.FeatureFieldType();
return new RankFeatureFieldMapper.RankFeatureFieldType();
}
@Before
@ -33,13 +33,13 @@ public class FeatureFieldTypeTests extends FieldTypeTestCase {
addModifier(new Modifier("positive_score_impact", false) {
@Override
public void modify(MappedFieldType ft) {
FeatureFieldMapper.FeatureFieldType tft = (FeatureFieldMapper.FeatureFieldType)ft;
RankFeatureFieldMapper.RankFeatureFieldType tft = (RankFeatureFieldMapper.RankFeatureFieldType)ft;
tft.setPositiveScoreImpact(tft.positiveScoreImpact() == false);
}
@Override
public void normalizeOther(MappedFieldType other) {
super.normalizeOther(other);
((FeatureFieldMapper.FeatureFieldType) other).setPositiveScoreImpact(true);
((RankFeatureFieldMapper.RankFeatureFieldType) other).setPositiveScoreImpact(true);
}
});
}

View File

@ -29,7 +29,7 @@ import org.junit.Before;
import java.util.Collection;
public class FeatureMetaFieldMapperTests extends ESSingleNodeTestCase {
public class RankFeatureMetaFieldMapperTests extends ESSingleNodeTestCase {
IndexService indexService;
DocumentMapperParser parser;
@ -47,12 +47,12 @@ public class FeatureMetaFieldMapperTests extends ESSingleNodeTestCase {
public void testBasics() throws Exception {
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
.startObject("properties").startObject("field").field("type", "feature").endObject().endObject()
.startObject("properties").startObject("field").field("type", "rank_feature").endObject().endObject()
.endObject().endObject());
DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
assertEquals(mapping, mapper.mappingSource().toString());
assertNotNull(mapper.metadataMapper(FeatureMetaFieldMapper.class));
assertNotNull(mapper.metadataMapper(RankFeatureMetaFieldMapper.class));
}
}

View File

@ -19,11 +19,11 @@
package org.elasticsearch.index.mapper;
public class FeatureVectorFieldTypeTests extends FieldTypeTestCase {
public class RankFeatureMetaFieldTypeTests extends FieldTypeTestCase {
@Override
protected MappedFieldType createDefaultFieldType() {
return new FeatureVectorFieldMapper.FeatureVectorFieldType();
return new RankFeatureMetaFieldMapper.RankFeatureMetaFieldType();
}
}

View File

@ -36,7 +36,7 @@ import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
public class FeatureVectorFieldMapperTests extends ESSingleNodeTestCase {
public class RankFeaturesFieldMapperTests extends ESSingleNodeTestCase {
IndexService indexService;
DocumentMapperParser parser;
@ -54,7 +54,7 @@ public class FeatureVectorFieldMapperTests extends ESSingleNodeTestCase {
public void testDefaults() throws Exception {
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
.startObject("properties").startObject("field").field("type", "feature_vector").endObject().endObject()
.startObject("properties").startObject("field").field("type", "rank_features").endObject().endObject()
.endObject().endObject());
DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
@ -79,15 +79,15 @@ public class FeatureVectorFieldMapperTests extends ESSingleNodeTestCase {
FeatureField featureField2 = (FeatureField) fields[1];
assertThat(featureField2.stringValue(), Matchers.equalTo("bar"));
int freq1 = FeatureFieldMapperTests.getFrequency(featureField1.tokenStream(null, null));
int freq2 = FeatureFieldMapperTests.getFrequency(featureField2.tokenStream(null, null));
int freq1 = RankFeatureFieldMapperTests.getFrequency(featureField1.tokenStream(null, null));
int freq2 = RankFeatureFieldMapperTests.getFrequency(featureField2.tokenStream(null, null));
assertTrue(freq1 < freq2);
}
public void testRejectMultiValuedFields() throws MapperParsingException, IOException {
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
.startObject("properties").startObject("field").field("type", "feature_vector").endObject().startObject("foo")
.startObject("properties").startObject("field").field("type", "feature_vector").endObject().endObject()
.startObject("properties").startObject("field").field("type", "rank_features").endObject().startObject("foo")
.startObject("properties").startObject("field").field("type", "rank_features").endObject().endObject()
.endObject().endObject().endObject().endObject());
DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
@ -103,7 +103,7 @@ public class FeatureVectorFieldMapperTests extends ESSingleNodeTestCase {
.endObject()
.endObject()),
XContentType.JSON)));
assertEquals("[feature_vector] fields take hashes that map a feature to a strictly positive float, but got unexpected token " +
assertEquals("[rank_features] fields take hashes that map a feature to a strictly positive float, but got unexpected token " +
"START_ARRAY", e.getCause().getMessage());
e = expectThrows(MapperParsingException.class,
@ -124,7 +124,7 @@ public class FeatureVectorFieldMapperTests extends ESSingleNodeTestCase {
.endArray()
.endObject()),
XContentType.JSON)));
assertEquals("[feature_vector] fields do not support indexing multiple values for the same feature [foo.field.bar] in the same " +
"document", e.getCause().getMessage());
assertEquals("[rank_features] fields do not support indexing multiple values for the same rank feature [foo.field.bar] in " +
"the same document", e.getCause().getMessage());
}
}

View File

@ -19,11 +19,11 @@
package org.elasticsearch.index.mapper;
public class FeatureMetaFieldTypeTests extends FieldTypeTestCase {
public class RankFeaturesFieldTypeTests extends FieldTypeTestCase {
@Override
protected MappedFieldType createDefaultFieldType() {
return new FeatureMetaFieldMapper.FeatureMetaFieldType();
return new RankFeaturesFieldMapper.RankFeaturesFieldType();
}
}

View File

@ -27,7 +27,7 @@ import org.elasticsearch.common.Strings;
import org.elasticsearch.common.compress.CompressedXContent;
import org.elasticsearch.index.mapper.MapperExtrasPlugin;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.query.FeatureQueryBuilder.ScoreFunction;
import org.elasticsearch.index.query.RankFeatureQueryBuilder.ScoreFunction;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.search.internal.SearchContext;
import org.elasticsearch.test.AbstractQueryTestCase;
@ -41,14 +41,14 @@ import java.util.List;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.Matchers.either;
public class FeatureQueryBuilderTests extends AbstractQueryTestCase<FeatureQueryBuilder> {
public class RankFeatureQueryBuilderTests extends AbstractQueryTestCase<RankFeatureQueryBuilder> {
@Override
protected void initializeAdditionalMappings(MapperService mapperService) throws IOException {
mapperService.merge("_doc", new CompressedXContent(Strings.toString(PutMappingRequest.buildFromSimplifiedDef("_doc",
"my_feature_field", "type=feature",
"my_negative_feature_field", "type=feature,positive_score_impact=false",
"my_feature_vector_field", "type=feature_vector"))), MapperService.MergeReason.MAPPING_UPDATE);
"my_feature_field", "type=rank_feature",
"my_negative_feature_field", "type=rank_feature,positive_score_impact=false",
"my_feature_vector_field", "type=rank_features"))), MapperService.MergeReason.MAPPING_UPDATE);
}
@Override
@ -57,7 +57,7 @@ public class FeatureQueryBuilderTests extends AbstractQueryTestCase<FeatureQuery
}
@Override
protected FeatureQueryBuilder doCreateTestQueryBuilder() {
protected RankFeatureQueryBuilder doCreateTestQueryBuilder() {
ScoreFunction function;
boolean mayUseNegativeField = true;
switch (random().nextInt(3)) {
@ -87,18 +87,18 @@ public class FeatureQueryBuilderTests extends AbstractQueryTestCase<FeatureQuery
}
final String field = randomFrom(fields);
return new FeatureQueryBuilder(field, function);
return new RankFeatureQueryBuilder(field, function);
}
@Override
protected void doAssertLuceneQuery(FeatureQueryBuilder queryBuilder, Query query, SearchContext context) throws IOException {
protected void doAssertLuceneQuery(RankFeatureQueryBuilder queryBuilder, Query query, SearchContext context) throws IOException {
Class<?> expectedClass = FeatureField.newSaturationQuery("", "", 1, 1).getClass();
assertThat(query, either(instanceOf(MatchNoDocsQuery.class)).or(instanceOf(expectedClass)));
}
public void testDefaultScoreFunction() throws IOException {
String query = "{\n" +
" \"feature\" : {\n" +
" \"rank_feature\" : {\n" +
" \"field\": \"my_feature_field\"\n" +
" }\n" +
"}";
@ -108,17 +108,18 @@ public class FeatureQueryBuilderTests extends AbstractQueryTestCase<FeatureQuery
public void testIllegalField() throws IOException {
String query = "{\n" +
" \"feature\" : {\n" +
" \"rank_feature\" : {\n" +
" \"field\": \"" + STRING_FIELD_NAME + "\"\n" +
" }\n" +
"}";
IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> parseQuery(query).toQuery(createShardContext()));
assertEquals("[feature] query only works on [feature] fields and features of [feature_vector] fields, not [text]", e.getMessage());
assertEquals("[rank_feature] query only works on [rank_feature] fields and features of [rank_features] fields, not [text]",
e.getMessage());
}
public void testIllegalCombination() throws IOException {
String query = "{\n" +
" \"feature\" : {\n" +
" \"rank_feature\" : {\n" +
" \"field\": \"my_negative_feature_field\",\n" +
" \"log\" : {\n" +
" \"scaling_factor\": 4.5\n" +

View File

@ -1,7 +1,7 @@
setup:
- skip:
version: " - 6.99.99"
reason: "The feature field/query was introduced in 7.0.0"
reason: "The rank feature field/query was introduced in 7.0.0"
- do:
indices.create:
@ -13,9 +13,9 @@ setup:
_doc:
properties:
pagerank:
type: feature
type: rank_feature
url_length:
type: feature
type: rank_feature
positive_score_impact: false
- do:
@ -47,7 +47,7 @@ setup:
rest_total_hits_as_int: true
body:
query:
feature:
rank_feature:
field: pagerank
log:
scaling_factor: 3
@ -69,7 +69,7 @@ setup:
rest_total_hits_as_int: true
body:
query:
feature:
rank_feature:
field: pagerank
saturation:
pivot: 20
@ -91,7 +91,7 @@ setup:
rest_total_hits_as_int: true
body:
query:
feature:
rank_feature:
field: pagerank
sigmoid:
pivot: 20
@ -115,7 +115,7 @@ setup:
rest_total_hits_as_int: true
body:
query:
feature:
rank_feature:
field: url_length
log:
scaling_factor: 3
@ -128,7 +128,7 @@ setup:
rest_total_hits_as_int: true
body:
query:
feature:
rank_feature:
field: url_length
saturation:
pivot: 20
@ -150,7 +150,7 @@ setup:
rest_total_hits_as_int: true
body:
query:
feature:
rank_feature:
field: url_length
sigmoid:
pivot: 20

View File

@ -1,7 +1,7 @@
setup:
- skip:
version: " - 6.99.99"
reason: "The feature_vector field was introduced in 7.0.0"
reason: "The rank_features field was introduced in 7.0.0"
- do:
indices.create:
@ -13,7 +13,7 @@ setup:
_doc:
properties:
tags:
type: feature_vector
type: rank_features
- do:
index:
@ -46,7 +46,7 @@ setup:
rest_total_hits_as_int: true
body:
query:
feature:
rank_feature:
field: tags.bar
log:
scaling_factor: 3
@ -68,7 +68,7 @@ setup:
rest_total_hits_as_int: true
body:
query:
feature:
rank_feature:
field: tags.bar
saturation:
pivot: 20
@ -90,7 +90,7 @@ setup:
rest_total_hits_as_int: true
body:
query:
feature:
rank_feature:
field: tags.bar
sigmoid:
pivot: 20