Skip to content

Commit 50caa22

Browse files
committed
feat: Add GoogleTextSearch ITextSearch<GoogleWebPage> LINQ implementation (PR4)
- Implements ITextSearch<GoogleWebPage> with LINQ filtering support - Adds comprehensive LINQ filter verification tests - Includes C# 14 MemoryExtensions.Contains pattern handling - Enhanced documentation and error handling for unsupported patterns - Squashed from feature-text-search-linq-pr4 for clean dependency management
1 parent 4481615 commit 50caa22

File tree

5 files changed

+1109
-20
lines changed

5 files changed

+1109
-20
lines changed

dotnet/samples/Concepts/Search/Google_TextSearch.cs

Lines changed: 111 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public async Task UsingGoogleTextSearchAsync()
2626
var query = "What is the Semantic Kernel?";
2727

2828
// Search and return results as string items
29-
KernelSearchResults<string> stringResults = await textSearch.SearchAsync(query, new() { Top = 4, Skip = 0 });
29+
KernelSearchResults<string> stringResults = await textSearch.SearchAsync(query, new TextSearchOptions { Top = 4, Skip = 0 });
3030
Console.WriteLine("——— String Results ———\n");
3131
await foreach (string result in stringResults.Results)
3232
{
@@ -35,7 +35,7 @@ public async Task UsingGoogleTextSearchAsync()
3535
}
3636

3737
// Search and return results as TextSearchResult items
38-
KernelSearchResults<TextSearchResult> textResults = await textSearch.GetTextSearchResultsAsync(query, new() { Top = 4, Skip = 4 });
38+
KernelSearchResults<TextSearchResult> textResults = await textSearch.GetTextSearchResultsAsync(query, new TextSearchOptions { Top = 4, Skip = 4 });
3939
Console.WriteLine("\n——— Text Search Results ———\n");
4040
await foreach (TextSearchResult result in textResults.Results)
4141
{
@@ -46,7 +46,7 @@ public async Task UsingGoogleTextSearchAsync()
4646
}
4747

4848
// Search and return results as Google.Apis.CustomSearchAPI.v1.Data.Result items
49-
KernelSearchResults<object> fullResults = await textSearch.GetSearchResultsAsync(query, new() { Top = 4, Skip = 8 });
49+
KernelSearchResults<object> fullResults = await textSearch.GetSearchResultsAsync(query, new TextSearchOptions { Top = 4, Skip = 8 });
5050
Console.WriteLine("\n——— Google Web Page Results ———\n");
5151
await foreach (Google.Apis.CustomSearchAPI.v1.Data.Result result in fullResults.Results)
5252
{
@@ -74,7 +74,7 @@ public async Task UsingGoogleTextSearchWithACustomMapperAsync()
7474
var query = "What is the Semantic Kernel?";
7575

7676
// Search with TextSearchResult textResult type
77-
KernelSearchResults<string> stringResults = await textSearch.SearchAsync(query, new() { Top = 2, Skip = 0 });
77+
KernelSearchResults<string> stringResults = await textSearch.SearchAsync(query, new TextSearchOptions { Top = 2, Skip = 0 });
7878
Console.WriteLine("--- Serialized JSON Results ---");
7979
await foreach (string result in stringResults.Results)
8080
{
@@ -107,6 +107,113 @@ public async Task UsingGoogleTextSearchWithASiteSearchFilterAsync()
107107
}
108108
}
109109

110+
/// <summary>
111+
/// Show how to use enhanced LINQ filtering with GoogleTextSearch including Contains, NOT, FileType, and compound AND expressions.
112+
/// </summary>
113+
[Fact]
114+
public async Task UsingGoogleTextSearchWithEnhancedLinqFilteringAsync()
115+
{
116+
// Create an ITextSearch<GoogleWebPage> instance using Google search
117+
var textSearch = new GoogleTextSearch(
118+
initializer: new() { ApiKey = TestConfiguration.Google.ApiKey, HttpClientFactory = new CustomHttpClientFactory(this.Output) },
119+
searchEngineId: TestConfiguration.Google.SearchEngineId);
120+
121+
var query = "Semantic Kernel AI";
122+
123+
// Example 1: Simple equality filtering
124+
Console.WriteLine("——— Example 1: Equality Filter (DisplayLink) ———\n");
125+
var equalityOptions = new TextSearchOptions<GoogleWebPage>
126+
{
127+
Top = 2,
128+
Skip = 0,
129+
Filter = page => page.DisplayLink == "microsoft.com"
130+
};
131+
var equalityResults = await textSearch.SearchAsync(query, equalityOptions);
132+
await foreach (string result in equalityResults.Results)
133+
{
134+
Console.WriteLine(result);
135+
Console.WriteLine(new string('—', HorizontalRuleLength));
136+
}
137+
138+
// Example 2: Contains filtering
139+
Console.WriteLine("\n——— Example 2: Contains Filter (Title) ———\n");
140+
var containsOptions = new TextSearchOptions<GoogleWebPage>
141+
{
142+
Top = 2,
143+
Skip = 0,
144+
Filter = page => page.Title != null && page.Title.Contains("AI")
145+
};
146+
var containsResults = await textSearch.SearchAsync(query, containsOptions);
147+
await foreach (string result in containsResults.Results)
148+
{
149+
Console.WriteLine(result);
150+
Console.WriteLine(new string('—', HorizontalRuleLength));
151+
}
152+
153+
// Example 3: NOT Contains filtering (exclusion)
154+
Console.WriteLine("\n——— Example 3: NOT Contains Filter (Exclude 'deprecated') ———\n");
155+
var notContainsOptions = new TextSearchOptions<GoogleWebPage>
156+
{
157+
Top = 2,
158+
Skip = 0,
159+
Filter = page => page.Title != null && !page.Title.Contains("deprecated")
160+
};
161+
var notContainsResults = await textSearch.SearchAsync(query, notContainsOptions);
162+
await foreach (string result in notContainsResults.Results)
163+
{
164+
Console.WriteLine(result);
165+
Console.WriteLine(new string('—', HorizontalRuleLength));
166+
}
167+
168+
// Example 4: FileFormat filtering
169+
Console.WriteLine("\n——— Example 4: FileFormat Filter (PDF files) ———\n");
170+
var fileFormatOptions = new TextSearchOptions<GoogleWebPage>
171+
{
172+
Top = 2,
173+
Skip = 0,
174+
Filter = page => page.FileFormat == "pdf"
175+
};
176+
var fileFormatResults = await textSearch.SearchAsync(query, fileFormatOptions);
177+
await foreach (string result in fileFormatResults.Results)
178+
{
179+
Console.WriteLine(result);
180+
Console.WriteLine(new string('—', HorizontalRuleLength));
181+
}
182+
183+
// Example 5: Compound AND filtering (multiple conditions)
184+
Console.WriteLine("\n——— Example 5: Compound AND Filter (Title + Site) ———\n");
185+
var compoundOptions = new TextSearchOptions<GoogleWebPage>
186+
{
187+
Top = 2,
188+
Skip = 0,
189+
Filter = page => page.Title != null && page.Title.Contains("Semantic") &&
190+
page.DisplayLink != null && page.DisplayLink.Contains("microsoft")
191+
};
192+
var compoundResults = await textSearch.SearchAsync(query, compoundOptions);
193+
await foreach (string result in compoundResults.Results)
194+
{
195+
Console.WriteLine(result);
196+
Console.WriteLine(new string('—', HorizontalRuleLength));
197+
}
198+
199+
// Example 6: Complex compound filtering (equality + contains + exclusion)
200+
Console.WriteLine("\n——— Example 6: Complex Compound Filter (FileFormat + Contains + NOT Contains) ———\n");
201+
var complexOptions = new TextSearchOptions<GoogleWebPage>
202+
{
203+
Top = 2,
204+
Skip = 0,
205+
Filter = page => page.FileFormat == "pdf" &&
206+
page.Title != null && page.Title.Contains("AI") &&
207+
page.Snippet != null && !page.Snippet.Contains("deprecated")
208+
};
209+
var complexResults = await textSearch.SearchAsync(query, complexOptions);
210+
await foreach (string result in complexResults.Results)
211+
{
212+
Console.WriteLine(result);
213+
Console.WriteLine(new string('—', HorizontalRuleLength));
214+
}
215+
}
216+
110217
#region private
111218
private const int HorizontalRuleLength = 80;
112219

dotnet/samples/GettingStartedWithTextSearch/Step1_Web_Search.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public async Task BingSearchAsync()
2525
var query = "What is the Semantic Kernel?";
2626

2727
// Search and return results
28-
KernelSearchResults<string> searchResults = await textSearch.SearchAsync(query, new() { Top = 4 });
28+
KernelSearchResults<string> searchResults = await textSearch.SearchAsync(query, new TextSearchOptions { Top = 4 });
2929
await foreach (string result in searchResults.Results)
3030
{
3131
Console.WriteLine(result);
@@ -46,7 +46,7 @@ public async Task GoogleSearchAsync()
4646
var query = "What is the Semantic Kernel?";
4747

4848
// Search and return results
49-
KernelSearchResults<string> searchResults = await textSearch.SearchAsync(query, new() { Top = 4 });
49+
KernelSearchResults<string> searchResults = await textSearch.SearchAsync(query, new TextSearchOptions { Top = 4 });
5050
await foreach (string result in searchResults.Results)
5151
{
5252
Console.WriteLine(result);

0 commit comments

Comments
 (0)