Elastic.SemanticKernel.Connectors.Elasticsearch/ElasticsearchDataModelMapper.cs (50 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.Collections.Generic;
using System.Linq;
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 ElasticsearchDataModelMapper<TRecord> :
IVectorStoreRecordMapper<TRecord, (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 ElasticsearchDataModelMapper(
Dictionary<VectorStoreRecordProperty, string> propertyToStorageName,
IElasticsearchClientSettings elasticsearchClientSettings)
{
Verify.NotNull(propertyToStorageName);
Verify.NotNull(elasticsearchClientSettings);
// Assign.
_elasticsearchClientSettings = elasticsearchClientSettings;
_propertyToStorageName = propertyToStorageName;
}
/// <inheritdoc />
public (string? id, JsonObject document) MapFromDataToStorageModel(TRecord dataModel)
{
// Serialize the whole record to JsonObject.
var document = SerializeSource(dataModel, _elasticsearchClientSettings)!;
// Extract key property.
var keyProperty = _propertyToStorageName.Single(x => x.Key is VectorStoreRecordKeyProperty);
var keyValue = document[keyProperty.Value]!.AsValue();
var id = keyValue.GetValue<string?>();
// Remove key property from document.
document.Remove(keyProperty.Value);
return (id, document);
}
/// <inheritdoc />
public TRecord MapFromStorageToDataModel((string? id, JsonObject document) storageModel,
StorageToDataModelMapperOptions options)
{
// Add key property to document.
var keyProperty = _propertyToStorageName.Single(x => x.Key is VectorStoreRecordKeyProperty);
storageModel.document.Add(keyProperty.Value, storageModel.id);
// Serialize the whole model into the user-defined record type.
return _elasticsearchClientSettings.SourceSerializer.Deserialize<TRecord>(storageModel.document)!;
}
private static JsonObject? 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<JsonObject>(stream);
}
}