Elastic.SemanticKernel.Connectors.Elasticsearch/ElasticsearchGenericDataModelMapper.cs (91 lines of code) (raw):

// Licensed to Elasticsearch B.V under one or more agreements. // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; using System.Text.Json.Nodes; using Elastic.Clients.Elasticsearch; using Elastic.Transport.Extensions; using Microsoft.Extensions.VectorData; using Microsoft.SemanticKernel; namespace Elastic.SemanticKernel.Connectors.Elasticsearch; /// <summary> /// A mapper that maps between the generic Semantic Kernel data model and the model that the data is stored under, /// within Elasticsearch. /// </summary> internal sealed class ElasticsearchGenericDataModelMapper : IVectorStoreRecordMapper<VectorStoreGenericDataModel<string>, (string? id, JsonObject document)> { /// <summary>The Elasticsearch client settings.</summary> private readonly IElasticsearchClientSettings _elasticsearchClientSettings; /// <summary>A mapping from <see cref="VectorStoreRecordDefinition" /> to storage model property name.</summary> private readonly Dictionary<VectorStoreRecordProperty, string> _propertyToStorageName; /// <summary> /// Initializes a new instance of the <see cref="ElasticsearchGenericDataModelMapper" /> class. /// </summary> /// <param name="propertyToStorageName">A mapping from <see cref="VectorStoreRecordDefinition" /> to storage model property name.</param> /// <param name="elasticsearchClientSettings">The Elasticsearch client settings to use.</param> public ElasticsearchGenericDataModelMapper( Dictionary<VectorStoreRecordProperty, string> propertyToStorageName, IElasticsearchClientSettings elasticsearchClientSettings) { Verify.NotNull(propertyToStorageName); Verify.NotNull(elasticsearchClientSettings); // Assign. _elasticsearchClientSettings = elasticsearchClientSettings; _propertyToStorageName = propertyToStorageName; } /// <inheritdoc /> public (string? id, JsonObject document) MapFromDataToStorageModel(VectorStoreGenericDataModel<string> dataModel) { Verify.NotNull(dataModel); var document = new JsonObject(); foreach (var item in _propertyToStorageName) { var property = item.Key; var storageModelPropertyName = item.Value; var sourceDictionary = property switch { VectorStoreRecordKeyProperty => null, VectorStoreRecordVectorProperty => dataModel.Vectors, VectorStoreRecordDataProperty => dataModel.Data, _ => throw new NotSupportedException($"Property of type '{property.GetType().Name}' is not supported.") }; if (sourceDictionary is null) { continue; } if (!sourceDictionary.TryGetValue(property.DataModelPropertyName, out var value)) { // Just skip this property if it's not in the data model. continue; } document.Add(storageModelPropertyName, SerializeSource(value, _elasticsearchClientSettings)); } return (dataModel.Key, document); } /// <inheritdoc /> public VectorStoreGenericDataModel<string> MapFromStorageToDataModel((string? id, JsonObject document) storageModel, StorageToDataModelMapperOptions options) { Verify.NotNull(storageModel); var dataModel = new VectorStoreGenericDataModel<string>(storageModel.id!); foreach (var item in _propertyToStorageName) { var property = item.Key; var storageModelPropertyName = item.Value; if (!storageModel.document.TryGetPropertyValue(storageModelPropertyName, out var value)) { // Just skip this property if it's not in the storage model. continue; } var targetDictionary = property switch { VectorStoreRecordKeyProperty => null, VectorStoreRecordVectorProperty => dataModel.Vectors, VectorStoreRecordDataProperty => dataModel.Data, _ => throw new NotSupportedException($"Property of type '{property.GetType().Name}' is not supported.") }; if (targetDictionary is null) { continue; } targetDictionary[property.DataModelPropertyName] = value is null ? null : _elasticsearchClientSettings.SourceSerializer.Deserialize(value, property.PropertyType); } return dataModel; } private static JsonNode? SerializeSource<T>(T? obj, IElasticsearchClientSettings settings) { if (obj is null) { return null; } using var stream = settings.MemoryStreamFactory.Create(); settings.SourceSerializer.Serialize(obj, stream); stream.Position = 0; return settings.SourceSerializer.Deserialize<JsonNode>(stream); } }