forked from dotnet/extensions
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCollectionJsonModelBuilder.cs
More file actions
108 lines (95 loc) · 4.98 KB
/
Copy pathCollectionJsonModelBuilder.cs
File metadata and controls
108 lines (95 loc) · 4.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.AI;
using Microsoft.Shared.DiagnosticIds;
namespace Microsoft.Extensions.VectorData.ProviderServices;
/// <summary>
/// Represents a model builder that performs logic specific to providers that use System.Text.Json for serialization.
/// This is an internal support type meant for use by providers only and not by applications.
/// </summary>
[Experimental(DiagnosticIds.Experiments.VectorDataCollectionModel, UrlFormat = DiagnosticIds.UrlFormat)]
public abstract class CollectionJsonModelBuilder : CollectionModelBuilder
{
private JsonSerializerOptions? _jsonSerializerOptions;
/// <summary>
/// Initializes a new instance of the <see cref="CollectionJsonModelBuilder"/> class.
/// </summary>
protected CollectionJsonModelBuilder(CollectionModelBuildingOptions options)
: base(options)
{
}
/// <summary>
/// Builds and returns a <see cref="CollectionModel"/> from the given <paramref name="recordType"/> and <paramref name="definition"/>.
/// </summary>
/// <returns>The built <see cref="CollectionModel"/>.</returns>
[RequiresDynamicCode("This model building variant is not compatible with NativeAOT. See BuildDynamic() for dynamic mapping, and a third variant accepting source-generated delegates will be introduced in the future.")]
[RequiresUnreferencedCode("This model building variant is not compatible with trimming. See BuildDynamic() for dynamic mapping, and a third variant accepting source-generated delegates will be introduced in the future.")]
public virtual CollectionModel Build(
Type recordType,
Type keyType,
VectorStoreCollectionDefinition? definition,
IEmbeddingGenerator? defaultEmbeddingGenerator,
JsonSerializerOptions jsonSerializerOptions)
{
_jsonSerializerOptions = jsonSerializerOptions;
return Build(recordType, keyType, definition, defaultEmbeddingGenerator);
}
/// <summary>
/// Builds and returns a <see cref="CollectionModel"/> for dynamic mapping scenarios from the given <paramref name="definition"/>.
/// </summary>
/// <returns>The built <see cref="CollectionModel"/>.</returns>
public virtual CollectionModel BuildDynamic(
VectorStoreCollectionDefinition definition,
IEmbeddingGenerator? defaultEmbeddingGenerator,
JsonSerializerOptions jsonSerializerOptions)
{
_jsonSerializerOptions = jsonSerializerOptions;
return BuildDynamic(definition, defaultEmbeddingGenerator);
}
/// <inheritdoc/>
protected override void Customize()
{
// This mimics the naming behavior of the System.Text.Json serializer, which we use for serialization/deserialization.
// The property storage names in the model must be in sync with the serializer configuration, since the model is used e.g. for filtering
// even if serialization/deserialization doesn't use the model.
var namingPolicy = _jsonSerializerOptions?.PropertyNamingPolicy;
foreach (var property in Properties)
{
var keyPropertyWithReservedName = Options.ReservedKeyStorageName is not null && property is KeyPropertyModel;
string storageName;
if (property.PropertyInfo?.GetCustomAttribute<JsonPropertyNameAttribute>() is { } jsonPropertyNameAttribute)
{
if (keyPropertyWithReservedName && jsonPropertyNameAttribute.Name != Options.ReservedKeyStorageName)
{
throw new InvalidOperationException($"The key property for your provider must always have the reserved name '{Options.ReservedKeyStorageName}' and cannot be changed.");
}
storageName = jsonPropertyNameAttribute.Name;
}
else if (namingPolicy is not null)
{
storageName = namingPolicy.ConvertName(property.ModelName);
}
else
{
storageName = property.ModelName;
}
if (keyPropertyWithReservedName)
{
// Some providers (Weaviate, Cosmos NoSQL) have a fixed, reserved storage name for keys (id), and at the same time use an external
// JSON serializer to serialize the entire user POCO. Since the serializer is unaware of the reserved storage name, it will produce
// a storage name as usual, based on the .NET property's name, possibly with a naming policy applied to it. The provider then needs
// to look that up and replace with the reserved name.
((KeyPropertyModel)property).SerializedKeyName = storageName;
}
else
{
property.StorageName = storageName;
}
}
}
}