-
-
Notifications
You must be signed in to change notification settings - Fork 71
Open
Description
Summary
The current Find-Item cmdlet requires verbose hashtable-based SearchCriteria arrays that are error-prone and difficult to discover. This adds a SearchBuilder extension library (following the DialogBuilder pattern) that provides a fluent, pipeline-based API for constructing search queries.
Based on recommendations for improving Find-Item usability in MCP/LLM-driven interactions.
Features
Core Builder
New-SearchBuilder-- creates builder with-Index,-Path,-OrderBy,-First,-Skip,-Last,-MaxResults,-Strict,-IncludeMetadata,-Property,-QueryType,-FacetOn,-FacetMinCountInvoke-Search-- executes builder viaFind-Item, returns result object with pagination state, auto-advances_Skipeach callReset-SearchBuilder-- resets pagination for re-use
Filter Functions (fluent, pipeline-based)
Add-SearchFilter-- core filter:-Field,-Filter,-Value,-Invert,-Boost,-CaseSensitiveAdd-TemplateFilter-- convenience:-Nameor-IdAdd-FieldContains/Add-FieldEquals-- shorthand wrappersAdd-DateRangeFilter-- relative syntax (-Last "7d","2w","3m","1y") and absolute (-From/-To)
Predicate Grouping
New-SearchFilterGroup/Add-SearchFilterGroup-- compose OR/AND groups within queries (CLM-safe, no scriptblocks)
Discovery & Validation
Get-SearchFilter-- lists all 14 valid FilterType values with descriptions at runtimeGet-SearchIndexField-- lists all indexed fields viaSchema.AllFieldNames-Strictmode validates field names against the index schema before executing
Performance & Advanced
-Property-- select specificSearchResultItemproperties (e.g.,Name,Path,TemplateName) to avoid deserializing full objects-QueryType-- pass a customSearchResultItemsubclass for strongly-typed index fields-FacetOn/-FacetMinCount-- faceted search returning category aggregations instead of items
Result Object (CLM-safe via New-PSObject)
Standard search:
Items, HasMore, PageNumber, PageSize, TotalCount, Truncated, MaxResults, IndexName, Query
With -IncludeMetadata: adds IndexLastUpdated
Faceted search (-FacetOn):
Facets (raw FacetResults), Categories (convenience accessor), IndexName, Query
Pagination
Auto-advancing pagination with HasMore signal for paged accumulation loops:
$search = New-SearchBuilder -Index "sitecore_master_index" -First 50 -MaxResults 500
$search | Add-TemplateFilter -Name "Article"
do {
$results = $search | Invoke-Search
$results.Items | ForEach-Object { # process }
} while ($results.HasMore)Implementation
- 3 YAML files following DialogBuilder 3-level serialization pattern (Script Folder > Script Module > Script)
- 14 PowerShell functions (12 public + 2 internal helpers)
- CLM allowlist -- all functions added to
content-editorprofile inSpe.config - No C# changes -- pure PowerShell extension loaded via
Import-Function -Name SearchBuilder
Testing
- Unit tests (
tests/unit/SPE.SearchBuilder.Tests.ps1): 102 assertions covering all functions - Integration tests (
tests/integration/Remoting.SearchBuilder.Tests.ps1): 11 test sections against live Sitecore - Live validation: all scenarios verified against Sitecore 10.4 instance
Usage Examples
Simple search
Import-Function -Name SearchBuilder
$search = New-SearchBuilder -Index "sitecore_master_index" -First 25
$search | Add-TemplateFilter -Name "Article"
$search | Add-FieldContains -Field "Title" -Value "Welcome"
$results = $search | Invoke-Search
$results.Items | Initialize-Item | ForEach-Object { $_.Name }Complex OR group with date range
$search = New-SearchBuilder -Index "sitecore_master_index" -Strict
$group = New-SearchFilterGroup -Operation Or
$group | Add-TemplateFilter -Name "Article"
$group | Add-TemplateFilter -Name "Blog Post"
$search | Add-SearchFilterGroup -Group $group
$search | Add-DateRangeFilter -Field "__Updated" -Last "30d"
$results = $search | Invoke-SearchPaged accumulation with safety cap
$search = New-SearchBuilder -Index "sitecore_master_index" -First 100 -MaxResults 500
$search | Add-TemplateFilter -Name "Article"
$all = [System.Collections.ArrayList]@()
do {
$results = $search | Invoke-Search
$all.AddRange($results.Items)
} while ($results.HasMore)
Write-Host "Collected $($all.Count) items, truncated: $($results.Truncated)"Select specific properties for performance
$search = New-SearchBuilder -Index "sitecore_master_index" -First 10 -Property @("Name", "Path", "TemplateName")
$search | Add-TemplateFilter -Name "Template Folder"
$results = $search | Invoke-Search
$results.Items | ForEach-Object { "$($_.Name) [$($_.TemplateName)]" }Faceted search
$search = New-SearchBuilder -Index "sitecore_master_index" -FacetOn @("TemplateName") -FacetMinCount 50
$search | Add-FieldContains -Field "_fullpath" -Value "powershell"
$results = $search | Invoke-Search
$results.Categories | ForEach-Object {
Write-Host "$($_.Name):"
$_.Values | ForEach-Object { Write-Host " $($_.Name): $($_.AggregateCount)" }
}Inverted filter with boost
$search = New-SearchBuilder -Index "sitecore_master_index" -First 5
$search | Add-TemplateFilter -Name "Template Folder"
$search | Add-SearchFilter -Field "_name" -Filter "Contains" -Value "system" -Invert -Boost 5
$results = $search | Invoke-Search
# Returns Template Folders NOT containing "system" in nameStrict mode -- field validation
$search = New-SearchBuilder -Index "sitecore_master_index" -Strict
$search | Add-SearchFilter -Field "bogus_field" -Filter "Equals" -Value "test"
$search | Invoke-Search
# Throws: "Strict mode: The following fields are not indexed: 'bogus_field'"Discovery
# List all valid filter types
Get-SearchFilter
# List all indexed fields
Get-SearchIndexField -Index "sitecore_master_index"Reset and re-run
$search | Reset-SearchBuilder # resets Skip and PageNumber to 0
$results = $search | Invoke-Search # starts from page 1 againNotes
-Propertyand-FacetOnuse C# property names onSearchResultItem(Name,Path,TemplateName), not index field names (_name,_fullpath,_templatename). This matches nativeFind-Itembehavior.Invoke-Searchreturns a wrapper object. To pipe toInitialize-Item, use$results.Items | Initialize-Item.- All output objects use
New-PSObjectfor CLM (Constrained Language Mode) compatibility. - The builder is a hashtable (reference type) --
Add-*functions mutate in-place, no reassignment needed.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels