diff --git a/guides/search.md b/guides/search.md index 52eb39b120..ed8d022a97 100644 --- a/guides/search.md +++ b/guides/search.md @@ -151,18 +151,18 @@ var page1 = await client.SearchAsync(s => s .Query(_ => query) .Sort(_ => sort) .Size(2) - .PointInTime(p => p.PitId(pitResp.PitId).KeepAlive("1m"))); + .PointInTime(p => p.Id(pitResp.PitId).KeepAlive("1m"))); var page2 = await client.SearchAsync(s => s .Query(_ => query) .Sort(_ => sort) .Size(2) - .PointInTime(p => p.PitId(pitResp.PitId).KeepAlive("1m")) + .PointInTime(p => p.Id(pitResp.PitId).KeepAlive("1m")) .SearchAfter(page1.Hits.Last().Sorts)); var page3 = await client.SearchAsync(s => s .Query(_ => query) .Sort(_ => sort) .Size(2) - .PointInTime(p => p.PitId(pitResp.PitId).KeepAlive("1m")) + .PointInTime(p => p.Id(pitResp.PitId).KeepAlive("1m")) .SearchAfter(page2.Hits.Last().Sorts)); foreach (var doc in page1.Documents.Concat(page2.Documents).Concat(page3.Documents)) @@ -170,7 +170,7 @@ foreach (var doc in page1.Documents.Concat(page2.Documents).Concat(page3.Documen Console.WriteLine(doc.Title); } -await client.DeletePitAsync(p => p.PitIds(pitResp.PitId)); +await client.DeletePitAsync(p => p.PitId(pitResp.PitId)); ``` Note that a point-in-time is associated with an index or a set of index. So, when performing a search with a point-in-time, you DO NOT specify the index in the search. diff --git a/src/OpenSearch.Client/Search/Search/PointInTime/PointInTime.cs b/src/OpenSearch.Client/Search/Search/PointInTime/PointInTime.cs index b1b3c4e546..53a8eb2aec 100644 --- a/src/OpenSearch.Client/Search/Search/PointInTime/PointInTime.cs +++ b/src/OpenSearch.Client/Search/Search/PointInTime/PointInTime.cs @@ -30,7 +30,7 @@ public class PointInTimeDescriptor : DescriptorBase Assign(id, (a, v) => a.Id = v); + public PointInTimeDescriptor Id(string id) => Assign(id, (a, v) => a.Id = v); public PointInTimeDescriptor KeepAlive(Time keepAlive) => Assign(keepAlive, (a, v) => a.KeepAlive = v); } diff --git a/tests/Tests/Search/PointInTime/CreatePitApiTests.cs b/tests/Tests/Search/PointInTime/CreatePitApiTests.cs index d3810d7075..0ccecdb9cd 100644 --- a/tests/Tests/Search/PointInTime/CreatePitApiTests.cs +++ b/tests/Tests/Search/PointInTime/CreatePitApiTests.cs @@ -54,7 +54,7 @@ protected override LazyResponses ClientUsage() => Calls( (c, r) => c.CreatePitAsync(r) ); - protected override CreatePitDescriptor NewDescriptor() => new(CallIsolatedValue); + protected override CreatePitDescriptor NewDescriptor() => new(OpenSearch.Client.Indices.Index()); protected override void ExpectResponse(CreatePitResponse response) { @@ -65,10 +65,5 @@ protected override void ExpectResponse(CreatePitResponse response) response.Shards.Should().NotBeNull(); } - protected override void OnAfterCall(IOpenSearchClient client) - { - if (string.IsNullOrEmpty(_pitId)) return; - client.DeletePit(d => d.PitId(_pitId)); - _pitId = null; - } + protected override void OnAfterCall(IOpenSearchClient client) => client.DeletePit(d => d.PitId(_pitId)); } diff --git a/tests/Tests/Search/PointInTime/DeleteAllPitsApiTests.cs b/tests/Tests/Search/PointInTime/DeleteAllPitsApiTests.cs index 74df4e78fb..dbf50f1118 100644 --- a/tests/Tests/Search/PointInTime/DeleteAllPitsApiTests.cs +++ b/tests/Tests/Search/PointInTime/DeleteAllPitsApiTests.cs @@ -22,11 +22,11 @@ namespace Tests.Search.PointInTime; [SkipVersion("<2.4.0", "Point-In-Time search support was added in version 2.4.0")] public class DeleteAllPitsApiTests - : ApiIntegrationTestBase + : ApiIntegrationTestBase { private readonly List _pitIds = new(); - public DeleteAllPitsApiTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) { } + public DeleteAllPitsApiTests(WritableCluster cluster, EndpointUsage usage) : base(cluster, usage) { } protected override bool ExpectIsValid => true; @@ -65,8 +65,6 @@ protected override void ExpectResponse(DeleteAllPitsResponse response) protected override void OnBeforeCall(IOpenSearchClient client) { - _pitIds.Clear(); - for (var i = 0; i < 5; i++) { var pit = Client.CreatePit(OpenSearch.Client.Indices.Index(), c => c.KeepAlive("1h")); diff --git a/tests/Tests/Search/PointInTime/DeletePitApiTests.cs b/tests/Tests/Search/PointInTime/DeletePitApiTests.cs index 637c8c3e37..95e50229c2 100644 --- a/tests/Tests/Search/PointInTime/DeletePitApiTests.cs +++ b/tests/Tests/Search/PointInTime/DeletePitApiTests.cs @@ -67,10 +67,10 @@ protected override void ExpectResponse(DeletePitResponse response) protected override void OnBeforeCall(IOpenSearchClient client) { - var pit = Client.CreatePit(OpenSearch.Client.Indices.Index(), c => c.KeepAlive("1h")); + var pit = client.CreatePit(OpenSearch.Client.Indices.Index(), c => c.KeepAlive("1h")); if (!pit.IsValid) throw new Exception("Setup: Initial PIT failed."); - _pitId = pit.PitId ?? _pitId; + _pitId = pit.PitId; } } diff --git a/tests/Tests/Search/PointInTime/GetAllPitsApiTests.cs b/tests/Tests/Search/PointInTime/GetAllPitsApiTests.cs index 095472a792..b63379c71b 100644 --- a/tests/Tests/Search/PointInTime/GetAllPitsApiTests.cs +++ b/tests/Tests/Search/PointInTime/GetAllPitsApiTests.cs @@ -24,7 +24,7 @@ namespace Tests.Search.PointInTime; public class GetAllPitsApiTests : ApiIntegrationTestBase { - private readonly List _pits = new(); + private List<(string id, long creationTime)> _pits = new(); public GetAllPitsApiTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) { } @@ -52,32 +52,31 @@ protected override LazyResponses ClientUsage() => Calls( protected override void ExpectResponse(GetAllPitsResponse response) { + _pits.Should().HaveCount(5); response.ShouldBeValid(); response.Pits.Should() .NotBeNull() .And.HaveCount(5) .And.BeEquivalentTo(_pits.Select(p => new PitDetail { - PitId = p.PitId, - CreationTime = p.CreationTime, + PitId = p.id, + CreationTime = p.creationTime, KeepAlive = 60 * 60 * 1000 })); } protected override void OnBeforeCall(IOpenSearchClient client) { - _pits.Clear(); - for (var i = 0; i < 5; i++) { - var pit = Client.CreatePit(OpenSearch.Client.Indices.Index(), c => c.KeepAlive("1h")); + var pit = client.CreatePit(OpenSearch.Client.Indices.Index(), c => c.KeepAlive("1h")); if (!pit.IsValid) throw new Exception("Setup: Initial PIT failed."); - _pits.Add(pit); + _pits.Add((pit.PitId, pit.CreationTime)); } } protected override void OnAfterCall(IOpenSearchClient client) => - client.DeletePit(d => d.PitId(_pits.Select(p => p.PitId))); + client.DeletePit(d => d.PitId(_pits.Select(p => p.id))); } diff --git a/tests/Tests/Search/PointInTime/PitSearchApiTests.cs b/tests/Tests/Search/PointInTime/PitSearchApiTests.cs new file mode 100644 index 0000000000..4877909c99 --- /dev/null +++ b/tests/Tests/Search/PointInTime/PitSearchApiTests.cs @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: Apache-2.0 +* +* The OpenSearch Contributors require contributions made to +* this file be licensed under the Apache-2.0 license or a +* compatible open source license. +*/ + +using System; +using System.Linq; +using FluentAssertions; +using OpenSearch.Client; +using OpenSearch.Net; +using OpenSearch.OpenSearch.Xunit.XunitPlumbing; +using Tests.Core.Extensions; +using Tests.Core.ManagedOpenSearch.Clusters; +using Tests.Framework.EndpointTests; +using Tests.Framework.EndpointTests.TestState; + +namespace Tests.Search.PointInTime; + +[SkipVersion("<2.4.0", "Point-In-Time search support was added in version 2.4.0")] +public sealed class PitSearchApiTests : + ApiIntegrationTestBase< + WritableCluster, + ISearchResponse, + ISearchRequest, + SearchDescriptor, + SearchRequest + > +{ + public PitSearchApiTests(WritableCluster cluster, EndpointUsage usage) : base(cluster, usage) { } + + private string _pitId = "default-for-unit-tests"; + + protected override object ExpectJson => new + { + query = new + { + match_all = new { } + }, + pit = new + { + id = _pitId, + keep_alive = "1h" + }, + track_total_hits = true + }; + + protected override HttpMethod HttpMethod => HttpMethod.POST; + protected override string UrlPath => "/_search"; + + protected override Func, ISearchRequest> Fluent => s => s + .Query(q => q.MatchAll()) + .PointInTime(p => p + .Id(_pitId) + .KeepAlive("1h")) + .TrackTotalHits(); + + protected override bool ExpectIsValid => true; + protected override int ExpectStatusCode => 200; + + protected override SearchRequest Initializer => new(null) + { + Query = new QueryContainer(new MatchAllQuery()), + PointInTime = new OpenSearch.Client.PointInTime + { + Id = _pitId, + KeepAlive = "1h" + }, + TrackTotalHits = true + }; + + protected override SearchDescriptor NewDescriptor() => new(null); + + protected override void ExpectResponse(ISearchResponse response) + { + response.ShouldBeValid(); + response.Total.Should().Be(10); + response + .Documents + .Should() + .NotBeNull() + .And.HaveCount(10) + .And.BeEquivalentTo(Enumerable.Range(0, 10).Select(i => new Doc { Id = i })); + } + + protected override void OnBeforeCall(IOpenSearchClient client) + { + var bulkResp = client.Bulk(b => b + .Index(CallIsolatedValue) + .IndexMany(Enumerable.Range(0, 10).Select(i => new Doc { Id = i })) + .Refresh(Refresh.WaitFor)); + bulkResp.ShouldBeValid(); + + var pitResp = client.CreatePit(CallIsolatedValue, p => p.KeepAlive("1h")); + pitResp.ShouldBeValid(); + _pitId = pitResp.PitId; + + bulkResp = client.Bulk(b => b + .Index(CallIsolatedValue) + .IndexMany(Enumerable.Range(10, 10).Select(i => new Doc { Id = i })) + .Refresh(Refresh.WaitFor)); + bulkResp.ShouldBeValid(); + } + + protected override void OnAfterCall(IOpenSearchClient client) => client.DeletePit(d => d.PitId(_pitId)); + + protected override LazyResponses ClientUsage() => Calls( + (c, f) => c.Search(f), + (c, f) => c.SearchAsync(f), + (c, r) => c.Search(r), + (c, r) => c.SearchAsync(r) + ); + + public class Doc + { + public long Id { get; set; } + } +} diff --git a/tests/Tests/Search/PointInTime/PointInTimeIntegrationTests.cs b/tests/Tests/Search/PointInTime/PointInTimeIntegrationTests.cs deleted file mode 100644 index 4096804e68..0000000000 --- a/tests/Tests/Search/PointInTime/PointInTimeIntegrationTests.cs +++ /dev/null @@ -1,106 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 -* -* The OpenSearch Contributors require contributions made to -* this file be licensed under the Apache-2.0 license or a -* compatible open source license. -*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using FluentAssertions; -using OpenSearch.Client; -using OpenSearch.Net; -using OpenSearch.OpenSearch.Xunit.XunitPlumbing; -using Tests.Core.Extensions; -using Tests.Core.ManagedOpenSearch.Clusters; - -namespace Tests.Search.PointInTime; - -[SkipVersion("<2.4.0", "Point-In-Time search support was added in version 2.4.0")] -public sealed class PointInTimeIntegrationTests : IClusterFixture, IDisposable -{ - private readonly WritableCluster _cluster; - - public PointInTimeIntegrationTests(WritableCluster cluster) => _cluster = cluster; - - public void Dispose() => _cluster.Client.DeleteAllPits(); - - [I] public async Task PointInTimeQuery() - { - var client = _cluster.Client; - var index = nameof(PointInTimeQuery).ToLowerInvariant(); - - var bulkResp = await client.BulkAsync(b => b - .Index(index) - .IndexMany(Enumerable.Range(0, 10).Select(i => new Doc { Id = i })) - .Refresh(Refresh.WaitFor)); - bulkResp.ShouldBeValid(); - - var pitResp = await client.CreatePitAsync(index, p => p.KeepAlive("1h")); - pitResp.ShouldBeValid(); - - bulkResp = await client.BulkAsync(b => b - .Index(index) - .IndexMany(Enumerable.Range(10, 10).Select(i => new Doc { Id = i })) - .Refresh(Refresh.WaitFor)); - bulkResp.ShouldBeValid(); - - var liveSearch = await client.SearchAsync(s => s - .Index(index) - .MatchAll() - .TrackTotalHits()); - liveSearch.ShouldBeValid(); - liveSearch.Total.Should().Be(20); - - var pitSearch = await client.SearchAsync(s => s - .MatchAll() - .PointInTime(p => p.PitId(pitResp.PitId)) - .TrackTotalHits()); - pitSearch.ShouldBeValid(); - pitSearch.Total.Should().Be(10); - pitSearch.Documents.Should().AllSatisfy(d => d.Id.Should().BeLessThan(10)); - - var deleteResp = await client.DeletePitAsync(d => d.PitId(pitResp.PitId)); - deleteResp.ShouldBeValid(); - deleteResp.Pits.Should().BeEquivalentTo(new[] - { - new DeletedPit - { - PitId = pitResp.PitId, - Successful = true - } - }); - } - - [I] public async Task PointInTimeSearchExtendKeepAlive() - { - var client = _cluster.Client; - var index = nameof(PointInTimeSearchExtendKeepAlive).ToLowerInvariant(); - - var createIndexResp = await client.Indices.CreateAsync(index); - createIndexResp.ShouldBeValid(); - - var createResp = await client.CreatePitAsync(index, c => c.KeepAlive("1h")); - createResp.ShouldBeValid(); - - var searchResp = await client.SearchAsync(s => s - .MatchAll() - .PointInTime(p => p.PitId(createResp.PitId).KeepAlive("4h"))); - searchResp.ShouldBeValid(); - - var getAllResp = await client.GetAllPitsAsync(); - getAllResp.ShouldBeValid(); - var pit = getAllResp.Pits.FirstOrDefault(p => p.PitId == createResp.PitId); - - pit.Should().NotBeNull(); - pit.CreationTime.Should().Be(createResp.CreationTime); - pit.KeepAlive.Should().Be(4 * 60 * 60 * 1000); - } - - private class Doc - { - public long Id { get; set; } - } -}