/* 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; using System.Collections.Generic; using System.Runtime.Serialization; using OpenSearch.Net.Utf8Json; namespace OpenSearch.Client { [InterfaceDataContract] [ReadAs(typeof(TypeMapping))] public interface ITypeMapping { /// <summary> /// If enabled (default), then new string fields are checked to see whether their contents match /// any of the date patterns specified in <see cref="DynamicDateFormats"/>. /// If a match is found, a new date field is added with the corresponding format. /// </summary> [DataMember(Name = "date_detection")] bool? DateDetection { get; set; } /// <summary> /// Whether new unseen fields will be added to the mapping. Default is <c>true</c>. /// A value of <c>false</c> will ignore unknown fields and a value of <see cref="DynamicMapping.Strict"/> /// will result in an error if an unknown field is encountered in a document. /// </summary> [DataMember(Name = "dynamic")] [JsonFormatter(typeof(DynamicMappingFormatter))] Union<bool, DynamicMapping> Dynamic { get; set; } /// <summary> /// Date formats used by <see cref="DateDetection"/> /// </summary> [DataMember(Name = "dynamic_date_formats")] IEnumerable<string> DynamicDateFormats { get; set; } /// <summary> /// Dynamic templates allow you to define custom mappings that can be applied to dynamically added fields based on /// <para>- the datatype detected by OpenSearch, with <see cref="IDynamicTemplate.MatchMappingType"/>.</para> /// <para>- the name of the field, with <see cref="IDynamicTemplate.Match"/> and <see cref="IDynamicTemplate.Unmatch"/> or /// <see cref="IDynamicTemplate.MatchPattern"/>.</para> /// <para>- the full dotted path to the field, with <see cref="IDynamicTemplate.PathMatch"/> and /// <see cref="IDynamicTemplate.PathUnmatch"/>.</para> /// <para>The original field name <c>{name}</c> and the detected datatype <c>{dynamic_type}</c> template variables can be /// used in the mapping specification as placeholders.</para> /// </summary> [DataMember(Name = "dynamic_templates")] IDynamicTemplateContainer DynamicTemplates { get; set; } /// <summary> /// Used to index the names of every field in a document that contains any value other than null. /// This field was used by the exists query to find documents that either have or don’t have any non-null value for a particular field. /// Now, it only indexes the names of fields that have doc_values and norms disabled. /// Can be disabled. Disabling _field_names is often not necessary because it no longer carries the index overhead it once did. /// If you have a lot of fields which have doc_values and norms disabled and you do not need to execute exists queries /// using those fields you might want to disable /// </summary> [DataMember(Name = "_field_names")] IFieldNamesField FieldNamesField { get; set; } /// <summary> /// Custom meta data to associate with a mapping. Not used by OpenSearch, /// but can be used to store application-specific metadata. /// </summary> [DataMember(Name = "_meta")] [JsonFormatter(typeof(VerbatimDictionaryInterfaceKeysFormatter<string, object>))] IDictionary<string, object> Meta { get; set; } /// <summary> /// If enabled (not enabled by default), then new string fields are checked to see whether /// they wholly contain a numeric value and if so, to map as a numeric field. /// </summary> [DataMember(Name = "numeric_detection")] bool? NumericDetection { get; set; } /// <summary> /// Specifies the mapping properties /// </summary> [DataMember(Name = "properties")] IProperties Properties { get; set; } /// <summary> /// Specifies configuration for the _routing parameter /// </summary> [DataMember(Name = "_routing")] IRoutingField RoutingField { get; set; } /// <summary> /// Specifies runtime fields for the mapping. /// </summary> [DataMember(Name = "runtime")] IRuntimeFields RuntimeFields { get; set; } /// <summary> /// If enabled, indexes the size in bytes of the original _source field. /// Requires mapper-size plugin be installed /// </summary> [DataMember(Name = "_size")] ISizeField SizeField { get; set; } /// <summary> /// Specifies configuration for the _source field /// </summary> [DataMember(Name = "_source")] ISourceField SourceField { get; set; } } public class TypeMapping : ITypeMapping { /// <inheritdoc /> public bool? DateDetection { get; set; } /// <inheritdoc /> public Union<bool, DynamicMapping> Dynamic { get; set; } /// <inheritdoc /> public IEnumerable<string> DynamicDateFormats { get; set; } /// <inheritdoc /> public IDynamicTemplateContainer DynamicTemplates { get; set; } /// <inheritdoc /> public IFieldNamesField FieldNamesField { get; set; } /// <inheritdoc /> public IDictionary<string, object> Meta { get; set; } /// <inheritdoc /> public bool? NumericDetection { get; set; } /// <inheritdoc /> public IProperties Properties { get; set; } /// <inheritdoc /> public IRoutingField RoutingField { get; set; } /// <inheritdoc /> public IRuntimeFields RuntimeFields { get; set; } /// <inheritdoc /> public ISizeField SizeField { get; set; } /// <inheritdoc /> public ISourceField SourceField { get; set; } } public class TypeMappingDescriptor<T> : DescriptorBase<TypeMappingDescriptor<T>, ITypeMapping>, ITypeMapping where T : class { bool? ITypeMapping.DateDetection { get; set; } Union<bool, DynamicMapping> ITypeMapping.Dynamic { get; set; } IEnumerable<string> ITypeMapping.DynamicDateFormats { get; set; } IDynamicTemplateContainer ITypeMapping.DynamicTemplates { get; set; } IFieldNamesField ITypeMapping.FieldNamesField { get; set; } IDictionary<string, object> ITypeMapping.Meta { get; set; } bool? ITypeMapping.NumericDetection { get; set; } IProperties ITypeMapping.Properties { get; set; } IRoutingField ITypeMapping.RoutingField { get; set; } IRuntimeFields ITypeMapping.RuntimeFields { get; set; } ISizeField ITypeMapping.SizeField { get; set; } ISourceField ITypeMapping.SourceField { get; set; } /// <summary> /// Convenience method to map as much as it can based on <see cref="OpenSearchTypeAttribute" /> attributes set on the /// type, as well as inferring mappings from the CLR property types. /// <pre>This method also automatically sets up mappings for known values types (int, long, double, datetime, etc)</pre> /// <pre>Class types default to object and Enums to int</pre> /// <pre>Later calls can override whatever is set is by this call.</pre> /// </summary> public TypeMappingDescriptor<T> AutoMap(IPropertyVisitor visitor = null, int maxRecursion = 0) => Assign(Self.Properties.AutoMap<T>(visitor, maxRecursion), (a, v) => a.Properties = v); /// <summary> /// Convenience method to map as much as it can based on <see cref="OpenSearchTypeAttribute" /> attributes set on the /// type, as well as inferring mappings from the CLR property types. /// This particular overload is useful for automapping any children /// <pre>This method also automatically sets up mappings for known values types (int, long, double, datetime, etc)</pre> /// <pre>Class types default to object and Enums to int</pre> /// <pre>Later calls can override whatever is set is by this call.</pre> /// </summary> public TypeMappingDescriptor<T> AutoMap(Type documentType, IPropertyVisitor visitor = null, int maxRecursion = 0) { if (!documentType.IsClass) throw new ArgumentException("must be a reference type", nameof(documentType)); return Assign(Self.Properties.AutoMap(documentType, visitor, maxRecursion), (a, v) => a.Properties = v); } /// <summary> /// Convenience method to map as much as it can based on <see cref="OpenSearchTypeAttribute" /> attributes set on the /// type, as well as inferring mappings from the CLR property types. /// This particular overload is useful for automapping any children /// <pre>This method also automatically sets up mappings for known values types (int, long, double, datetime, etc)</pre> /// <pre>Class types default to object and Enums to int</pre> /// <pre>Later calls can override whatever is set is by this call.</pre> /// </summary> public TypeMappingDescriptor<T> AutoMap<TDocument>(IPropertyVisitor visitor = null, int maxRecursion = 0) where TDocument : class => Assign(Self.Properties.AutoMap<TDocument>(visitor, maxRecursion), (a, v) => a.Properties = v); /// <summary> /// Convenience method to map as much as it can based on <see cref="OpenSearchTypeAttribute" /> attributes set on the /// type, as well as inferring mappings from the CLR property types. /// This overload determines how deep automapping should recurse on a complex CLR type. /// </summary> public TypeMappingDescriptor<T> AutoMap(int maxRecursion) => AutoMap(null, maxRecursion); /// <inheritdoc cref="ITypeMapping.Dynamic" /> public TypeMappingDescriptor<T> Dynamic(Union<bool, DynamicMapping> dynamic) => Assign(dynamic, (a, v) => a.Dynamic = v); /// <inheritdoc cref="ITypeMapping.Dynamic" /> public TypeMappingDescriptor<T> Dynamic(bool dynamic = true) => Assign(dynamic, (a, v) => a.Dynamic = v); /// <inheritdoc cref="ITypeMapping.SizeField" /> public TypeMappingDescriptor<T> SizeField(Func<SizeFieldDescriptor, ISizeField> sizeFieldSelector) => Assign(sizeFieldSelector, (a, v) => a.SizeField = v?.Invoke(new SizeFieldDescriptor())); /// <inheritdoc cref="ITypeMapping.SourceField" /> public TypeMappingDescriptor<T> SourceField(Func<SourceFieldDescriptor, ISourceField> sourceFieldSelector) => Assign(sourceFieldSelector, (a, v) => a.SourceField = v?.Invoke(new SourceFieldDescriptor())); /// <inheritdoc cref="ITypeMapping.SizeField" /> public TypeMappingDescriptor<T> DisableSizeField(bool? disabled = true) => Assign(new SizeField { Enabled = !disabled }, (a, v) => a.SizeField = v); /// <inheritdoc cref="ITypeMapping.DynamicDateFormats" /> public TypeMappingDescriptor<T> DynamicDateFormats(IEnumerable<string> dateFormats) => Assign(dateFormats, (a, v) => a.DynamicDateFormats = v); /// <inheritdoc cref="ITypeMapping.DateDetection" /> public TypeMappingDescriptor<T> DateDetection(bool? detect = true) => Assign(detect, (a, v) => a.DateDetection = v); /// <inheritdoc cref="ITypeMapping.NumericDetection" /> public TypeMappingDescriptor<T> NumericDetection(bool? detect = true) => Assign(detect, (a, v) => a.NumericDetection = v); /// <inheritdoc cref="ITypeMapping.RoutingField" /> public TypeMappingDescriptor<T> RoutingField(Func<RoutingFieldDescriptor<T>, IRoutingField> routingFieldSelector) => Assign(routingFieldSelector, (a, v) => a.RoutingField = v?.Invoke(new RoutingFieldDescriptor<T>())); public TypeMappingDescriptor<T> RuntimeFields(Func<RuntimeFieldsDescriptor<T>, IPromise<IRuntimeFields>> runtimeFieldsSelector) => Assign(runtimeFieldsSelector, (a, v) => a.RuntimeFields = v?.Invoke(new RuntimeFieldsDescriptor<T>())?.Value); /// <inheritdoc cref="ITypeMapping.RuntimeFields" /> public TypeMappingDescriptor<T> RuntimeFields<TDocument>(Func<RuntimeFieldsDescriptor<TDocument>, IPromise<IRuntimeFields>> runtimeFieldsSelector) where TDocument : class => Assign(runtimeFieldsSelector, (a, v) => a.RuntimeFields = v?.Invoke(new RuntimeFieldsDescriptor<TDocument>())?.Value); /// <inheritdoc cref="ITypeMapping.FieldNamesField" /> public TypeMappingDescriptor<T> FieldNamesField(Func<FieldNamesFieldDescriptor<T>, IFieldNamesField> fieldNamesFieldSelector) => Assign(fieldNamesFieldSelector.Invoke(new FieldNamesFieldDescriptor<T>()), (a, v) => a.FieldNamesField = v); /// <inheritdoc cref="ITypeMapping.Meta" /> public TypeMappingDescriptor<T> Meta(Func<FluentDictionary<string, object>, FluentDictionary<string, object>> metaSelector) => Assign(metaSelector(new FluentDictionary<string, object>()), (a, v) => a.Meta = v); /// <inheritdoc cref="ITypeMapping.Meta" /> public TypeMappingDescriptor<T> Meta(Dictionary<string, object> metaDictionary) => Assign(metaDictionary, (a, v) => a.Meta = v); /// <inheritdoc cref="ITypeMapping.Properties" /> public TypeMappingDescriptor<T> Properties(Func<PropertiesDescriptor<T>, IPromise<IProperties>> propertiesSelector) => Assign(propertiesSelector, (a, v) => a.Properties = v?.Invoke(new PropertiesDescriptor<T>(Self.Properties))?.Value); /// <inheritdoc cref="ITypeMapping.Properties" /> public TypeMappingDescriptor<T> Properties<TDocument>(Func<PropertiesDescriptor<TDocument>, IPromise<IProperties>> propertiesSelector) where TDocument : class => Assign(propertiesSelector, (a, v) => a.Properties = v?.Invoke(new PropertiesDescriptor<TDocument>(Self.Properties))?.Value); /// <inheritdoc cref="ITypeMapping.DynamicTemplates" /> public TypeMappingDescriptor<T> DynamicTemplates( Func<DynamicTemplateContainerDescriptor<T>, IPromise<IDynamicTemplateContainer>> dynamicTemplatesSelector ) => Assign(dynamicTemplatesSelector, (a, v) => a.DynamicTemplates = v?.Invoke(new DynamicTemplateContainerDescriptor<T>())?.Value); } }