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:
parent
9d87ca567a
commit
a30ce6a00a
|
@ -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[]
|
||||
|
||||
|
|
|
@ -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.
|
|
@ -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.
|
|
@ -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%.
|
||||
|
|
@ -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,
|
|
@ -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
|
||||
|
|
|
@ -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[]
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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) {
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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" +
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue