Skip to content

Commit e9195fd

Browse files
stevejgordongithub-actions[bot]
authored andcommitted
Fixes for UpdateRequest (#6711)
* Remove document as body members from UpdateRequest * Upsert uses source serializer on UpdateRequest * Add coordinated tests for updating documents
1 parent 298e12a commit e9195fd

File tree

3 files changed

+255
-28
lines changed

3 files changed

+255
-28
lines changed

src/Elastic.Clients.Elasticsearch/Api/UpdateRequestDescriptor.cs

-17
This file was deleted.

src/Elastic.Clients.Elasticsearch/_Generated/Api/UpdateRequest.g.cs

+19-11
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,6 @@ public UpdateRequest(Elastic.Clients.Elasticsearch.IndexName index, Elastic.Clie
7070
internal override ApiUrls ApiUrls => ApiUrlsLookups.NoNamespaceUpdate;
7171
protected override HttpMethod HttpMethod => HttpMethod.POST;
7272
protected override bool SupportsBody => true;
73-
[JsonIgnore]
74-
public TDocument Document { get; set; }
75-
7673
[JsonIgnore]
7774
public long? IfPrimaryTerm { get => Q<long?>("if_primary_term"); set => Q("if_primary_term", value); }
7875

@@ -133,20 +130,33 @@ public UpdateRequest(Elastic.Clients.Elasticsearch.IndexName index, Elastic.Clie
133130

134131
[JsonInclude]
135132
[JsonPropertyName("upsert")]
133+
[SourceConverter]
136134
public TDocument? Upsert { get; set; }
137135
}
138136

139137
public sealed partial class UpdateRequestDescriptor<TDocument, TPartialDocument> : RequestDescriptorBase<UpdateRequestDescriptor<TDocument, TPartialDocument>, UpdateRequestParameters>
140138
{
141139
internal UpdateRequestDescriptor(Action<UpdateRequestDescriptor<TDocument, TPartialDocument>> configure) => configure.Invoke(this);
142-
internal UpdateRequestDescriptor(Elastic.Clients.Elasticsearch.IndexName index, Elastic.Clients.Elasticsearch.Id id) : base(r => r.Required("index", index).Required("id", id))
140+
public UpdateRequestDescriptor(Elastic.Clients.Elasticsearch.IndexName index, Elastic.Clients.Elasticsearch.Id id) : base(r => r.Required("index", index).Required("id", id))
141+
{
142+
}
143+
144+
public UpdateRequestDescriptor(TDocument document) : this(typeof(TDocument), Elasticsearch.Id.From(document))
145+
{
146+
}
147+
148+
public UpdateRequestDescriptor(TDocument document, IndexName index, Id id) : this(index, id)
149+
{
150+
}
151+
152+
public UpdateRequestDescriptor(TDocument document, IndexName index) : this(index, Elasticsearch.Id.From(document))
153+
{
154+
}
155+
156+
public UpdateRequestDescriptor(TDocument document, Id id) : this(typeof(TDocument), id)
143157
{
144158
}
145159

146-
public UpdateRequestDescriptor(TDocument document) : this(typeof(TDocument), Elasticsearch.Id.From(document)) => DocumentValue = document;
147-
public UpdateRequestDescriptor(TDocument document, IndexName index, Id id) : this(index, id) => DocumentValue = document;
148-
public UpdateRequestDescriptor(TDocument document, IndexName index) : this(index, Elasticsearch.Id.From(document)) => DocumentValue = document;
149-
public UpdateRequestDescriptor(TDocument document, Id id) : this(typeof(TDocument), id) => DocumentValue = document;
150160
public UpdateRequestDescriptor(Id id) : this(typeof(TDocument), id)
151161
{
152162
}
@@ -195,8 +205,6 @@ public UpdateRequestDescriptor<TDocument, TPartialDocument> Index(Elastic.Client
195205

196206
private TDocument? UpsertValue { get; set; }
197207

198-
private TDocument DocumentValue { get; set; }
199-
200208
public UpdateRequestDescriptor<TDocument, TPartialDocument> Source(Elastic.Clients.Elasticsearch.SourceConfig? source)
201209
{
202210
SourceValue = source;
@@ -281,7 +289,7 @@ protected override void Serialize(Utf8JsonWriter writer, JsonSerializerOptions o
281289
if (UpsertValue is not null)
282290
{
283291
writer.WritePropertyName("upsert");
284-
JsonSerializer.Serialize(writer, UpsertValue, options);
292+
SourceSerialisation.Serialize(UpsertValue, writer, settings);
285293
}
286294

287295
writer.WriteEndObject();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Threading.Tasks;
6+
using Tests.Core.ManagedElasticsearch.Clusters;
7+
using Tests.Framework.EndpointTests;
8+
using Tests.Framework.EndpointTests.TestState;
9+
using System.Text.Json.Serialization;
10+
11+
namespace Tests.Document.Single.Update;
12+
13+
public class UpdateDocumentsCoordinatedTests : CoordinatedIntegrationTestBase<WritableCluster>
14+
{
15+
private const string IndexDocumentStep = nameof(IndexDocumentStep);
16+
private const string GetAfterScriptedUpdateStep = nameof(GetAfterScriptedUpdateStep);
17+
private const string UpdateWithScriptStep = nameof(UpdateWithScriptStep);
18+
private const string UpdateWithPartialStep = nameof(UpdateWithPartialStep);
19+
private const string GetAfterPartialUpdateStep = nameof(GetAfterPartialUpdateStep);
20+
private const string UpdateWithPartialStepTwo = nameof(UpdateWithPartialStepTwo);
21+
private const string UpsertExistingStep = nameof(UpsertExistingStep);
22+
private const string GetAfterUpsertExistingStep = nameof(GetAfterUpsertExistingStep);
23+
private const string UpsertNewStep = nameof(UpsertNewStep);
24+
private const string GetAfterUpsertNewStep = nameof(GetAfterUpsertNewStep);
25+
26+
private static Script TestScript => new(new InlineScript
27+
{
28+
Source = "ctx._source.counter += params.count",
29+
Language = ScriptLanguage.Painless,
30+
Params = new() { { "count", 4 } }
31+
});
32+
33+
public UpdateDocumentsCoordinatedTests(WritableCluster cluster, EndpointUsage usage) : base(
34+
new CoordinatedUsage(cluster, usage)
35+
{
36+
{
37+
IndexDocumentStep, u =>
38+
u.Calls<IndexRequestDescriptor<UpdateTestDocument>, IndexRequest<UpdateTestDocument>, IndexResponse>(
39+
v => new IndexRequest<UpdateTestDocument>(UpdateTestDocument.InitialValues, v),
40+
(v, d) => d,
41+
(v, c, f) => c.Index(UpdateTestDocument.InitialValues, Infer.Index<UpdateTestDocument>(), r => r.Id(v)),
42+
(v, c, f) => c.IndexAsync(UpdateTestDocument.InitialValues, Infer.Index<UpdateTestDocument>(),r => r.Id(v)),
43+
(_, c, r) => c.Index(r),
44+
(_, c, r) => c.IndexAsync(r)
45+
)
46+
},
47+
{
48+
UpdateWithScriptStep, u =>
49+
u.Calls<UpdateRequestDescriptor<UpdateTestDocument, object>, UpdateRequest<UpdateTestDocument, object>, UpdateResponse<UpdateTestDocument>>(
50+
v => new UpdateRequest<UpdateTestDocument, object>(typeof(UpdateTestDocument), v) { Script = TestScript },
51+
(v, d) => d.Script(TestScript),
52+
(v, c, f) => c.Update(Infer.Index<UpdateTestDocument>(), v, f),
53+
(v, c, f) => c.UpdateAsync(Infer.Index<UpdateTestDocument>(), v, f),
54+
(_, c, r) => c.Update(r),
55+
(_, c, r) => c.UpdateAsync(r)
56+
)
57+
},
58+
{
59+
GetAfterScriptedUpdateStep, u =>
60+
u.Calls<GetRequestDescriptor<UpdateTestDocument>, GetRequest, GetResponse<UpdateTestDocument>>(
61+
v => new GetRequest(typeof(UpdateTestDocument), v),
62+
(v, d) => d,
63+
(v, c, f) => c.Get(Infer.Index<UpdateTestDocument>(), v, f),
64+
(v, c, f) => c.GetAsync(Infer.Index<UpdateTestDocument>(), v, f),
65+
(_, c, r) => c.Get<UpdateTestDocument>(r),
66+
(_, c, r) => c.GetAsync<UpdateTestDocument>(r)
67+
)
68+
},
69+
{
70+
UpdateWithPartialStep, u =>
71+
u.Calls<UpdateRequestDescriptor<UpdateTestDocument, UpdateTestDocumentPartial>, UpdateRequest<UpdateTestDocument, UpdateTestDocumentPartial>, UpdateResponse<UpdateTestDocument>>(
72+
v => new UpdateRequest<UpdateTestDocument, UpdateTestDocumentPartial>(typeof(UpdateTestDocument), v) { Doc = new UpdateTestDocumentPartial() },
73+
(v, d) => d.Doc(new UpdateTestDocumentPartial()),
74+
(v, c, f) => c.Update(Infer.Index<UpdateTestDocument>(), v, f),
75+
(v, c, f) => c.UpdateAsync(Infer.Index<UpdateTestDocument>(), v, f),
76+
(_, c, r) => c.Update(r),
77+
(_, c, r) => c.UpdateAsync(r)
78+
)
79+
},
80+
{
81+
GetAfterPartialUpdateStep, u =>
82+
u.Calls<GetRequestDescriptor<UpdateTestDocument>, GetRequest, GetResponse<UpdateTestDocument>>(
83+
v => new GetRequest(typeof(UpdateTestDocument), v),
84+
(v, d) => d,
85+
(v, c, f) => c.Get(Infer.Index<UpdateTestDocument>(), v, f),
86+
(v, c, f) => c.GetAsync(Infer.Index<UpdateTestDocument>(), v, f),
87+
(_, c, r) => c.Get<UpdateTestDocument>(r),
88+
(_, c, r) => c.GetAsync<UpdateTestDocument>(r)
89+
)
90+
},
91+
{
92+
UpdateWithPartialStepTwo, u =>
93+
u.Calls<UpdateRequestDescriptor<UpdateTestDocument, UpdateTestDocumentPartial>, UpdateRequest<UpdateTestDocument, UpdateTestDocumentPartial>, UpdateResponse<UpdateTestDocument>>(
94+
v => new UpdateRequest<UpdateTestDocument, UpdateTestDocumentPartial>(typeof(UpdateTestDocument), v) { Doc = new UpdateTestDocumentPartial() },
95+
(v, d) => d.Doc(new UpdateTestDocumentPartial()),
96+
(v, c, f) => c.Update(Infer.Index<UpdateTestDocument>(), v, f),
97+
(v, c, f) => c.UpdateAsync(Infer.Index<UpdateTestDocument>(), v, f),
98+
(_, c, r) => c.Update(r),
99+
(_, c, r) => c.UpdateAsync(r)
100+
)
101+
},
102+
{
103+
UpsertExistingStep, u =>
104+
u.Calls<UpdateRequestDescriptor<UpdateTestDocument, object>, UpdateRequest<UpdateTestDocument, object>, UpdateResponse<UpdateTestDocument>>(
105+
v => new UpdateRequest<UpdateTestDocument, object>(typeof(UpdateTestDocument), v) { Script = TestScript, Upsert = new UpdateTestDocument { Counter = 100, Name = "Newly inserted" } },
106+
(v, d) => d.Script(TestScript).Upsert(new UpdateTestDocument { Counter = 100, Name = "Newly inserted" }),
107+
(v, c, f) => c.Update(Infer.Index<UpdateTestDocument>(), v, f),
108+
(v, c, f) => c.UpdateAsync(Infer.Index<UpdateTestDocument>(), v, f),
109+
(_, c, r) => c.Update(r),
110+
(_, c, r) => c.UpdateAsync(r)
111+
)
112+
},
113+
{
114+
GetAfterUpsertExistingStep, u =>
115+
u.Calls<GetRequestDescriptor<UpdateTestDocument>, GetRequest, GetResponse<UpdateTestDocument>>(
116+
v => new GetRequest(typeof(UpdateTestDocument), v),
117+
(v, d) => d,
118+
(v, c, f) => c.Get(Infer.Index<UpdateTestDocument>(), v, f),
119+
(v, c, f) => c.GetAsync(Infer.Index<UpdateTestDocument>(), v, f),
120+
(_, c, r) => c.Get<UpdateTestDocument>(r),
121+
(_, c, r) => c.GetAsync<UpdateTestDocument>(r)
122+
)
123+
},
124+
{
125+
UpsertNewStep, u =>
126+
u.Calls<UpdateRequestDescriptor<UpdateTestDocument, object>, UpdateRequest<UpdateTestDocument, object>, UpdateResponse<UpdateTestDocument>>(
127+
v => new UpdateRequest<UpdateTestDocument, object>(typeof(UpdateTestDocument), $"{v}-new") { Script = TestScript, Upsert = new UpdateTestDocument { Counter = 100, Name = "Newly inserted" } },
128+
(v, d) => d.Script(TestScript).Upsert(new UpdateTestDocument { Counter = 100, Name = "Newly inserted" }),
129+
(v, c, f) => c.Update(Infer.Index<UpdateTestDocument>(), $"{v}-new", f),
130+
(v, c, f) => c.UpdateAsync(Infer.Index<UpdateTestDocument>(), $"{v}-new", f),
131+
(_, c, r) => c.Update(r),
132+
(_, c, r) => c.UpdateAsync(r)
133+
)
134+
},
135+
{
136+
GetAfterUpsertNewStep, u =>
137+
u.Calls<GetRequestDescriptor<UpdateTestDocument>, GetRequest, GetResponse<UpdateTestDocument>>(
138+
v => new GetRequest(typeof(UpdateTestDocument), $"{v}-new"),
139+
(v, d) => d,
140+
(v, c, f) => c.Get(Infer.Index<UpdateTestDocument>(), $"{v}-new", f),
141+
(v, c, f) => c.GetAsync(Infer.Index<UpdateTestDocument>(), $"{v}-new", f),
142+
(_, c, r) => c.Get<UpdateTestDocument>(r),
143+
(_, c, r) => c.GetAsync<UpdateTestDocument>(r)
144+
)
145+
},
146+
})
147+
{
148+
}
149+
150+
[I]
151+
public async Task UpdateWithScriptResponse() => await Assert<UpdateResponse<UpdateTestDocument>>(UpdateWithScriptStep, (v, r) =>
152+
{
153+
r.IsValid.Should().BeTrue();
154+
r.Result.Should().Be(Result.Updated);
155+
});
156+
157+
[I]
158+
public async Task GetResponseAfterScriptedUpdateStep() => await Assert<GetResponse<UpdateTestDocument>>(GetAfterScriptedUpdateStep, (v, r) =>
159+
{
160+
r.IsValid.Should().BeTrue();
161+
r.Source.Name.Should().Be("Initial"); // The name should not have changed
162+
r.Source.Counter.Should().Be(5); // The script updates by 4
163+
r.Source.RenamedField.Should().BeNull(); // This hasn't been set yet and should not exist on the source
164+
});
165+
166+
[I]
167+
public async Task UpdateWithPartialResponse() => await Assert<UpdateResponse<UpdateTestDocument>>(UpdateWithPartialStep, (v, r) =>
168+
{
169+
r.IsValid.Should().BeTrue();
170+
r.Result.Should().Be(Result.Updated);
171+
});
172+
173+
[I]
174+
public async Task GetResponseAfterPartialUpdateStep() => await Assert<GetResponse<UpdateTestDocument>>(GetAfterPartialUpdateStep, (v, r) =>
175+
{
176+
r.IsValid.Should().BeTrue();
177+
r.Source.Name.Should().Be("Initial"); // The name should not have changed
178+
r.Source.Counter.Should().Be(5); // The count should still be five
179+
r.Source.RenamedField.Should().Be("Partial"); // The partial update should have added the partial value for RenamedField
180+
});
181+
182+
[I]
183+
public async Task UpdateWithPartialNoOpResponse() => await Assert<UpdateResponse<UpdateTestDocument>>(UpdateWithPartialStepTwo, (v, r) =>
184+
{
185+
r.IsValid.Should().BeTrue();
186+
r.Result.Should().Be(Result.NoOp);
187+
});
188+
189+
[I]
190+
public async Task UpsertExistingResponse() => await Assert<UpdateResponse<UpdateTestDocument>>(UpsertExistingStep, (v, r) =>
191+
{
192+
r.IsValid.Should().BeTrue();
193+
r.Result.Should().Be(Result.Updated);
194+
});
195+
196+
[I]
197+
public async Task GetResponseAfterUpsertExistingStep() => await Assert<GetResponse<UpdateTestDocument>>(GetAfterUpsertExistingStep, (v, r) =>
198+
{
199+
r.IsValid.Should().BeTrue();
200+
r.Source.Name.Should().Be("Initial"); // The name should not have changed
201+
r.Source.Counter.Should().Be(9); // The count should have been updated by the script
202+
r.Source.RenamedField.Should().Be("Partial"); // The RenamedField should not have changed
203+
});
204+
205+
[I]
206+
public async Task UpsertNewResponse() => await Assert<UpdateResponse<UpdateTestDocument>>(UpsertNewStep, (v, r) =>
207+
{
208+
r.IsValid.Should().BeTrue();
209+
r.Result.Should().Be(Result.Created);
210+
});
211+
212+
[I]
213+
public async Task GetResponseAfterUpsertNewStep() => await Assert<GetResponse<UpdateTestDocument>>(GetAfterUpsertNewStep, (v, r) =>
214+
{
215+
r.IsValid.Should().BeTrue();
216+
r.Source.Name.Should().Be("Newly inserted");
217+
r.Source.Counter.Should().Be(100);
218+
r.Source.RenamedField.Should().BeNull();
219+
});
220+
221+
private class UpdateTestDocument
222+
{
223+
[JsonPropertyName("another_field")]
224+
public string RenamedField { get; set; }
225+
public int Counter { get; set; }
226+
public string Name { get; set; }
227+
228+
public static UpdateTestDocument InitialValues => new() { Counter = 1, Name = "Initial" };
229+
}
230+
231+
private class UpdateTestDocumentPartial
232+
{
233+
[JsonPropertyName("another_field")]
234+
public string RenamedField { get; set; } = "Partial";
235+
}
236+
}

0 commit comments

Comments
 (0)