Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- Implement GRPC ConstantScoreQuery, FuzzyQuery, MatchBoolPrefixQuery, MatchPhrasePrefix, PrefixQuery, MatchQuery ([#19854](https://github.com/opensearch-project/OpenSearch/pull/19854))
- Add async periodic flush task support for pull-based ingestion ([#19878](https://github.com/opensearch-project/OpenSearch/pull/19878))
- Add support for context aware segments ([#19098](https://github.com/opensearch-project/OpenSearch/pull/19098))
- Implement GRPC FunctionScoreQuery ([#19888](https://github.com/opensearch-project/OpenSearch/pull/19888))

### Changed
- Faster `terms` query creation for `keyword` field with index and docValues enabled ([#19350](https://github.com/opensearch-project/OpenSearch/pull/19350))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.opensearch.common.inject.Singleton;
import org.opensearch.index.query.QueryBuilder;
import org.opensearch.protobufs.QueryContainer;
import org.opensearch.transport.grpc.proto.request.search.query.functionscore.FunctionScoreQueryBuilderProtoConverter;
import org.opensearch.transport.grpc.spi.QueryBuilderProtoConverter;
import org.opensearch.transport.grpc.spi.QueryBuilderProtoConverterRegistry;

Expand Down Expand Up @@ -67,6 +68,7 @@ protected void registerBuiltInConverters() {
delegate.registerConverter(new MatchQueryBuilderProtoConverter());
delegate.registerConverter(new MatchBoolPrefixQueryBuilderProtoConverter());
delegate.registerConverter(new MatchPhrasePrefixQueryBuilderProtoConverter());
delegate.registerConverter(new FunctionScoreQueryBuilderProtoConverter());

// Set the registry on all converters so they can access each other
delegate.setRegistryOnAllConverters(this);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/
package org.opensearch.transport.grpc.proto.request.search.query.functionscore;

import org.opensearch.common.geo.GeoPoint;
import org.opensearch.index.query.functionscore.ExponentialDecayFunctionBuilder;
import org.opensearch.index.query.functionscore.ScoreFunctionBuilder;
import org.opensearch.protobufs.DateDecayPlacement;
import org.opensearch.protobufs.DecayFunction;
import org.opensearch.protobufs.DecayPlacement;
import org.opensearch.protobufs.GeoDecayPlacement;
import org.opensearch.protobufs.NumericDecayPlacement;
import org.opensearch.transport.grpc.proto.request.common.GeoPointProtoUtils;

import java.util.Map;

/**
* Utility class for converting Protocol Buffer DecayFunction to OpenSearch ExponentialDecayFunctionBuilder.
* This utility handles the transformation of Protocol Buffer DecayFunction objects
* into OpenSearch ExponentialDecayFunctionBuilder instances.
*/
class ExpDecayFunctionProtoUtils {

private ExpDecayFunctionProtoUtils() {
// Utility class, no instances
}

/**
* Converts a Protocol Buffer DecayFunction to an OpenSearch ScoreFunctionBuilder.
* Similar to {@link org.opensearch.index.query.functionscore.DecayFunctionParser#fromXContent(org.opensearch.core.xcontent.XContentParser)},
* this method parses the Protocol Buffer representation and creates a properly configured
* ExponentialDecayFunctionBuilder with decay placement parameters (numeric, geo, or date).
*
* @param decayFunction the Protocol Buffer DecayFunction
* @return the corresponding OpenSearch ScoreFunctionBuilder
* @throws IllegalArgumentException if the decayFunction is null or doesn't contain placements
*/
static ScoreFunctionBuilder<?> fromProto(DecayFunction decayFunction) {
if (decayFunction == null || decayFunction.getPlacementCount() == 0) {
throw new IllegalArgumentException("DecayFunction must have at least one placement");
}

Map.Entry<String, DecayPlacement> entry = decayFunction.getPlacementMap().entrySet().iterator().next();
String fieldName = entry.getKey();
DecayPlacement decayPlacement = entry.getValue();

if (decayPlacement.hasNumericDecayPlacement()) {
return parseNumericExpDecay(fieldName, decayPlacement.getNumericDecayPlacement());
} else if (decayPlacement.hasGeoDecayPlacement()) {
return parseGeoExpDecay(fieldName, decayPlacement.getGeoDecayPlacement());
} else if (decayPlacement.hasDateDecayPlacement()) {
return parseDateExpDecay(fieldName, decayPlacement.getDateDecayPlacement());
} else {
throw new IllegalArgumentException("Unsupported decay placement type");
}
}

/**
* Parses a numeric decay placement for exponential decay.
*/
private static ScoreFunctionBuilder<?> parseNumericExpDecay(String fieldName, NumericDecayPlacement numericPlacement) {
ExponentialDecayFunctionBuilder builder;
if (numericPlacement.hasDecay()) {
builder = new ExponentialDecayFunctionBuilder(
fieldName,
numericPlacement.getOrigin(),
numericPlacement.getScale(),
numericPlacement.hasOffset() ? numericPlacement.getOffset() : null,
numericPlacement.getDecay()
);
} else {
builder = new ExponentialDecayFunctionBuilder(
fieldName,
numericPlacement.getOrigin(),
numericPlacement.getScale(),
numericPlacement.hasOffset() ? numericPlacement.getOffset() : null
);
}

return builder;
}

/**
* Parses a geo decay placement for exponential decay.
*/
private static ScoreFunctionBuilder<?> parseGeoExpDecay(String fieldName, GeoDecayPlacement geoPlacement) {
GeoPoint geoPoint = GeoPointProtoUtils.parseGeoPoint(geoPlacement.getOrigin());

ExponentialDecayFunctionBuilder builder;
if (geoPlacement.hasDecay()) {
builder = new ExponentialDecayFunctionBuilder(
fieldName,
geoPoint,
geoPlacement.getScale(),
geoPlacement.hasOffset() ? geoPlacement.getOffset() : null,
geoPlacement.getDecay()
);
} else {
builder = new ExponentialDecayFunctionBuilder(
fieldName,
geoPoint,
geoPlacement.getScale(),
geoPlacement.hasOffset() ? geoPlacement.getOffset() : null
);
}

return builder;
}

/**
* Parses a date decay placement for exponential decay.
*/
private static ScoreFunctionBuilder<?> parseDateExpDecay(String fieldName, DateDecayPlacement datePlacement) {
Object origin = datePlacement.hasOrigin() ? datePlacement.getOrigin() : null;

ExponentialDecayFunctionBuilder builder;
if (datePlacement.hasDecay()) {
builder = new ExponentialDecayFunctionBuilder(
fieldName,
origin,
datePlacement.getScale(),
datePlacement.hasOffset() ? datePlacement.getOffset() : null,
datePlacement.getDecay()
);
} else {
builder = new ExponentialDecayFunctionBuilder(
fieldName,
origin,
datePlacement.getScale(),
datePlacement.hasOffset() ? datePlacement.getOffset() : null
);
}

return builder;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/
package org.opensearch.transport.grpc.proto.request.search.query.functionscore;

import org.opensearch.index.query.functionscore.FieldValueFactorFunctionBuilder;
import org.opensearch.index.query.functionscore.ScoreFunctionBuilder;
import org.opensearch.protobufs.FieldValueFactorModifier;
import org.opensearch.protobufs.FieldValueFactorScoreFunction;

/**
* Utility class for converting Protocol Buffer FieldValueFactorScoreFunction to OpenSearch objects.
* This utility handles the transformation of Protocol Buffer FieldValueFactorScoreFunction objects
* into OpenSearch FieldValueFactorFunctionBuilder instances.
*/
class FieldValueFactorFunctionProtoUtils {

private FieldValueFactorFunctionProtoUtils() {
// Utility class, no instances
}

/**
* Converts a Protocol Buffer FieldValueFactorScoreFunction to an OpenSearch ScoreFunctionBuilder.
* Similar to {@link FieldValueFactorFunctionBuilder#fromXContent(XContentParser)}, this method
* parses the field, factor, missing value, and modifier parameters.
*
* @param fieldValueFactor the Protocol Buffer FieldValueFactorScoreFunction
* @return the corresponding OpenSearch ScoreFunctionBuilder
* @throws IllegalArgumentException if the fieldValueFactor is null
*/
static ScoreFunctionBuilder<?> fromProto(FieldValueFactorScoreFunction fieldValueFactor) {
if (fieldValueFactor == null) {
throw new IllegalArgumentException("FieldValueFactorScoreFunction cannot be null");
}

FieldValueFactorFunctionBuilder builder = new FieldValueFactorFunctionBuilder(fieldValueFactor.getField());

if (fieldValueFactor.hasFactor()) {
builder.factor(fieldValueFactor.getFactor());
}

if (fieldValueFactor.hasMissing()) {
builder.missing(fieldValueFactor.getMissing());
}

if (fieldValueFactor.getModifier() != FieldValueFactorModifier.FIELD_VALUE_FACTOR_MODIFIER_NONE) {
builder.modifier(parseFieldValueFactorModifier(fieldValueFactor.getModifier()));
}

return builder;
}

/**
* Parses a protobuf FieldValueFactorModifier and returns the corresponding OpenSearch modifier.
*
* @param modifier the protobuf FieldValueFactorModifier
* @return the corresponding OpenSearch FieldValueFactorFunction.Modifier
*/
private static org.opensearch.common.lucene.search.function.FieldValueFactorFunction.Modifier parseFieldValueFactorModifier(
FieldValueFactorModifier modifier
) {
return switch (modifier) {
case FIELD_VALUE_FACTOR_MODIFIER_NONE -> org.opensearch.common.lucene.search.function.FieldValueFactorFunction.Modifier.NONE;
case FIELD_VALUE_FACTOR_MODIFIER_LOG -> org.opensearch.common.lucene.search.function.FieldValueFactorFunction.Modifier.LOG;
case FIELD_VALUE_FACTOR_MODIFIER_LOG1P -> org.opensearch.common.lucene.search.function.FieldValueFactorFunction.Modifier.LOG1P;
case FIELD_VALUE_FACTOR_MODIFIER_LOG2P -> org.opensearch.common.lucene.search.function.FieldValueFactorFunction.Modifier.LOG2P;
case FIELD_VALUE_FACTOR_MODIFIER_LN -> org.opensearch.common.lucene.search.function.FieldValueFactorFunction.Modifier.LN;
case FIELD_VALUE_FACTOR_MODIFIER_LN1P -> org.opensearch.common.lucene.search.function.FieldValueFactorFunction.Modifier.LN1P;
case FIELD_VALUE_FACTOR_MODIFIER_LN2P -> org.opensearch.common.lucene.search.function.FieldValueFactorFunction.Modifier.LN2P;
case FIELD_VALUE_FACTOR_MODIFIER_SQUARE ->
org.opensearch.common.lucene.search.function.FieldValueFactorFunction.Modifier.SQUARE;
case FIELD_VALUE_FACTOR_MODIFIER_SQRT -> org.opensearch.common.lucene.search.function.FieldValueFactorFunction.Modifier.SQRT;
case FIELD_VALUE_FACTOR_MODIFIER_RECIPROCAL ->
org.opensearch.common.lucene.search.function.FieldValueFactorFunction.Modifier.RECIPROCAL;
case FIELD_VALUE_FACTOR_MODIFIER_UNSPECIFIED ->
org.opensearch.common.lucene.search.function.FieldValueFactorFunction.Modifier.NONE;
default -> throw new IllegalArgumentException("Unknown FieldValueFactorModifier: " + modifier);
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/
package org.opensearch.transport.grpc.proto.request.search.query.functionscore;

import org.opensearch.index.query.QueryBuilder;
import org.opensearch.protobufs.QueryContainer;
import org.opensearch.transport.grpc.spi.QueryBuilderProtoConverter;
import org.opensearch.transport.grpc.spi.QueryBuilderProtoConverterRegistry;

/**
* Converter for FunctionScore queries.
* This class implements the QueryBuilderProtoConverter interface to provide FunctionScore query support
* for the gRPC transport module.
*/
public class FunctionScoreQueryBuilderProtoConverter implements QueryBuilderProtoConverter {

/**
* Default constructor for FunctionScoreQueryBuilderProtoConverter.
*/
public FunctionScoreQueryBuilderProtoConverter() {}

private QueryBuilderProtoConverterRegistry registry;

@Override
public void setRegistry(QueryBuilderProtoConverterRegistry registry) {
this.registry = registry;
}

@Override
public QueryContainer.QueryContainerCase getHandledQueryCase() {
return QueryContainer.QueryContainerCase.FUNCTION_SCORE;
}

@Override
public QueryBuilder fromProto(QueryContainer queryContainer) {
if (queryContainer == null || !queryContainer.hasFunctionScore()) {
throw new IllegalArgumentException("QueryContainer does not contain a FunctionScore query");
}

return FunctionScoreQueryBuilderProtoUtils.fromProto(queryContainer.getFunctionScore(), registry);
}
}
Loading
Loading