Skip to content

Commit c1d5bed

Browse files
committed
Add LINQ filtering examples to Step1_Web_Search
- Restored original BingSearchAsync() and GoogleSearchAsync() for simple string searches - Added new BingSearchWithLinqFilteringAsync() showing ITextSearch<BingWebPage> with LINQ filtering - Added new GoogleSearchWithLinqFilteringAsync() showing ITextSearch<GoogleWebPage> with LINQ filtering - Progressive learning path: simple searches -> LINQ filtering with real API calls - Demonstrates migration from legacy ITextSearch to generic ITextSearch<TRecord> - All examples use real API calls, showing actual usage patterns
1 parent e465b3c commit c1d5bed

File tree

1 file changed

+74
-116
lines changed

1 file changed

+74
-116
lines changed

dotnet/samples/GettingStartedWithTextSearch/Step1_Web_Search.cs

Lines changed: 74 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,79 @@ public async Task GoogleSearchAsync()
5353
}
5454
}
5555

56+
/// <summary>
57+
/// Show how to use <see cref="BingTextSearch"/> with the new generic <see cref="ITextSearch{TRecord}"/>
58+
/// interface and LINQ filtering for type-safe searches.
59+
/// </summary>
60+
[Fact]
61+
public async Task BingSearchWithLinqFilteringAsync()
62+
{
63+
// Create a BingTextSearch instance
64+
var bingSearch = new BingTextSearch(apiKey: TestConfiguration.Bing.ApiKey);
65+
66+
// Use the new generic interface for type-safe searches
67+
ITextSearch<BingWebPage> textSearch = bingSearch;
68+
69+
var query = "What is the Semantic Kernel?";
70+
71+
// Use LINQ filtering for type-safe search with compile-time validation
72+
var options = new TextSearchOptions<BingWebPage>
73+
{
74+
Top = 4,
75+
Filter = page => page.Language == "en" && page.IsFamilyFriendly == true
76+
};
77+
78+
// Search and return strongly-typed results
79+
Console.WriteLine("\n--- Bing Search with LINQ Filtering ---\n");
80+
KernelSearchResults<BingWebPage> searchResults = await textSearch.GetSearchResultsAsync(query, options);
81+
await foreach (BingWebPage page in searchResults.Results)
82+
{
83+
Console.WriteLine($"Name: {page.Name}");
84+
Console.WriteLine($"Snippet: {page.Snippet}");
85+
Console.WriteLine($"Url: {page.Url}");
86+
Console.WriteLine($"Language: {page.Language}");
87+
Console.WriteLine($"Family Friendly: {page.IsFamilyFriendly}");
88+
Console.WriteLine("---");
89+
}
90+
}
91+
92+
/// <summary>
93+
/// Show how to use <see cref="GoogleTextSearch"/> with the new generic <see cref="ITextSearch{TRecord}"/>
94+
/// interface and LINQ filtering for type-safe searches.
95+
/// </summary>
96+
[Fact]
97+
public async Task GoogleSearchWithLinqFilteringAsync()
98+
{
99+
// Create a GoogleTextSearch instance
100+
var googleSearch = new GoogleTextSearch(
101+
searchEngineId: TestConfiguration.Google.SearchEngineId,
102+
apiKey: TestConfiguration.Google.ApiKey);
103+
104+
// Use the new generic interface for type-safe searches
105+
ITextSearch<GoogleWebPage> textSearch = googleSearch;
106+
107+
var query = "What is the Semantic Kernel?";
108+
109+
// Use LINQ filtering for type-safe search with compile-time validation
110+
var options = new TextSearchOptions<GoogleWebPage>
111+
{
112+
Top = 4,
113+
Filter = page => page.Title.Contains("Semantic") && page.DisplayLink.EndsWith(".com")
114+
};
115+
116+
// Search and return strongly-typed results
117+
Console.WriteLine("\n--- Google Search with LINQ Filtering ---\n");
118+
KernelSearchResults<GoogleWebPage> searchResults = await textSearch.GetSearchResultsAsync(query, options);
119+
await foreach (GoogleWebPage page in searchResults.Results)
120+
{
121+
Console.WriteLine($"Title: {page.Title}");
122+
Console.WriteLine($"Snippet: {page.Snippet}");
123+
Console.WriteLine($"Link: {page.Link}");
124+
Console.WriteLine($"Display Link: {page.DisplayLink}");
125+
Console.WriteLine("---");
126+
}
127+
}
128+
56129
/// <summary>
57130
/// Show how to create a <see cref="BingTextSearch"/> and use it to perform a search
58131
/// and return results as a collection of <see cref="BingWebPage"/> instances.
@@ -86,7 +159,7 @@ public async Task SearchForWebPagesAsync()
86159
}
87160
else
88161
{
89-
Console.WriteLine("\n——— Google Web Page Results ———\n");
162+
Console.WriteLine("\n��� Google Web Page Results ���\n");
90163
await foreach (Google.Apis.CustomSearchAPI.v1.Data.Result result in objectResults.Results)
91164
{
92165
Console.WriteLine($"Title: {result.Title}");
@@ -129,119 +202,4 @@ public async Task SearchForTextSearchResultsAsync()
129202
Console.WriteLine($"Link: {result.Link}");
130203
}
131204
}
132-
133-
/// <summary>
134-
/// Show how to use the new generic <see cref="ITextSearch{TRecord}"/> interface with LINQ filtering for type-safe searches.
135-
/// This demonstrates the modernized text search functionality introduced in Issue #10456.
136-
/// </summary>
137-
/// <remarks>
138-
/// This example shows the intended pattern for the new generic interfaces.
139-
/// Currently demonstrates the concept using examples from the existing connectors in this sample suite:
140-
/// - BingTextSearch and GoogleTextSearch (this file)
141-
/// - VectorStoreTextSearch (Step4_Search_With_VectorStore.cs)
142-
/// </remarks>
143-
[Fact]
144-
public Task SearchWithLinqFilteringAsync()
145-
{
146-
// This example demonstrates the NEW generic interface pattern with LINQ filtering
147-
// that provides compile-time type safety and IntelliSense support
148-
149-
Console.WriteLine("\n--- Type-Safe Search with Generic Interface and LINQ Filtering ---\n");
150-
Console.WriteLine("This demonstrates the modernized ITextSearch<TRecord> pattern from Issue #10456");
151-
Console.WriteLine("Key benefits:");
152-
Console.WriteLine("- Compile-time type safety (no runtime errors from property name typos)");
153-
Console.WriteLine("- IntelliSense support for filtering properties");
154-
Console.WriteLine("- LINQ expressions for complex filtering logic");
155-
Console.WriteLine("- Better developer experience with strongly-typed search results");
156-
Console.WriteLine();
157-
158-
Console.WriteLine("=== Connectors Available in This Sample Suite ===");
159-
Console.WriteLine();
160-
161-
Console.WriteLine("1. VectorStoreTextSearch<TRecord> (Step4_Search_With_VectorStore.cs)");
162-
Console.WriteLine(" [OK] Already implements ITextSearch<TRecord> with LINQ filtering:");
163-
Console.WriteLine(" var vectorSearch = new VectorStoreTextSearch<DataModel>(collection);");
164-
Console.WriteLine(" var options = new TextSearchOptions<DataModel>");
165-
Console.WriteLine(" {");
166-
Console.WriteLine(" Filter = record => record.Tag == \"Technology\" && record.Title.Contains(\"AI\")");
167-
Console.WriteLine(" };");
168-
Console.WriteLine(" var results = await vectorSearch.GetSearchResultsAsync(query, options);");
169-
Console.WriteLine();
170-
171-
if (this.UseBingSearch)
172-
{
173-
Console.WriteLine("2. BingTextSearch (this file - BingSearchAsync())");
174-
Console.WriteLine(" [PLANNED] Pattern for future generic interface (once PR3 is merged):");
175-
Console.WriteLine(" var bingSearch = new BingTextSearch(apiKey);");
176-
Console.WriteLine(" var options = new TextSearchOptions<BingWebPage>");
177-
Console.WriteLine(" {");
178-
Console.WriteLine(" Top = 4,");
179-
Console.WriteLine(" Filter = page => page.Name.Contains(\"Microsoft\") && page.Snippet.Contains(\"AI\")");
180-
Console.WriteLine(" };");
181-
Console.WriteLine(" var results = await ((ITextSearch<BingWebPage>)bingSearch).GetSearchResultsAsync(query, options);");
182-
Console.WriteLine(" // Type-safe access: page.Name, page.Snippet, page.Url, page.DateLastCrawled");
183-
}
184-
else
185-
{
186-
Console.WriteLine("2. BingTextSearch (set UseBingSearch = true to see example)");
187-
}
188-
189-
Console.WriteLine();
190-
Console.WriteLine("3. GoogleTextSearch (this file - GoogleSearchAsync())");
191-
Console.WriteLine(" [PLANNED] Pattern for future generic interface (once PR4 is merged):");
192-
Console.WriteLine(" // Note: GoogleWebPage is a conceptual type pending PR4 implementation");
193-
Console.WriteLine(" // The actual Google API currently uses: Google.Apis.CustomSearchAPI.v1.Data.Result");
194-
Console.WriteLine(" var googleSearch = new GoogleTextSearch(searchEngineId, apiKey);");
195-
Console.WriteLine(" var options = new TextSearchOptions<GoogleWebPage>");
196-
Console.WriteLine(" {");
197-
Console.WriteLine(" Top = 4,");
198-
Console.WriteLine(" Filter = page => page.Title.Contains(\"AI\") && page.DisplayLink.EndsWith(\".com\")");
199-
Console.WriteLine(" };");
200-
Console.WriteLine(" var results = await ((ITextSearch<GoogleWebPage>)googleSearch).GetSearchResultsAsync(query, options);");
201-
Console.WriteLine(" // Type-safe access: page.Title, page.Snippet, page.DisplayLink, page.Link");
202-
Console.WriteLine();
203-
204-
Console.WriteLine("=== Key Technical Benefits ===");
205-
Console.WriteLine();
206-
Console.WriteLine("[OK] Compile-time validation - no more runtime property name errors");
207-
Console.WriteLine("[OK] IntelliSense support - IDE shows available properties for each connector");
208-
Console.WriteLine("[OK] Type safety - strongly typed search results and filtering");
209-
Console.WriteLine("[OK] LINQ expressions - familiar &&, ||, Contains(), StartsWith(), comparisons, etc.");
210-
Console.WriteLine("[OK] C# version compatibility - expressions work across C# 12, 13, and 14+");
211-
Console.WriteLine("[OK] 100% backward compatibility - existing ITextSearch code unchanged");
212-
Console.WriteLine();
213-
214-
Console.WriteLine("=== Example LINQ Filtering Patterns ===");
215-
Console.WriteLine();
216-
Console.WriteLine("// Bing: Filter web pages by content and metadata");
217-
Console.WriteLine("Filter = page => page.Name.Contains(\"Microsoft\") && page.DateLastCrawled > DateTime.Now.AddDays(-7)");
218-
Console.WriteLine("// ↑ String.Contains (instance method) - works in all C# versions");
219-
Console.WriteLine();
220-
Console.WriteLine("// Google: Filter search results by domain and content");
221-
Console.WriteLine("Filter = result => result.Title.Contains(\"AI\") && result.DisplayLink.EndsWith(\".edu\")");
222-
Console.WriteLine("// ↑ String.Contains (instance method) - works in all C# versions");
223-
Console.WriteLine();
224-
Console.WriteLine("// Vector Store: Filter custom record types with complex logic");
225-
Console.WriteLine("Filter = record => record.Category == \"Technology\" && record.Score > 0.75 && record.Tags.Any(t => t == \"AI\")");
226-
Console.WriteLine("// ↑ Use Any() for collections");
227-
Console.WriteLine();
228-
Console.WriteLine("// C# 14 Compatibility Note:");
229-
Console.WriteLine("// - String.Contains() (instance method): ✅ Works in all C# versions");
230-
Console.WriteLine("// - For collection filtering, use Any() or Where(): array.Any(x => x == value)");
231-
Console.WriteLine("// - Avoid collection.Contains(item) in expressions (C# 14 resolution changes)");
232-
Console.WriteLine();
233-
234-
Console.WriteLine("The VectorStoreTextSearch already demonstrates this pattern in Step4!");
235-
Console.WriteLine("See Step4_Search_With_VectorStore.cs for working generic interface examples.");
236-
Console.WriteLine();
237-
Console.WriteLine("This modernization is part of the structured PR series for Issue #10456:");
238-
Console.WriteLine("PR1: Core generic interfaces [OK]");
239-
Console.WriteLine("PR2: VectorStoreTextSearch implementation [OK]");
240-
Console.WriteLine("PR3: BingTextSearch connector (future) [PLANNED]");
241-
Console.WriteLine("PR4: GoogleTextSearch connector (future) [PLANNED]");
242-
Console.WriteLine("PR5: TavilyTextSearch & BraveTextSearch connectors (future) [PLANNED]");
243-
Console.WriteLine("PR6: Samples and documentation (this PR) [OK]");
244-
245-
return Task.CompletedTask;
246-
}
247205
}

0 commit comments

Comments
 (0)