Skip to content

Commit 2d2145e

Browse files
Extend search override model (#285)
1 parent 606305e commit 2d2145e

File tree

5 files changed

+139
-6
lines changed

5 files changed

+139
-6
lines changed

src/Typesense/Converter/UnixEpochDateTimeConverter.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using System;
2-
using System.Text.Json.Serialization;
3-
using System.Text.Json;
42
using System.Globalization;
3+
using System.Text.Json;
4+
using System.Text.Json.Serialization;
55

66
namespace Typesense.Converter;
77

@@ -15,7 +15,6 @@ public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, Jso
1515
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
1616
{
1717
ArgumentNullException.ThrowIfNull(writer);
18-
1918
writer.WriteStringValue((value - DateTime.UnixEpoch).TotalSeconds.ToString(CultureInfo.InvariantCulture));
2019
}
2120
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System;
2+
using System.Text.Json;
3+
using System.Text.Json.Serialization;
4+
5+
namespace Typesense.Converter;
6+
7+
/// <summary>
8+
/// Converts between nullable DateTime and Unix epoch seconds as a long integer value.
9+
/// </summary>
10+
public class UnixEpochDateTimeLongConverter : JsonConverter<DateTime>
11+
{
12+
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
13+
{
14+
return DateTime.UnixEpoch.AddSeconds(reader.GetInt64());
15+
}
16+
17+
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
18+
{
19+
ArgumentNullException.ThrowIfNull(writer);
20+
writer.WriteNumberValue(Convert.ToInt64((value - DateTime.UnixEpoch).TotalSeconds));
21+
}
22+
}

src/Typesense/SearchOverride.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Text.Json.Serialization;
4+
using Typesense.Converter;
45

56
namespace Typesense;
67

@@ -72,6 +73,47 @@ public record SearchOverride
7273
[JsonPropertyName("includes")]
7374
public IEnumerable<Include>? Includes { get; init; }
7475

76+
/// <summary>
77+
/// Custom metadata for the search override.
78+
/// </summary>
79+
/// <remarks>
80+
/// Example metadata JSON:
81+
/// <code>
82+
/// {
83+
/// "metadata": {
84+
/// "createdBy": "admin",
85+
/// "tags": ["featured", "promotion"]
86+
/// }
87+
/// }
88+
/// </code>
89+
/// </remarks>
90+
[JsonPropertyName("metadata")]
91+
public IDictionary<string, object>? Metadata { get; init; }
92+
93+
[JsonPropertyName("filter_by")]
94+
public string? FilterBy { get; init; }
95+
96+
[JsonPropertyName("sort_by")]
97+
public string? SortBy { get; init; }
98+
99+
[JsonPropertyName("replace_query")]
100+
public string? ReplaceQuery { get; init; }
101+
102+
[JsonPropertyName("remove_matched_tokens")]
103+
public bool? RemoveMatchedTokens { get; init; }
104+
105+
[JsonPropertyName("filter_curated_hits")]
106+
public bool? FilterCuratedHits { get; init; }
107+
108+
[JsonPropertyName("stop_processing")]
109+
public bool? StopProcessing { get; init; }
110+
111+
[JsonPropertyName("effective_from_ts"), JsonConverter(typeof(UnixEpochDateTimeLongConverter))]
112+
public DateTime? EffectiveFromTs { get; init; }
113+
114+
[JsonPropertyName("effective_to_ts"), JsonConverter(typeof(UnixEpochDateTimeLongConverter))]
115+
public DateTime? EffectiveToTs { get; init; }
116+
75117
[JsonPropertyName("rule")]
76118
public Rule Rule { get; init; }
77119

src/Typesense/SearchOverrideResponse.cs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
using System;
12
using System.Collections.Generic;
23
using System.Text.Json.Serialization;
4+
using Typesense.Converter;
35

46
namespace Typesense;
57

@@ -14,14 +16,53 @@ public record SearchOverrideResponse
1416
[JsonPropertyName("includes")]
1517
public IEnumerable<Include> Includes { get; init; }
1618

19+
[JsonPropertyName("metadata")]
20+
public IDictionary<string, object> Metadata { get; init; }
21+
22+
[JsonPropertyName("filter_by")]
23+
public string FilterBy { get; init; }
24+
25+
[JsonPropertyName("sort_by")]
26+
public string SortBy { get; init; }
27+
28+
[JsonPropertyName("replace_query")]
29+
public string ReplaceQuery { get; init; }
30+
31+
[JsonPropertyName("remove_matched_tokens")]
32+
public bool RemoveMatchedTokens { get; init; }
33+
34+
[JsonPropertyName("filter_curated_hits")]
35+
public bool FilterCuratedHits { get; init; }
36+
37+
[JsonPropertyName("stop_processing")]
38+
public bool StopProcessing { get; init; }
39+
40+
[JsonPropertyName("effective_from_ts"), JsonConverter(typeof(UnixEpochDateTimeLongConverter))]
41+
public DateTime EffectiveFromTs { get; init; }
42+
43+
[JsonPropertyName("effective_to_ts"), JsonConverter(typeof(UnixEpochDateTimeLongConverter))]
44+
public DateTime EffectiveToTs { get; init; }
45+
1746
[JsonPropertyName("rule")]
1847
public Rule Rule { get; init; }
1948

2049
[JsonConstructor]
21-
public SearchOverrideResponse(IEnumerable<Exclude> excludes, IEnumerable<Include> includes, Rule rule, string id)
50+
public SearchOverrideResponse(IEnumerable<Exclude> excludes, IEnumerable<Include> includes,
51+
IDictionary<string, object> metadata, string filterBy, string sortBy, string replaceQuery, bool removeMatchedTokens,
52+
bool filterCuratedHits, bool stopProcessing, DateTime effectiveFromTs, DateTime effectiveToTs,
53+
Rule rule, string id)
2254
{
2355
Excludes = excludes;
2456
Includes = includes;
57+
Metadata = metadata;
58+
FilterBy = filterBy;
59+
SortBy = sortBy;
60+
ReplaceQuery = replaceQuery;
61+
RemoveMatchedTokens = removeMatchedTokens;
62+
FilterCuratedHits = filterCuratedHits;
63+
StopProcessing = stopProcessing;
64+
EffectiveFromTs = effectiveFromTs;
65+
EffectiveToTs = effectiveToTs;
2566
Rule = rule;
2667
Id = id;
2768
}

test/Typesense.Tests/TypesenseClientTests.cs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1967,22 +1967,30 @@ public async Task Upsert_search_override()
19671967
new Include("422", 1),
19681968
new Include("54", 2)
19691969
},
1970+
FilterBy = "NOT description:=[pink lady 1]",
1971+
Metadata = new Dictionary<string, object> { ["apple_color"] = "green" },
1972+
SortBy = "color:asc",
1973+
ReplaceQuery = "replacement query",
1974+
FilterCuratedHits = false,
1975+
StopProcessing = false,
1976+
EffectiveFromTs = DateTime.UtcNow,
19701977
};
19711978

19721979
var response = await _client.UpsertSearchOverride(
19731980
"companies", "customize-apple", searchOverride);
19741981
}
19751982

19761983
[Fact, TestPriority(19)]
1977-
public async Task Retrive_search_override()
1984+
public async Task Retrieve_search_override()
19781985
{
19791986
var searchOverrides = await _client.ListSearchOverrides("companies");
19801987

19811988
var expected = searchOverrides.SearchOverrides.First();
19821989

19831990
var response = await _client.RetrieveSearchOverride("companies", expected.Id);
19841991

1985-
response.Should().BeEquivalentTo(expected);
1992+
// FluentAssertions doesn't support JsonElements
1993+
response.Should().BeEquivalentTo(expected, options => options.Excluding(x => x.Metadata));
19861994
}
19871995

19881996
[Fact, TestPriority(20)]
@@ -1996,6 +2004,14 @@ public async Task List_search_overrides()
19962004
new Include("422", 1),
19972005
new Include("54", 2)
19982006
},
2007+
FilterBy = "NOT description:=[pink lady 1]",
2008+
Metadata = new Dictionary<string, object> { ["apple_color"] = "green" },
2009+
SortBy = "color:asc",
2010+
ReplaceQuery = "replacement query",
2011+
RemoveMatchedTokens = true,
2012+
FilterCuratedHits = false,
2013+
StopProcessing = false,
2014+
EffectiveFromTs = DateTime.UtcNow,
19992015
};
20002016

20012017
var response = await _client.ListSearchOverrides("companies");
@@ -2007,8 +2023,21 @@ public async Task List_search_overrides()
20072023
{
20082024
first.Id.Should().Be("customize-apple");
20092025
first.Includes.Should().BeEquivalentTo(expected.Includes);
2026+
first.FilterBy.Should().BeEquivalentTo(expected.FilterBy);
2027+
first.SortBy.Should().BeEquivalentTo(expected.SortBy);
2028+
first.ReplaceQuery.Should().BeEquivalentTo(expected.ReplaceQuery);
2029+
first.RemoveMatchedTokens.Should().BeTrue();
2030+
first.FilterCuratedHits.Should().BeFalse();
2031+
first.StopProcessing.Should().BeFalse();
2032+
first.EffectiveFromTs.Should().HaveYear(DateTime.UtcNow.Year);
2033+
first.EffectiveFromTs.Should().HaveMonth(DateTime.UtcNow.Month);
2034+
first.EffectiveFromTs.Should().HaveDay(DateTime.UtcNow.Day);
20102035
first.Rule.Should().BeEquivalentTo(expected.Rule);
20112036
});
2037+
2038+
// FluentAssertions doesn't support JsonElements, so we need to check it separately.
2039+
response.SearchOverrides.FirstOrDefault()?.Metadata?.Should().NotBeEmpty();
2040+
response.SearchOverrides.FirstOrDefault()?.Metadata["apple_color"].As<JsonElement>().GetString().Should().Be("green");
20122041
}
20132042

20142043
[Fact, TestPriority(21)]

0 commit comments

Comments
 (0)