/* 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. */ /* * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. * * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch B.V. licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ using System.Runtime.Serialization; using OpenSearch.Net.Utf8Json; using OpenSearch.Net.Utf8Json.Internal; namespace OpenSearch.Client { /// /// Boosts the relevance score of documents closer to a provided origin date or point. For example, you can use this query to give /// more weight to documents closer to a certain date or location. /// You can use the distance_feature query to find the nearest neighbors to a location. You can also use the query in a bool /// search’s should filter to add boosted relevance scores to the bool query’s scores. /// [JsonFormatter(typeof(DistanceFeatureQueryFormatter))] [InterfaceDataContract] public interface IDistanceFeatureQuery : IFieldNameQuery { /// /// Date or point of origin used to calculate distances. /// If the field value is a date or date_nanos field, the origin value must be a date. Date Math, such as now-1h, is supported. /// If the field value is a geo_point field, the origin value must be a geopoint. /// [DataMember(Name = "origin")] Union Origin { get; set; } /// /// Distance from the origin at which relevance scores receive half of the boost value. /// If the field value is a date or date_nanos field, the pivot value must be a time unit, such as 1h or 10d. /// If the field value is a geo_point field, the pivot value must be a distance unit, such as 1km or 12m. /// [DataMember(Name = "pivot")] Union Pivot { get; set; } } /// public class DistanceFeatureQuery : FieldNameQueryBase, IDistanceFeatureQuery { protected override bool Conditionless => IsConditionless(this); internal static bool IsConditionless(IDistanceFeatureQuery q) => q.Field.IsConditionless() || q.Origin == null && q.Pivot == null; internal override void InternalWrapInContainer(IQueryContainer container) => container.DistanceFeature = this; /// public Union Origin { get; set; } /// public Union Pivot { get; set; } } public class DistanceFeatureQueryDescriptor : FieldNameQueryDescriptorBase, IDistanceFeatureQuery, T> , IDistanceFeatureQuery where T : class { Union IDistanceFeatureQuery.Origin { get; set; } Union IDistanceFeatureQuery.Pivot { get; set; } protected override bool Conditionless => DistanceFeatureQuery.IsConditionless(this); /// public DistanceFeatureQueryDescriptor Origin(DateMath origin) => Assign(origin, (a, v) => a.Origin = v); /// public DistanceFeatureQueryDescriptor Origin(GeoCoordinate origin) => Assign(origin, (a, v) => a.Origin = v); /// public DistanceFeatureQueryDescriptor Pivot(Time pivot) => Assign(pivot, (a, v) => a.Pivot = v); /// public DistanceFeatureQueryDescriptor Pivot(Distance pivot) => Assign(pivot, (a, v) => a.Pivot = v); } internal class DistanceFeatureQueryFormatter : IJsonFormatter { private static readonly UnionFormatter OriginUnionFormatter = new UnionFormatter(true); private static readonly UnionFormatter PivotUnionFormatter = new UnionFormatter(); public void Serialize(ref JsonWriter writer, IDistanceFeatureQuery value, IJsonFormatterResolver formatterResolver) { if (value == null) { writer.WriteNull(); return; } writer.WriteBeginObject(); if (!string.IsNullOrEmpty(value.Name)) { writer.WritePropertyName("_name"); writer.WriteString(value.Name); writer.WriteValueSeparator(); } if (value.Boost.HasValue) { writer.WritePropertyName("boost"); writer.WriteDouble(value.Boost.Value); writer.WriteValueSeparator(); } writer.WritePropertyName("field"); var fieldFormatter = formatterResolver.GetFormatter(); fieldFormatter.Serialize(ref writer, value.Field, formatterResolver); writer.WriteValueSeparator(); writer.WritePropertyName("origin"); OriginUnionFormatter.Serialize(ref writer, value.Origin, formatterResolver); writer.WriteValueSeparator(); writer.WritePropertyName("pivot"); PivotUnionFormatter.Serialize(ref writer, value.Pivot, formatterResolver); writer.WriteEndObject(); } private static readonly AutomataDictionary Fields = new AutomataDictionary { { "field", 0 }, { "origin", 1 }, { "pivot", 2 }, { "boost", 3 }, { "_name", 4 } }; public IDistanceFeatureQuery Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver) { if (reader.ReadIsNull()) return null; var query = new DistanceFeatureQuery(); var count = 0; while (reader.ReadIsInObject(ref count)) { if (Fields.TryGetValue(reader.ReadPropertyNameSegmentRaw(), out var value)) { switch (value) { case 0: query.Field = formatterResolver.GetFormatter().Deserialize(ref reader, formatterResolver); break; case 1: query.Origin = OriginUnionFormatter.Deserialize(ref reader, formatterResolver); break; case 2: query.Pivot = PivotUnionFormatter.Deserialize(ref reader, formatterResolver); break; case 3: query.Boost = reader.ReadDouble(); break; case 4: query.Name = reader.ReadString(); break; } } else reader.ReadNextBlock(); } return query; } } }