Elastic.SemanticKernel.Connectors.Elasticsearch/ElasticsearchVectorStoreCollectionCreateMapping.cs (82 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 Elastic.Clients.Elasticsearch.Mapping;
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel.Data;
namespace Elastic.SemanticKernel.Connectors.Elasticsearch;
/// <summary>
/// Contains mapping helpers to use when creating an Elasticsearch vector collection.
/// </summary>
internal static class ElasticsearchVectorStoreCollectionCreateMapping
{
/// <summary>
/// TBC
/// </summary>
/// <param name="propertyReader"></param>
/// <param name="propertyToStorageName"></param>
/// <returns></returns>
public static Properties BuildPropertyMappings(
VectorStoreRecordPropertyReader propertyReader,
Dictionary<VectorStoreRecordProperty, string> propertyToStorageName)
{
var propertyMappings = new Properties();
var vectorProperties = propertyReader.VectorProperties;
foreach (var property in vectorProperties)
{
propertyMappings.Add(propertyToStorageName[property],
new DenseVectorProperty
{
Dims = property.Dimensions,
Index = true,
Similarity = GetSimilarityFunction(property),
IndexOptions = new DenseVectorIndexOptions
{
Type = GetIndexKind(property)
}
});
}
var dataProperties = propertyReader.DataProperties;
foreach (var property in dataProperties)
{
if (property.IsFullTextSearchable)
{
propertyMappings.Add(propertyToStorageName[property], new TextProperty());
}
else if (property.IsFilterable)
{
propertyMappings.Add(propertyToStorageName[property], new KeywordProperty());
}
}
return propertyMappings;
}
/// <summary>
/// Get the configured Elasticsearch index kind from the given <paramref name="vectorProperty" />.
/// If none is configured, the default is <c>int8_hnsw</c>.
/// </summary>
/// <param name="vectorProperty">The vector property definition.</param>
/// <returns>The chosen Elasticsearch index kind.</returns>
/// <exception cref="InvalidOperationException">Thrown if an index kind is chosen that isn't supported by Elasticsearch.</exception>
private static DenseVectorIndexOptionsType GetIndexKind(VectorStoreRecordVectorProperty vectorProperty)
{
const string int8HnswIndexKind = "int8_hnsw";
const string int4HnswIndexKind = "int4_hnsw";
const string int8FlatIndexKind = "int8_flat";
const string int4FlatIndexKind = "int4_flat";
if (vectorProperty.DistanceFunction is null)
{
return DenseVectorIndexOptionsType.Int8Hnsw;
}
return vectorProperty.IndexKind switch
{
IndexKind.Hnsw => DenseVectorIndexOptionsType.Hnsw,
int8HnswIndexKind => DenseVectorIndexOptionsType.Int8Hnsw,
int4HnswIndexKind => DenseVectorIndexOptionsType.Int4Hnsw,
IndexKind.Flat => DenseVectorIndexOptionsType.Flat,
int8FlatIndexKind => DenseVectorIndexOptionsType.Int8Flat,
int4FlatIndexKind => DenseVectorIndexOptionsType.Int4Flat,
_ => throw new InvalidOperationException(
$"Index kind '{vectorProperty.IndexKind}' for {nameof(VectorStoreRecordVectorProperty)} '{vectorProperty.DataModelPropertyName}' is not supported by the Elasticsearch VectorStore.")
};
}
/// <summary>
/// Get the configured Elasticsearch distance function from the given <paramref name="vectorProperty" />.
/// If none is configured, the default is <c>cosine</c>.
/// </summary>
/// <param name="vectorProperty">The vector property definition.</param>
/// <returns>The chosen Elasticsearch distance function.</returns>
/// <exception cref="InvalidOperationException">
/// Thrown if a distance function is chosen that isn't supported by
/// Elasticsearch.
/// </exception>
private static DenseVectorSimilarity GetSimilarityFunction(VectorStoreRecordVectorProperty vectorProperty)
{
const string maxInnerProductSimilarity = "max_inner_product";
if (vectorProperty.DistanceFunction is null)
{
return DenseVectorSimilarity.Cosine;
}
return vectorProperty.DistanceFunction switch
{
DistanceFunction.CosineSimilarity => DenseVectorSimilarity.Cosine,
DistanceFunction.DotProductSimilarity => DenseVectorSimilarity.DotProduct,
DistanceFunction.EuclideanDistance => DenseVectorSimilarity.L2Norm,
maxInnerProductSimilarity => DenseVectorSimilarity.MaxInnerProduct,
_ => throw new InvalidOperationException(
$"Distance function '{vectorProperty.DistanceFunction}' for {nameof(VectorStoreRecordVectorProperty)} '{vectorProperty.DataModelPropertyName}' is not supported by the Elasticsearch VectorStore.")
};
}
}