Skip to content

Commit

Permalink
SemanticTextFieldMapper implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
carlosdelest committed Dec 4, 2023
1 parent ffd86f7 commit 085751e
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,14 @@ public List<String> dimensions() {
return Collections.emptyList();
}

public String getInferenceModel() {
return null;
}

public final boolean hasInferenceModel() {
return getInferenceModel() != null;
}

/**
* @return metric type or null if the field is not a metric field
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import org.elasticsearch.env.Environment;
import org.elasticsearch.index.analysis.CharFilterFactory;
import org.elasticsearch.index.analysis.TokenizerFactory;
import org.elasticsearch.index.mapper.Mapper;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.indices.AssociatedIndexDescriptor;
import org.elasticsearch.indices.SystemIndexDescriptor;
Expand All @@ -65,6 +66,7 @@
import org.elasticsearch.plugins.CircuitBreakerPlugin;
import org.elasticsearch.plugins.ExtensiblePlugin;
import org.elasticsearch.plugins.IngestPlugin;
import org.elasticsearch.plugins.MapperPlugin;
import org.elasticsearch.plugins.PersistentTaskPlugin;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.plugins.SearchPlugin;
Expand Down Expand Up @@ -359,6 +361,7 @@
import org.elasticsearch.xpack.ml.job.process.normalizer.NormalizerProcessFactory;
import org.elasticsearch.xpack.ml.job.snapshot.upgrader.SnapshotUpgradeTaskExecutor;
import org.elasticsearch.xpack.ml.job.task.OpenJobPersistentTasksExecutor;
import org.elasticsearch.xpack.ml.mapper.SemanticTextFieldMapper;
import org.elasticsearch.xpack.ml.notifications.AnomalyDetectionAuditor;
import org.elasticsearch.xpack.ml.notifications.DataFrameAnalyticsAuditor;
import org.elasticsearch.xpack.ml.notifications.InferenceAuditor;
Expand Down Expand Up @@ -476,7 +479,8 @@ public class MachineLearning extends Plugin
PersistentTaskPlugin,
SearchPlugin,
ShutdownAwarePlugin,
ExtensiblePlugin {
ExtensiblePlugin,
MapperPlugin {
public static final String NAME = "ml";
public static final String BASE_PATH = "/_ml/";
// Endpoints that were deprecated in 7.x can still be called in 8.x using the REST compatibility layer
Expand Down Expand Up @@ -2288,4 +2292,9 @@ public void signalShutdown(Collection<String> shutdownNodeIds) {
mlLifeCycleService.get().signalGracefulShutdown(shutdownNodeIds);
}
}

@Override
public Map<String, Mapper.TypeParser> getMappers() {
return Map.of(SemanticTextFieldMapper.CONTENT_TYPE, SemanticTextFieldMapper.PARSER);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

package org.elasticsearch.xpack.ml.mapper;

import org.apache.lucene.search.Query;
import org.elasticsearch.common.Strings;
import org.elasticsearch.index.mapper.DocumentParserContext;
import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.mapper.MapperBuilderContext;
import org.elasticsearch.index.mapper.SimpleMappedFieldType;
import org.elasticsearch.index.mapper.SourceValueFetcher;
import org.elasticsearch.index.mapper.TextSearchInfo;
import org.elasticsearch.index.mapper.ValueFetcher;
import org.elasticsearch.index.mapper.vectors.SparseVectorFieldMapper;
import org.elasticsearch.index.query.SearchExecutionContext;

import java.io.IOException;
import java.util.Map;

/** A {@link FieldMapper} for full-text fields. */
public class SemanticTextFieldMapper extends FieldMapper {

public static final String CONTENT_TYPE = "semantic_text";

private static SemanticTextFieldMapper toType(FieldMapper in) {
return (SemanticTextFieldMapper) in;
}

public static class Builder extends FieldMapper.Builder {

final Parameter<String> modelId = Parameter.stringParam("model_id", false, m -> toType(m).modelId, null).addValidator(v -> {
if (Strings.isEmpty(v)) {
throw new IllegalArgumentException("field [model_id] must be specified");
}
});

private final Parameter<Map<String, String>> meta = Parameter.metaParam();

public Builder(String name) {
super(name);
}

public Builder modelId(String modelId) {
this.modelId.setValue(modelId);
return this;
}

@Override
protected Parameter<?>[] getParameters() {
return new Parameter<?>[] { modelId, meta };
}

@Override
public SemanticTextFieldMapper build(MapperBuilderContext context) {
return new SemanticTextFieldMapper(
name(),
new SemanticTextFieldType(name(), modelId.getValue(), meta.getValue()),
modelId.getValue(),
copyTo
);
}
}

public static final TypeParser PARSER = new TypeParser((n, c) -> new Builder(n), notInMultiFields(CONTENT_TYPE));

public static class SemanticTextFieldType extends SimpleMappedFieldType {

private final SparseVectorFieldMapper.SparseVectorFieldType sparseVectorFieldType;

private final String modelId;

public SemanticTextFieldType(String name, String modelId, Map<String, String> meta) {
super(name, true, false, false, TextSearchInfo.NONE, meta);
this.sparseVectorFieldType = new SparseVectorFieldMapper.SparseVectorFieldType(name + "." + "inference", meta);
this.modelId = modelId;
}

@Override
public String typeName() {
return CONTENT_TYPE;
}

@Override
public String getInferenceModel() {
return modelId;
}

@Override
public Query termQuery(Object value, SearchExecutionContext context) {
return null;
}

@Override
public ValueFetcher valueFetcher(SearchExecutionContext context, String format) {
return SourceValueFetcher.identity(name(), context, format);
}
}

private final String modelId;

private SemanticTextFieldMapper(String simpleName, MappedFieldType mappedFieldType, String modelId, CopyTo copyTo) {
super(simpleName, mappedFieldType, MultiFields.empty(), copyTo);
this.modelId = modelId;
}

public String getModelId() {
return modelId;
}

@Override
public FieldMapper.Builder getMergeBuilder() {
return new Builder(simpleName()).init(this);
}

@Override
protected void parseCreateField(DocumentParserContext context) throws IOException {
context.parser().textOrNull();
}

@Override
protected String contentType() {
return CONTENT_TYPE;
}

@Override
public SemanticTextFieldType fieldType() {
return (SemanticTextFieldType) super.fieldType();
}
}

0 comments on commit 085751e

Please sign in to comment.