Light Mode

Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Upgrade to Elastic.Clients.Elasticsearch (ES 9.x)#216

Open
niemyjski wants to merge 82 commits intomainfrom
feature/elastic-client
Open

Upgrade to Elastic.Clients.Elasticsearch (ES 9.x)#216
niemyjski wants to merge 82 commits intomainfrom
feature/elastic-client

Conversation

Copy link
Member

niemyjski commented Feb 28, 2026 *
edited
Loading

Summary

Major upgrade migrating from NEST (Elasticsearch.Net 7.x) to Elastic.Clients.Elasticsearch (official ES 8.x/9.x client). This is a breaking change release (v8.0) that replaces the entire Elasticsearch client layer and serialization stack.

Depends on FoundatioFx/Foundatio.Parsers#84

Core Changes

  • Elasticsearch Client: IElasticClient (NEST) -> ElasticsearchClient (Elastic.Clients.Elasticsearch)
  • Serialization: Newtonsoft.Json (JToken/JObject) -> System.Text.Json (JsonNode/JsonElement) throughout
  • JsonPatch: Full port from Newtonsoft to System.Text.Json with RFC 6902 compliance
  • Query DSL: All query builders ported from QueryContainer/SearchDescriptor to Query/SearchRequestDescriptor
  • Index Configuration: All mapping/settings APIs ported to new descriptor types
  • Docker: Elasticsearch/Kibana images updated to 9.3.1

Breaking API Changes

All breaking changes are direct 1:1 type replacements required by the client migration:

Old (NEST) New (Elastic.Clients)
IElasticClient ElasticsearchClient
ConnectionSettings ElasticsearchClientSettings
IConnectionPool NodePool
QueryContainer Query
SearchDescriptor SearchRequestDescriptor
CreateIndexDescriptor CreateIndexRequestDescriptor
JToken/JObject JsonNode/JsonObject

Known Limitation: PartialPatch Cannot Set Fields to Null

This is a behavioral change from NEST. The DefaultSourceSerializer in Elastic.Clients.Elasticsearch sets JsonIgnoreCondition.WhenWritingNull by default, which means any property explicitly set to null in a PartialPatch document is silently stripped from the serialized JSON before it reaches Elasticsearch. The field retains its original value instead of being set to null.

// This will NOT set companyName to null -- the null is dropped silently
await repository.PatchAsync(id, new PartialPatch(new { companyName = (string)null }));

Root cause: Decompiled source of DefaultSourceSerializerOptionsProvider.MutateOptions in Elastic.Clients.Elasticsearch 9.3.1 confirms this is intentional:

options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; // set by the ES client

Why we cannot work around it globally: Setting JsonIgnoreCondition.Never on the SourceSerializer (the obvious fix) is not viable because:

  1. It causes all null properties on all documents to be serialized on every index/update operation, bloating payloads and potentially overwriting fields in multi-writer scenarios
  2. Some Elastic request types with object-typed fields get routed through the SourceSerializer and fail with Never (as reported in the upstream issue)
  3. Foundatio.Repositories is a library -- changing a global serializer default would silently affect all consumer documents

Upstream tracking: elastic/elasticsearch-net#8763 -- still open. We have commented on the issue with our specific use case.

Workarounds for consumers who need to set a field to null:

  • Use ScriptPatch: new ScriptPatch("ctx._source.companyName = null;")
  • Use JsonPatch (RFC 6902): new JsonPatch(new PatchDocument(new ReplaceOperation { Path = "companyName", Value = null }))

A regression test (PartialPatchAsync_WithNullField_RetainsOriginalValue) documents this behavior and will flip automatically once the upstream issue is resolved.

Quality & Robustness Improvements

  • Zero build warnings (down from 190): Fixed all xUnit1051, CS8002, AsyncFixer02 warnings
  • 441 total tests passing (96 core + 345 ES integration, 0 skipped, 0 failed)
  • All previously-skipped tests unskipped and passing (7 tests that were skipped on main have been fixed and re-enabled)
  • Fixed query timeout format to use ES duration syntax (30s) instead of .NET TimeSpan format
  • Fixed DoubleSystemTextJsonConverter.Read() (was throwing NotImplementedException)
  • Added NotSupportedException for JSONPath ($-prefixed) paths in patch operations with clear error message
  • Added null guard in Operation.Build() for malformed patch JSON
  • Strict validation in PatchDocument.Load() -- now throws on non-object elements
  • Fixed null-conditional constant conditions in JsonDiffer, LoggerExtensions, ElasticReindexer
  • Fixed potential null dereferences in test assertions (split Assert.NotNull(x?.Y) into proper two-step assertions)
  • Fixed GetAliasIndexCount to only treat HTTP 404 as "no aliases" and throw on real failures
  • Added PatchDocument.Add/Replace convenience overloads for string, int, long, double, bool
  • Improved RuntimeFieldsQueryBuilder test to verify builder output, not just input state
  • Removed all dead/commented-out code
  • Passed TestContext.Current.CancellationToken to all test async calls for proper cancellation support
  • Clean dotnet format -- no unused usings or style issues
  • Made ConfigureFoundatioRepositoryDefaults() idempotent to prevent duplicate converter registration
  • Fixed TopHitsAggregate._hits field to initialize with empty list in parameterless constructor
  • Fixed ElasticReindexer.CreateRangeQuery to use startTime.Value after HasValue guard for explicit intent

Review Feedback Addressed

All github-code-quality and Copilot review comments have been resolved:

  • partsToCreate unused container removed (was already fixed in prior commit)
  • Constant condition warnings in JsonDiffer.GetHashCode fixed with explicit null guard
  • Redundant null-conditional in LoggerExtensions.LogRequest fixed
  • startTime.Value changed in ElasticReindexer for explicit intent after null guard
  • as casts replaced with direct casts in test assertions
  • Query timeout format fixed from .ToString() to ES duration format
  • Operation.Build null guard added
  • PatchDocument.Load strict validation added
  • GetAliasIndexCount error handling improved
  • Stale NEST-style mapping syntax in docs updated to ES9 API
  • Silent bucket type skip in GetKeyedBuckets replaced with explicit error

niemyjski added 10 commits April 14, 2025 15:05
- Resolved 9 merge conflicts keeping new Elastic.Clients.Elasticsearch API
- Adapted DefaultSortQueryBuilder from NEST to new client (SortOptions pattern)
- Fixed Xunit.Abstractions -> Xunit namespace (xUnit v3 migration)
- Fixed Task -> ValueTask for InitializeAsync/DisposeAsync
- Removed ct: parameter from RefreshAsync (not in new client API)
- Adapted NestedFieldTests aggregation API to new client fluent pattern
- Took newer package versions from main (RandomData 2.0.1, Jint 4.6.0)
Ensures package versions start at 8.0 or higher. This aligns versioning with significant breaking changes, such as the upgrade to a new major Elasticsearch client, justifying a major version increment for the repository packages.
Updates Elasticsearch client usage across various index management and repository operations to align with "Opus" client API changes and address compatibility issues.

* Changes wildcard index resolution from `ResolveIndexAsync` to `GetAsync` to avoid an issue where the ES 9.x client sends a body that Elasticsearch rejects.
* Refines `Indices` parameter passing for delete operations using `Indices.Parse` or `Indices.Index`.
* Ensures `Reopen()` is invoked when updating index settings that necessitate it.
* Improves bulk operation reliability by checking `hit.IsValid` rather than specific status codes.
* Corrects reindex tests to account for `Version` (SeqNo/PrimaryTerm) not being preserved across reindex operations.
* Includes minor project file cleanup and type aliasing for improved readability.
niemyjski self-assigned this Feb 28, 2026
niemyjski requested a review from Copilot February 28, 2026 05:32
niemyjski added the enhancement label Feb 28, 2026
Copilot started reviewing on behalf of niemyjski February 28, 2026 05:33 View session
github-code-quality bot found potential problems Feb 28, 2026
Copilot AI reviewed Feb 28, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates Foundatio.Repositories' Elasticsearch integration and supporting utilities/tests to use Elastic.Clients.Elasticsearch (System.Text.Json-based) instead of NEST/Elasticsearch.Net/Newtonsoft, aligning serialization, query building, and index/mapping APIs with the new client.

Changes:

  • Migrate repository/query/index configuration code from NEST types (IElasticClient, SearchDescriptor, QueryContainer, etc.) to Elastic.Clients.Elasticsearch equivalents.
  • Convert JsonPatch implementation and related tests from Newtonsoft (JToken) to System.Text.Json (JsonNode/Utf8JsonWriter).
  • Update test infrastructure/helpers and docker-compose to run against newer Elasticsearch/Kibana images; adjust versioning/build settings.

Reviewed changes

Copilot reviewed 104 out of 104 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/Foundatio.Repositories.Tests/JsonPatch/JsonPatchTests.cs Switch JsonPatch tests to System.Text.Json JsonNode and update serialization assertions.
tests/Foundatio.Repositories.Elasticsearch.Tests/VersionedTests.cs Update client usage and request/response validation for new Elasticsearch client.
tests/Foundatio.Repositories.Elasticsearch.Tests/RepositoryTests.cs Refresh calls updated for new client API.
tests/Foundatio.Repositories.Elasticsearch.Tests/Repositories/Queries/EmailAddressQuery.cs Replace NEST query DSL with Elastic.Clients.Elasticsearch.QueryDsl queries.
tests/Foundatio.Repositories.Elasticsearch.Tests/Repositories/Queries/CompanyQuery.cs Replace NEST term/terms queries with new client equivalents.
tests/Foundatio.Repositories.Elasticsearch.Tests/Repositories/Queries/AgeQuery.cs Replace NEST term/terms queries with new client equivalents.
tests/Foundatio.Repositories.Elasticsearch.Tests/Repositories/ParentRepository.cs Update parent-child discriminator assignment for new JoinField type.
tests/Foundatio.Repositories.Elasticsearch.Tests/Repositories/Models/Parent.cs Expose discriminator as public property and set JSON name for join field.
tests/Foundatio.Repositories.Elasticsearch.Tests/Repositories/Models/Child.cs Expose discriminator as public property and set JSON name for join field.
tests/Foundatio.Repositories.Elasticsearch.Tests/Repositories/EmployeeWithCustomFieldsRepository.cs Update missing-field filters to new query DSL.
tests/Foundatio.Repositories.Elasticsearch.Tests/Repositories/EmployeeRepository.cs Update missing-field filters to new query DSL.
tests/Foundatio.Repositories.Elasticsearch.Tests/Repositories/Configuration/MyAppElasticConfiguration.cs Replace connection pool/client setup with ElasticsearchClientSettings/NodePool.
tests/Foundatio.Repositories.Elasticsearch.Tests/Repositories/Configuration/Indexes/ParentChildIndex.cs Port index creation/mappings to new client descriptors and join mapping API.
tests/Foundatio.Repositories.Elasticsearch.Tests/Repositories/Configuration/Indexes/MonthlyLogEventIndex.cs Port create-index configuration to new descriptors.
tests/Foundatio.Repositories.Elasticsearch.Tests/Repositories/Configuration/Indexes/MonthlyFileAccessHistoryIndex.cs Port create-index configuration to new descriptors.
tests/Foundatio.Repositories.Elasticsearch.Tests/Repositories/Configuration/Indexes/IdentityIndex.cs Port mapping/configuration to new mapping types.
tests/Foundatio.Repositories.Elasticsearch.Tests/Repositories/Configuration/Indexes/EmployeeWithCustomFieldsIndex.cs Port complex mappings (aliases, nested/object props) to new mapping APIs.
tests/Foundatio.Repositories.Elasticsearch.Tests/Repositories/Configuration/Indexes/EmployeeIndex.cs Port employee mappings and versioned index hooks to new mapping APIs.
tests/Foundatio.Repositories.Elasticsearch.Tests/Repositories/Configuration/Indexes/DailyLogEventIndex.cs Port daily index mapping/config to new mapping APIs.
tests/Foundatio.Repositories.Elasticsearch.Tests/Repositories/Configuration/Indexes/DailyFileAccessHistoryIndex.cs Port create-index configuration to new descriptors.
tests/Foundatio.Repositories.Elasticsearch.Tests/Repositories/Configuration/ElasticsearchJsonNetSerializer.cs Disable old JsonNet serializer shim (kept as reference).
tests/Foundatio.Repositories.Elasticsearch.Tests/Repositories/ChildRepository.cs Update parent-child discriminator assignment for new JoinField type.
tests/Foundatio.Repositories.Elasticsearch.Tests/ReadOnlyRepositoryTests.cs Adjust snapshot paging assertions and scroll cleanup for new client.
tests/Foundatio.Repositories.Elasticsearch.Tests/QueryTests.cs Update logging/options usage for queries.
tests/Foundatio.Repositories.Elasticsearch.Tests/QueryBuilderTests.cs Adjust runtime fields test for new request/runtime mapping behavior.
tests/Foundatio.Repositories.Elasticsearch.Tests/ParentChildTests.cs Ensure immediate consistency and adjust query logging for parent/child tests.
tests/Foundatio.Repositories.Elasticsearch.Tests/NestedFieldTests.cs Port nested aggregations API usage to new client aggregations builder.
tests/Foundatio.Repositories.Elasticsearch.Tests/MigrationTests.cs Refresh calls updated for new client API.
tests/Foundatio.Repositories.Elasticsearch.Tests/Foundatio.Repositories.Elasticsearch.Tests.csproj Remove NEST JsonNet serializer package reference.
tests/Foundatio.Repositories.Elasticsearch.Tests/Extensions/ElasticsearchExtensions.cs Update alias helpers for new client response shapes and add convenience methods.
tests/Foundatio.Repositories.Elasticsearch.Tests/ElasticRepositoryTestBase.cs Add wildcard index deletion helper compatible with ES destructive settings.
tests/Foundatio.Repositories.Elasticsearch.Tests/CustomFieldTests.cs Adjust custom field value assertions to handle JsonElement-backed values.
tests/Foundatio.Repositories.Elasticsearch.Tests/AggregationQueryTests.cs Add/adjust aggregation tests for new client aggregation response conversion/roundtrips.
src/Foundatio.Repositories/Models/Aggregations/ObjectValueAggregate.cs Support JsonNode/JsonElement values in aggregate value conversion.
src/Foundatio.Repositories/JsonPatch/TestOperation.cs Port JsonPatch operation to System.Text.Json read/write.
src/Foundatio.Repositories/JsonPatch/ReplaceOperation.cs Port JsonPatch operation to System.Text.Json read/write.
src/Foundatio.Repositories/JsonPatch/RemoveOperation.cs Port JsonPatch operation to System.Text.Json read/write.
src/Foundatio.Repositories/JsonPatch/PatchDocumentConverter.cs Implement System.Text.Json converter for PatchDocument.
src/Foundatio.Repositories/JsonPatch/PatchDocument.cs Port PatchDocument model/serialization to System.Text.Json.
src/Foundatio.Repositories/JsonPatch/Operation.cs Port operation base class to System.Text.Json writer/reader APIs.
src/Foundatio.Repositories/JsonPatch/MoveOperation.cs Port JsonPatch operation to System.Text.Json read/write.
src/Foundatio.Repositories/JsonPatch/JsonPatcher.cs Port patching engine to JsonNode, plus pointer navigation helpers.
src/Foundatio.Repositories/JsonPatch/JsonDiffer.cs Port diff generation to JsonNode and System.Text.Json output.
src/Foundatio.Repositories/JsonPatch/CopyOperation.cs Port JsonPatch operation to System.Text.Json read/write.
src/Foundatio.Repositories/JsonPatch/AddOperation.cs Port JsonPatch operation to System.Text.Json read/write.
src/Foundatio.Repositories/Extensions/StringExtensions.cs Remove ToJTokenPath helper (Newtonsoft-specific).
src/Foundatio.Repositories/Extensions/AggregationsExtensions.cs Adjust keyed bucket extraction to handle multiple generic bucket key types.
src/Foundatio.Repositories.Elasticsearch/Repositories/MigrationStateRepository.cs Port migration index mapping/config to new mapping/index descriptors.
src/Foundatio.Repositories.Elasticsearch/Repositories/IParentChildDocument.cs Switch JoinField import to new client package.
src/Foundatio.Repositories.Elasticsearch/Repositories/ElasticReindexer.cs Port reindex orchestration (tasks, aliases, failures) to new client APIs.
src/Foundatio.Repositories.Elasticsearch/Repositories/ElasticReadOnlyRepositoryBase.cs Port search/mget/exists/scroll/async search flows to new client APIs.
src/Foundatio.Repositories.Elasticsearch/Queries/Builders/SortQueryBuilder.cs Change sort handling to store sorts in context data for later application.
src/Foundatio.Repositories.Elasticsearch/Queries/Builders/SoftDeletesQueryBuilder.cs Update has_parent query usage and parent type naming for new client.
src/Foundatio.Repositories.Elasticsearch/Queries/Builders/SearchAfterQueryBuilder.cs Rework applying/reversing sorts + ID tiebreaker for search_after paging.
src/Foundatio.Repositories.Elasticsearch/Queries/Builders/RuntimeFieldsQueryBuilder.cs Port runtime field mapping to RuntimeMappings dictionary model.
src/Foundatio.Repositories.Elasticsearch/Queries/Builders/ParentQueryBuilder.cs Port has_parent query construction to new DSL.
src/Foundatio.Repositories.Elasticsearch/Queries/Builders/PageableQueryBuilder.cs Port paging options to From/SearchAfter with FieldValue conversion.
src/Foundatio.Repositories.Elasticsearch/Queries/Builders/IdentityQueryBuilder.cs Port ids query/exclude ids behavior to new DSL types.
src/Foundatio.Repositories.Elasticsearch/Queries/Builders/IElasticQueryBuilder.cs Core query-builder context now uses Query + SearchRequestDescriptor.
src/Foundatio.Repositories.Elasticsearch/Queries/Builders/FieldIncludesQueryBuilder.cs Port source includes/excludes to new SourceConfig/SourceFilter.
src/Foundatio.Repositories.Elasticsearch/Queries/Builders/FieldConditionsQueryBuilder.cs Port term/terms/must_not/exists queries to new DSL with FieldValue.
src/Foundatio.Repositories.Elasticsearch/Queries/Builders/ExpressionQueryBuilder.cs Store sorts in context data instead of applying directly to search descriptor.
src/Foundatio.Repositories.Elasticsearch/Queries/Builders/ElasticFilterQueryBuilder.cs Change elastic filter option type from QueryContainer to Query.
src/Foundatio.Repositories.Elasticsearch/Queries/Builders/DefaultSortQueryBuilder.cs Ensure default ID sort is always present (via context data).
src/Foundatio.Repositories.Elasticsearch/Queries/Builders/DateRangeQueryBuilder.cs Port date range query properties to new names (Gte/Lte).
src/Foundatio.Repositories.Elasticsearch/Queries/Builders/ChildQueryBuilder.cs Port has_child query construction to new DSL.
src/Foundatio.Repositories.Elasticsearch/Options/ElasticCommandOptions.cs Switch Elasticsearch imports to new client package.
src/Foundatio.Repositories.Elasticsearch/Jobs/SnapshotJob.cs Port snapshot creation/status polling to new snapshot APIs.
src/Foundatio.Repositories.Elasticsearch/Jobs/ReindexWorkItemHandler.cs Port handler to accept ElasticsearchClient.
src/Foundatio.Repositories.Elasticsearch/Jobs/CleanupSnapshotJob.cs Port snapshot listing/deletion to new snapshot APIs.
src/Foundatio.Repositories.Elasticsearch/Jobs/CleanupIndexesJob.cs Port index enumeration/deletion to new indices APIs.
src/Foundatio.Repositories.Elasticsearch/Foundatio.Repositories.Elasticsearch.csproj Normalize project file header/formatting (and supports other changes).
src/Foundatio.Repositories.Elasticsearch/Extensions/ResolverExtensions.cs Port sort resolution from IFieldSort to SortOptions.
src/Foundatio.Repositories.Elasticsearch/Extensions/LoggerExtensions.cs Port logging helpers to ElasticsearchResponse and new ApiCall details.
src/Foundatio.Repositories.Elasticsearch/Extensions/IBodyWithApiCallDetailsExtensions.cs Update raw deserialization helper and add OriginalException() accessor.
src/Foundatio.Repositories.Elasticsearch/Extensions/FindHitExtensions.cs Handle new sort value representation (FieldValue) and update reverse-sort logic.
src/Foundatio.Repositories.Elasticsearch/Extensions/ElasticLazyDocument.cs Replace NEST LazyDocument reflection with new hit/source-based lazy deserialization.
src/Foundatio.Repositories.Elasticsearch/ElasticUtility.cs Port snapshot/index listing and snapshot creation to new APIs.
src/Foundatio.Repositories.Elasticsearch/CustomFields/StandardFieldTypes/StringFieldType.cs Update custom field mapping signature to PropertyFactory function.
src/Foundatio.Repositories.Elasticsearch/CustomFields/StandardFieldTypes/LongFieldType.cs Update custom field mapping signature to PropertyFactory function.
src/Foundatio.Repositories.Elasticsearch/CustomFields/StandardFieldTypes/KeywordFieldType.cs Update custom field mapping signature to PropertyFactory function.
src/Foundatio.Repositories.Elasticsearch/CustomFields/StandardFieldTypes/IntegerFieldType.cs Update custom field mapping signature to PropertyFactory function.
src/Foundatio.Repositories.Elasticsearch/CustomFields/StandardFieldTypes/FloatFieldType.cs Update custom field mapping signature to PropertyFactory function.
src/Foundatio.Repositories.Elasticsearch/CustomFields/StandardFieldTypes/DoubleFieldType.cs Update custom field mapping signature to PropertyFactory function.
src/Foundatio.Repositories.Elasticsearch/CustomFields/StandardFieldTypes/DateFieldType.cs Update custom field mapping signature to PropertyFactory function.
src/Foundatio.Repositories.Elasticsearch/CustomFields/StandardFieldTypes/BooleanFieldType.cs Update custom field mapping signature to PropertyFactory function.
src/Foundatio.Repositories.Elasticsearch/CustomFields/ICustomFieldType.cs Update mapping contract to return Func, IProperty>.
src/Foundatio.Repositories.Elasticsearch/CustomFields/CustomFieldDefinitionRepository.cs Port mapping/index config and remove NEST-specific constructs.
src/Foundatio.Repositories.Elasticsearch/Configuration/VersionedIndex.cs Port alias/index/mapping update flows to new indices/mapping APIs.
src/Foundatio.Repositories.Elasticsearch/Configuration/MonthlyIndex.cs Port monthly index config/mapping to new APIs.
src/Foundatio.Repositories.Elasticsearch/Configuration/Index.cs Port index lifecycle (create/update/delete/settings) to new APIs and wildcard delete handling.
src/Foundatio.Repositories.Elasticsearch/Configuration/IIndex.cs Update settings/mapping method signatures for new client descriptors.
src/Foundatio.Repositories.Elasticsearch/Configuration/IElasticConfiguration.cs Switch configuration client type to ElasticsearchClient.
src/Foundatio.Repositories.Elasticsearch/Configuration/ElasticConfiguration.cs Port client creation/settings/pool types to new client.
src/Foundatio.Repositories.Elasticsearch/Configuration/DynamicIndex.cs Port dynamic mapping configuration to new mapping API.
src/Foundatio.Repositories.Elasticsearch/Configuration/DailyIndex.cs Port daily index creation/aliasing/mapping update flows to new APIs.
samples/Foundatio.SampleApp/Server/Repositories/Configuration/SampleAppElasticConfiguration.cs Port sample app configuration to new client settings/pool types.
samples/Foundatio.SampleApp/Server/Repositories/Configuration/Indexes/GameReviewIndex.cs Port sample index settings/mapping configuration to new APIs.
docker-compose.yml Update Elasticsearch/Kibana images to 9.3.1.
build/common.props Set MinVer minimum major/minor to 8.0.

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

niemyjski added 2 commits February 28, 2026 06:56
- Restore JSONPath support in JsonPatcher with built-in filter evaluator
(no external dep) and re-add 3 previously deleted JSONPath tests
- Re-add 3 pagination tests deleted during upgrade (no-duplicate paging)
- Enable TopHits aggregation round-trip for caching: serialize raw hit JSON
in new Hits property; update both JSON converters to handle tophits type
- Implement ElasticUtility stubs: WaitForTaskAsync, WaitForSafeToSnapshotAsync,
DeleteSnapshotsAsync, DeleteIndicesAsync, and complete CreateSnapshotAsync
- Fix Foundatio.Parsers.ElasticQueries package ref to published pre-release
so CI builds without source reference override
- Remove CA2264 no-op ThrowIfNull on non-nullable param in IElasticQueryBuilder
- Delete PipelineTests.cs and ElasticsearchJsonNetSerializer.cs (dead code)
- Update all docs to ES9 API: ConnectionPool, ConnectionSettings, ConfigureIndex
and ConfigureIndexMapping void return, property mapping syntax, IsValidResponse
- Add upgrading-to-es9.md migration guide with full breaking changes checklist

Made-with: Cursor
Upgrades the `Foundatio.Parsers.ElasticQueries` package to its latest 8.0.0-preview version. This update supports the ongoing migration to a new major version of the Elasticsearch client.
github-code-quality bot found potential problems Feb 28, 2026
Removes Rider-specific project files and updates .gitignore.
Adjusts build configuration to conditionally reference Foundatio.Repositories source.
Refines nullability checks in Elasticsearch logger extensions and improves JSON patch logic with better pattern matching, simplified path creation, and robust error handling for unknown operations.
Includes a fix for a test case ID assignment and adds a new test for runtime fields query builder.
niemyjski requested a review from Copilot February 28, 2026 15:13
Copilot started reviewing on behalf of niemyjski February 28, 2026 15:14 View session
github-code-quality bot found potential problems Feb 28, 2026
Copilot AI reviewed Feb 28, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 120 out of 124 changed files in this pull request and generated 4 comments.

Files not reviewed (3)
  • .idea/.idea.Foundatio.Repositories/.idea/indexLayout.xml: Language not supported
  • .idea/.idea.Foundatio.Repositories/.idea/projectSettingsUpdater.xml: Language not supported
  • .idea/.idea.Foundatio.Repositories/.idea/vcs.xml: Language not supported

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

niemyjski changed the title Upgrade to Elastic.Clients.Elasticsearch Upgrade to Elastic.Clients.Elasticsearch (ES 9.x) Feb 28, 2026
- Fix STJ vs Newtonsoft serialization test by using semantic JSON comparison
(JsonNode.DeepEquals) instead of raw string equality for TopHits encoding
- Replace TestContext.Current.CancellationToken with TestCancellationToken
- Replace all string concatenation (+) with string interpolation ($"")
- Add null guards, improve error handling for alias retrieval
- Fix constant condition warnings in LoggerExtensions and JsonDiffer
- Add convenience overloads for PatchDocument Add/Replace primitives
- Implement DoubleSystemTextJsonConverter.Read (was NotImplementedException)
- Add NotSupportedException for unsupported JSONPath in patch operations
- Validate patch array elements in PatchDocument.Load
- Remove dead code, unused imports, and commented-out routing call
- Suppress CS8002 for test-only GitHubActionsTestLogger dependency
- Simplify RuntimeFieldsQueryBuilder test to context-level assertions

Made-with: Cursor
niemyjski requested a review from Copilot February 28, 2026 18:31
Copilot started reviewing on behalf of niemyjski February 28, 2026 18:31 View session
Copilot AI reviewed Feb 28, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 123 out of 127 changed files in this pull request and generated 3 comments.

Files not reviewed (3)
  • .idea/.idea.Foundatio.Repositories/.idea/indexLayout.xml: Language not supported
  • .idea/.idea.Foundatio.Repositories/.idea/projectSettingsUpdater.xml: Language not supported
  • .idea/.idea.Foundatio.Repositories/.idea/vcs.xml: Language not supported
Comments suppressed due to low confidence (1)

docs/guide/troubleshooting.md:63

  • The troubleshooting guide shows ElasticsearchClientSettings but still uses settings.BasicAuthentication(...), which is a NEST-era API. With Elastic.Transport, authentication is configured via settings.Authentication(new BasicAuthentication(...)) (or ApiKey/BearerToken equivalents). Update this snippet so readers can copy/paste working code.
**Solutions:**

```csharp
protected override void ConfigureSettings(ElasticsearchClientSettings settings)
{
// Basic authentication
settings.BasicAuthentication("username", "password");


Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

niemyjski commented Mar 5, 2026
_logger.LogInformation("Reindex Task Id: {TaskId}", result.Task.FullyQualifiedId);
if (result.Task == null)
{
_logger.LogError("Reindex failed to start - no task returned. Response valid: {IsValid}, Error: {Error}",
Copy link
Member Author

niemyjski Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is {Error} the common name for this or is it Reason or Message, ensure we are consistent.

Copy link
Member Author

niemyjski Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Investigated: The codebase uses mixed naming -- {Error} for ES server error objects/strings, {Reason} for specific error reason fields, {Message} for exception messages. The usage here ({Error} for the error reason string) is consistent with the pattern in ElasticRepositoryBase.cs:880 and DailyIndex.cs:472. The template name matches what the value represents (the error details).

Copy link
Member Author

niemyjski Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Investigated: The codebase convention is: {Error} for ES server error objects/strings (ElasticsearchServerError), {Reason} for specific error reason fields (Error.Reason), {Message} for exception messages (ex.Message). The usage at this line passes ElasticsearchServerError?.Error?.Reason which is extracting the reason string from the server error. Renamed the placeholder to {Reason} for consistency since it specifically references the Reason field. See commit 8869558.

...ting

- Rename tests to three-part naming convention (Method_State_Expected) and add
AAA (Arrange/Act/Assert) comments
- Extract timeout formatting to TimeSpan.ToElasticDuration() extension method
with 18 unit tests covering all time units and edge cases
- Add blank line before ctx.Search.Source() in FieldIncludesQueryBuilder
github-code-quality bot found potential problems Mar 5, 2026
niemyjski added 4 commits March 5, 2026 16:26
...and test fixes

- Configure ES client source serializer with ConfigureFoundatioRepositoryDefaults() so IDictionary values deserialize as CLR types (not JsonElement)
- Replace manual retry loops in ElasticUtility with IResiliencePolicyProvider
- Use SerializerTestHelper in DoubleSystemTextJsonConverterTests for centralized config
- Fix ReindexTests ToJson to use ES client serializer for ES types (Properties)
- Remove redundant CreateElasticClient override in MyAppElasticConfiguration
- Remove .ToString() workarounds in CustomFieldTests now that source serializer is fixed
niemyjski requested a review from Copilot March 6, 2026 03:43
Copilot started reviewing on behalf of niemyjski March 6, 2026 03:43 View session
Copilot AI reviewed Mar 6, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 142 out of 146 changed files in this pull request and generated 1 comment.

Files not reviewed (3)
  • .idea/.idea.Foundatio.Repositories/.idea/indexLayout.xml: Language not supported
  • .idea/.idea.Foundatio.Repositories/.idea/projectSettingsUpdater.xml: Language not supported
  • .idea/.idea.Foundatio.Repositories/.idea/vcs.xml: Language not supported

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

byte b8 => FieldValue.Long(b8),
sbyte sb => FieldValue.Long(sb),
uint ui => FieldValue.Long(ui),
ulong ul => FieldValue.Long((long)ul),
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Casting ulong to long will throw an OverflowException at runtime (in a checked context) or silently produce incorrect values (in an unchecked context) for values greater than long.MaxValue (9,223,372,036,854,775,807). Consider using FieldValue.Double((double)ul) as a fallback for large ulong values, or at minimum adding a bounds check.

Copilot uses AI. Check for mistakes.
niemyjski requested a review from Copilot March 6, 2026 04:12
Copilot started reviewing on behalf of niemyjski March 6, 2026 04:12 View session
Copilot AI reviewed Mar 6, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 142 out of 146 changed files in this pull request and generated no new comments.

Files not reviewed (3)
  • .idea/.idea.Foundatio.Repositories/.idea/indexLayout.xml: Language not supported
  • .idea/.idea.Foundatio.Repositories/.idea/projectSettingsUpdater.xml: Language not supported
  • .idea/.idea.Foundatio.Repositories/.idea/vcs.xml: Language not supported

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Support extracting task status values when deserialized as `IDictionary` depending on serializer configuration
- Update `FieldValueHelper` to safely handle `ulong` values by falling back to `double` for values exceeding `long.MaxValue`
- Minor nullability fix in `IndexTests`
github-code-quality bot found potential problems Mar 6, 2026
...and keep running Elasticsearch 8 and upgrade to 9 seemlessly.
github-code-quality bot found potential problems Mar 6, 2026

if (!String.IsNullOrEmpty(timestampField))
return descriptor.DateRange(dr => dr.Field(timestampField).GreaterThanOrEquals(startTime));
return descriptor.Range(dr => dr.Date(drr => drr.Field(timestampField).Gte(startTime.Value)));
return descriptor.Range(dr => dr.Date(drr => drr.Field(timestampField).Gte(startTime.Value)));

return descriptor.TermRange(dr => dr.Field(ID_FIELD).GreaterThanOrEquals(ObjectId.GenerateNewId(startTime.Value).ToString()));
return descriptor.Range(dr => dr.Term(tr => tr.Field(ID_FIELD).Gte(ObjectId.GenerateNewId(startTime.Value).ToString())));
// Assert
Assert.NotNull(lastEmployee);
Assert.NotNull(lastEmployee!.Id);
var retrieved = await repository.GetByIdAsync(lastEmployee.Id);
niemyjski added 3 commits March 6, 2026 12:06
Made-with: Cursor

# Conflicts:
# src/Foundatio.Repositories.Elasticsearch/Extensions/ElasticIndexExtensions.cs
# src/Foundatio.Repositories.Elasticsearch/Repositories/ElasticReadOnlyRepositoryBase.cs
Apply the ILogger threading from PR #223 to the new Elastic client API. Thread logger through ToFindResults, ToCountResult, ToAggregations, ToAggregate, and all bucket aggregate helpers. Log warning when terms aggregations (String, Long, Double) report non-zero DocCountErrorUpperBound.

Made-with: Cursor
...er (#212)

Port of fix from main to feature/elastic-client. FieldConditionsQueryBuilder now resolves field names through the per-query QueryFieldResolver (set by OnCustomFieldsBeforeQuery) after ElasticMappingResolver, enabling custom field name resolution for all ComparisonOperator cases.
github-code-quality bot found potential problems Mar 7, 2026
Port all fixes from PR #225 to the Elastic v8 client:
- FieldConditionsQueryBuilder consults QueryFieldResolver for custom fields
- ResolveFieldAsync ordering: resolve base -> custom resolver -> nonAnalyzed
- Null-value rewrites extended to Contains/NotContains
- Contains/NotContains use Operator.And for multi-term matching
- Null-safe dereferences and ternary expressions for Contains text
- Multi-document discrimination tests for all 6 operators
- Reversed token order tests for AND semantics
- Null-value Contains/NotContains rewrite tests

Preserved Elastic v8 client types (Query, FieldValue, TermsQueryField,
FieldValueHelper.ToFieldValue) throughout.

All 485 tests pass (374 ES + 111 core).
github-code-quality bot found potential problems Mar 7, 2026
// Assert
var list = sample["tags"] as System.Text.Json.Nodes.JsonArray;
Assert.NotNull(list);
Assert.Equal(2, list.Count);
niemyjski added 3 commits March 6, 2026 22:47
Port auto-set UpdatedUtc for all patch operations from main to the
new Elasticsearch client branch:

- Resolve conflicts in ElasticRepositoryBase.cs and
ElasticReadOnlyRepositoryBase.cs
- Port all Newtonsoft JToken/JObject helpers to System.Text.Json.Nodes
(JsonNode/JsonObject/JsonValue) equivalents
- Port EmployeeWithDateMetaDataIndex from NEST to new client APIs
(CreateIndexRequestDescriptor, TypeMappingDescriptor, DynamicMapping)
- Remove duplicate Keyword(e => e.Id) mapping (already in SetupDefaults)
- Use ElasticsearchClientSettings.SourceSerializer (not ConnectionSettings)
- All 501 tests pass
...ient

- Replace magic strings "dateCreatedUtc"/"dateUpdatedUtc" in
EmployeeWithDateMetaDataIndex with nameof + JsonNamingPolicy.CamelCase
- Fix patch-operations.md reference to JToken (Newtonsoft) - JsonNode
niemyjski force-pushed the feature/elastic-client branch from 9fca6da to 8cdff51 Compare March 7, 2026 22:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

Copilot code review Copilot Copilot left review comments

+1 more reviewer

github-code-quality[bot] github-code-quality[bot] left review comments

Reviewers whose approvals may not affect merge requirements

At least 1 approving review is required to merge this pull request.

Assignees

niemyjski

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

2 participants