Skip to content

Conversation

@alzarei
Copy link

@alzarei alzarei commented Sep 29, 2025

Modernize samples and documentation for ITextSearch interface

Problem Statement

The existing GettingStartedWithTextSearch samples only demonstrate the legacy ITextSearch interface with clause-based filtering. With the introduction of generic ITextSearch interfaces, developers need clear examples showing both legacy patterns and modern LINQ-based patterns for migration guidance.

Technical Approach

This PR completes the Issue #10456 implementation by updating the GettingStartedWithTextSearch samples to demonstrate the new generic ITextSearch interface with LINQ filtering capabilities. The implementation focuses on developer education and smooth migration paths.

Implementation Details

Sample Structure

  • Existing legacy examples preserved unchanged for backward compatibility
  • New SearchWithLinqFilteringAsync() method demonstrates modern patterns
  • Educational examples showing intended usage patterns for BingWebPage, GoogleWebPage, and VectorStore connectors
  • Clear documentation explaining when to use which approach

Educational Content

  • Console output provides educational context about modernization benefits
  • Code comments explain the technical advantages of type-safe filtering
  • Examples show both simple and complex LINQ filtering scenarios
  • Clear messaging about availability and migration timeline

Code Examples

Legacy Interface (Preserved)

var legacyOptions = new TextSearchOptions
{
    Filter = new TextSearchFilter()
        .Equality("category", "AI")
        .Equality("language", "en")
};

Modern Interface Examples

// BingWebPage filtering
var modernBingOptions = new TextSearchOptions<BingWebPage>
{
    Filter = page => page.Name.Contains("AI") && page.Snippet.Contains("semantic")
};

// GoogleWebPage filtering
var modernGoogleOptions = new TextSearchOptions<GoogleWebPage>
{
    Filter = page => page.Name.Contains("machine learning") && page.Url.Contains("microsoft")
};

// VectorStore filtering
var modernVectorOptions = new TextSearchOptions<DataModel>
{
    Filter = record => record.Tag == "Technology" && record.Title.Contains("AI")
};

Implementation Benefits

For Developers Learning Text Search

  • Clear examples of both legacy and modern interface patterns
  • Educational console output explaining the benefits of each approach
  • Practical examples showing how to migrate existing filtering code
  • Understanding of compile-time safety vs. runtime string validation

For Existing Users

  • No disruption to existing code - all legacy examples still work unchanged
  • Clear migration path when ready to adopt modern interfaces
  • Understanding of when and how to use the new generic interfaces
  • Future-proof examples that work today and integrate seamlessly later

For the Semantic Kernel Ecosystem

  • Complete sample coverage for the modernized text search functionality
  • Educational content supporting developer adoption of new patterns
  • Foundation for demonstrating connector-specific implementations

Validation Results

Build Verification

  • Command: dotnet build --configuration Release --interactive
  • Result: Build succeeded
  • Status: ✅ PASSED (0 errors, 0 warnings)

Test Results
Full Test Suite (with external dependencies):

  • Passed: 7,042 (core functionality tests)
  • Failed: 2,934 (external API configuration issues)
  • Skipped: 389
  • Duration: 4 minutes 57 seconds

Core Unit Tests (framework only):

  • Command: dotnet test src\SemanticKernel.UnitTests\SemanticKernel.UnitTests.csproj --configuration Release
  • Total: 1,574 tests
  • Passed: 1,574 (100% core framework functionality)
  • Failed: 0
  • Duration: 1.5 seconds

Test Failure Analysis
The 2,934 test failures are infrastructure/configuration issues, not code defects:

  • Azure OpenAI Configuration: Missing API keys for external service integration tests
  • Docker Dependencies: Vector database containers not available in development environment
  • External Dependencies: Integration tests requiring live API services (Bing, Google, etc.)

These failures are expected in development environments without external API configurations.

Code Quality

  • Formatting: dotnet format SK-dotnet.slnx - no changes required (already compliant)
  • Code meets all formatting standards
  • Documentation follows XML documentation conventions
  • Sample structure follows established patterns in GettingStartedWithTextSearch

Files Modified

dotnet/samples/GettingStartedWithTextSearch/Step1_Web_Search.cs (MODIFIED)

Breaking Changes

None. All existing samples continue to work unchanged with new content being additive only.

Multi-PR Context

This is PR 6 of 6 in the structured implementation approach for Issue #10456. This PR provides educational content and sample modernization to complete the comprehensive text search interface modernization, enabling developers to understand and adopt the new LINQ-based filtering patterns.

@moonbox3 moonbox3 added the .NET Issue or Pull requests regarding .NET code label Sep 29, 2025
@alzarei alzarei force-pushed the feature-text-search-linq-pr6 branch from b33f63e to 209836a Compare September 29, 2025 03:55
@alzarei alzarei marked this pull request as ready for review September 29, 2025 05:17
@alzarei alzarei requested a review from a team as a code owner September 29, 2025 05:17
@alzarei alzarei force-pushed the feature-text-search-linq-pr6 branch from b0042f4 to a27e3f6 Compare October 30, 2025 06:54
@alzarei alzarei marked this pull request as draft October 30, 2025 08:02
@alzarei alzarei force-pushed the feature-text-search-linq-pr6 branch from a27e3f6 to e465b3c Compare November 3, 2025 05:44
@markwallace-microsoft markwallace-microsoft added kernel Issues or pull requests impacting the core kernel kernel.core labels Nov 3, 2025
@alzarei alzarei force-pushed the feature-text-search-linq-pr6 branch from c1d5bed to 46e2f37 Compare November 3, 2025 08:39
@alzarei alzarei marked this pull request as ready for review November 3, 2025 08:41
@alzarei
Copy link
Author

alzarei commented Nov 3, 2025

Needs to be merged after PRs: 3,4,5.

markwallace-microsoft pushed a commit that referenced this pull request Nov 3, 2025
…arch.GetSearchResultsAsync (#13318)

This PR enhances the type safety of the `ITextSearch<TRecord>` interface
by changing the `GetSearchResultsAsync` method to return
`KernelSearchResults<TRecord>` instead of `KernelSearchResults<object>`.
This improvement eliminates the need for manual casting and provides
better IntelliSense support for consumers.

## Motivation and Context

The current implementation of
`ITextSearch<TRecord>.GetSearchResultsAsync` returns
`KernelSearchResults<object>`, which requires consumers to manually cast
results to the expected type. This reduces type safety and degrades the
developer experience by losing compile-time type checking and
IntelliSense support.

This change aligns the return type with the generic type parameter
`TRecord`, providing the expected strongly-typed results that users of a
generic interface would anticipate.

## Changes Made

### Interface (ITextSearch.cs)
- Changed `ITextSearch<TRecord>.GetSearchResultsAsync` return type from
`KernelSearchResults<object>` to `KernelSearchResults<TRecord>`
- Updated XML documentation to reflect strongly-typed return value
- Legacy `ITextSearch` interface (non-generic) remains unchanged,
continuing to return `KernelSearchResults<object>` for backward
compatibility

### Implementation (VectorStoreTextSearch.cs)
- Added new `GetResultsAsTRecordAsync` helper method returning
`IAsyncEnumerable<TRecord>`
- Updated generic interface implementation to use the new strongly-typed
helper
- Retained `GetResultsAsRecordAsync` method for the legacy non-generic
interface

### Tests (VectorStoreTextSearchTests.cs)
- Updated 3 unit tests to use strongly-typed `DataModel` or
`DataModelWithRawEmbedding` instead of `object`
- Improved test assertions to leverage direct property access without
casting
- All 19 tests pass successfully

## Breaking Changes

**Interface Change (Experimental API):**
- `ITextSearch<TRecord>.GetSearchResultsAsync` now returns
`KernelSearchResults<TRecord>` instead of `KernelSearchResults<object>`
- This interface is marked with `[Experimental("SKEXP0001")]`,
indicating that breaking changes are expected during the preview period
- Legacy `ITextSearch` interface (non-generic) is unaffected and
maintains full backward compatibility

## Benefits

- **Improved Type Safety**: Eliminates runtime casting errors by
providing compile-time type checking
- **Enhanced Developer Experience**: Full IntelliSense support for
TRecord properties and methods
- **Cleaner Code**: Consumers no longer need to cast results from object
to the expected type
- **Consistent API Design**: Generic interface now behaves as expected,
returning strongly-typed results
- **Zero Impact on Legacy Code**: Non-generic ITextSearch interface
remains unchanged

## Testing

- All 19 existing unit tests pass
- Updated tests demonstrate improved type safety with direct property
access
- Verified both generic and legacy interfaces work correctly
- Confirmed zero breaking changes to non-generic ITextSearch consumers

## Related Work

This PR is part of the Issue #10456 multi-PR chain for modernizing
ITextSearch with LINQ-based filtering:
- PR #13175: Foundation (ITextSearch<TRecord> interface) - Merged
- PR #13179: VectorStoreTextSearch + deprecation pattern - In Review
- **This PR (2.1)**: API refinement for improved type safety
- PR #13188-13191: Connector migrations (Bing, Google, Tavily, Brave) -
Pending
- PR #13194: Samples and documentation - Pending

All PRs target the `feature-text-search-linq` branch for coordinated
release.

## Migration Guide for Consumers

### Before (Previous API)
```csharp
ITextSearch<DataModel> search = ...;
KernelSearchResults<object> results = await search.GetSearchResultsAsync("query", options);

foreach (var obj in results.Results)
{
    var record = (DataModel)obj;  // Manual cast required
    Console.WriteLine(record.Name);
}
```

### After (Improved API)
```csharp
ITextSearch<DataModel> search = ...;
KernelSearchResults<DataModel> results = await search.GetSearchResultsAsync("query", options);

foreach (var record in results.Results)  // Strongly typed!
{
    Console.WriteLine(record.Name);  // Direct property access with IntelliSense
}
```

## Checklist

- [x] Changes build successfully
- [x] All unit tests pass (19/19)
- [x] XML documentation updated
- [x] Breaking change documented (experimental API only)
- [x] Legacy interface backward compatibility maintained
- [x] Code follows project coding standards

Co-authored-by: Alexander Zarei <alzarei@users.noreply.github.com>
@alzarei alzarei force-pushed the feature-text-search-linq-pr6 branch from 46e2f37 to fe8e8aa Compare November 11, 2025 07:53
@markwallace-microsoft markwallace-microsoft removed kernel Issues or pull requests impacting the core kernel kernel.core labels Nov 11, 2025
@alzarei alzarei force-pushed the feature-text-search-linq-pr6 branch 2 times, most recently from 3ae3447 to b306591 Compare November 11, 2025 11:56
@alzarei
Copy link
Author

alzarei commented Nov 12, 2025

Hey @westey-m, @markwallace-microsoft, @roji: this PR is ready for review! When you get a chance, please check out the latest commit (there are just two in total).

@roji, this branch includes all the code for the feature, with everything else rebased and cherry-picked in. Thanks!

@alzarei alzarei requested a review from a team as a code owner November 12, 2025 09:45
@markwallace-microsoft markwallace-microsoft added the python Pull requests for the Python Semantic Kernel label Nov 12, 2025
@markwallace-microsoft markwallace-microsoft added kernel Issues or pull requests impacting the core kernel kernel.core documentation labels Nov 12, 2025
@github-actions github-actions bot changed the title .Net: feat: Modernize samples and documentation for ITextSearch<TRecord> interface (microsoft#10456) Python: .Net: feat: Modernize samples and documentation for ITextSearch<TRecord> interface (microsoft#10456) Nov 12, 2025
@alzarei alzarei force-pushed the feature-text-search-linq-pr6 branch from 6a2e4a6 to b306591 Compare November 12, 2025 10:03
@markwallace-microsoft markwallace-microsoft removed python Pull requests for the Python Semantic Kernel kernel Issues or pull requests impacting the core kernel kernel.core documentation labels Nov 12, 2025
@alzarei alzarei changed the title Python: .Net: feat: Modernize samples and documentation for ITextSearch<TRecord> interface (microsoft#10456) .Net: feat: Modernize samples and documentation for ITextSearch<TRecord> interface (microsoft#10456) Nov 12, 2025
@alzarei alzarei force-pushed the feature-text-search-linq-pr6 branch from b306591 to d84ccdc Compare November 12, 2025 12:29
…ples

Add comprehensive LINQ filtering demonstrations for the new ITextSearch<TRecord> interface to showcase type-safe search capabilities across multiple connectors.

**GettingStartedWithTextSearch (Step1_Web_Search.cs):**
- Add BingSearchWithLinqFilteringAsync() demonstrating ITextSearch<BingWebPage> with Language and FamilyFriendly filters
- Add GoogleSearchWithLinqFilteringAsync() demonstrating ITextSearch<GoogleWebPage> with Title.Contains() and DisplayLink.EndsWith() filters
- Use interface type declaration (ITextSearch<TRecord>) to showcase the new generic interface
- Include CA1859 pragma suppression with explanation (sample intentionally demonstrates interface usage)

**Concepts (Bing_TextSearch.cs):**
- Add UsingBingTextSearchWithLinqFilteringAsync() with 4 progressive LINQ filter examples
- Demonstrate Language equality, FamilyFriendly boolean filtering, compound AND filters, and complex multi-property filters
- Show both SearchAsync() and GetSearchResultsAsync() usage patterns

**Concepts (Tavily_TextSearch.cs):**
- Add UsingTavilyTextSearchWithLinqFilteringAsync() with 4 progressive LINQ filter examples
- Demonstrate Title.Contains() filtering, compound filters with exclusion (!Contains), full result retrieval with filtering, and complex multi-property filters
- Showcase null-safety patterns with Title != null checks

**Technical Notes:**
- Uses ITextSearch<TRecord> interface type to avoid method ambiguity with legacy ITextSearch
- Generic methods are explicitly implemented, requiring interface type or casting
- All examples include proper null checks for nullable properties
- Maintains backward compatibility with existing simple search examples

Addresses microsoft#10456
@alzarei alzarei force-pushed the feature-text-search-linq-pr6 branch from e877680 to 7354b99 Compare November 22, 2025 17:46
@alzarei
Copy link
Author

alzarei commented Nov 22, 2025

@westey-m this one was rebased on top of the feature branch and ready to merge now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

.NET Issue or Pull requests regarding .NET code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants