Functions/GenXdev.AI.Queries/Find-IndexedImage.ps1

###############################################################################
<#
.SYNOPSIS
Searches for images using an optimized SQLite database with fast indexed lookups.
 
.DESCRIPTION
Performs high-performance image searches using a pre-built SQLite database with
optimized indexes. This function provides the same search capabilities as
Find-Image but with significantly faster performance by eliminating file system
scans and using database indexes for all search criteria.
 
.PARAMETER Any
Will match any of all the possible meta data types.
 
.PARAMETER DatabaseFilePath
The path to the image database file. If not specified, a default path is used.
 
.PARAMETER ImageDirectories
Array of directory paths to search for images.
 
.PARAMETER PathLike
Array of directory path-like search strings to filter images by path (SQL LIKE
patterns, e.g. '%\2024\%').
 
.PARAMETER Language
Language for descriptions and keywords.
 
.PARAMETER FacesDirectory
The directory containing face images organized by person folders. If not
specified, uses the configured faces directory preference.
 
.PARAMETER EmbedImages
Embed images as base64.
 
.PARAMETER ForceIndexRebuild
Force rebuild of the image index database.
 
.PARAMETER NoFallback
Switch to disable fallback behavior.
 
.PARAMETER NeverRebuild
Switch to skip database initialization and rebuilding.
 
.PARAMETER DescriptionSearch
The description text to look for, wildcards allowed.
 
.PARAMETER Keywords
The keywords to look for, wildcards allowed.
 
.PARAMETER People
People to look for, wildcards allowed.
 
.PARAMETER Objects
Objects to look for, wildcards allowed.
 
.PARAMETER Scenes
Scenes to look for, wildcards allowed.
 
.PARAMETER PictureType
Picture types to filter by, wildcards allowed.
 
.PARAMETER StyleType
Style types to filter by, wildcards allowed.
 
.PARAMETER OverallMood
Overall moods to filter by, wildcards allowed.
 
.PARAMETER HasNudity
Filter images that contain nudity.
 
.PARAMETER NoNudity
Filter images that do NOT contain nudity.
 
.PARAMETER HasExplicitContent
Filter images that contain explicit content.
 
.PARAMETER NoExplicitContent
Filter images that do NOT contain explicit content.
 
.PARAMETER ShowInBrowser
Show results in a browser gallery.
 
.PARAMETER PassThru
Return image data as objects.
 
.PARAMETER Title
Title for the image gallery.
 
.PARAMETER Description
Description for the image gallery.
 
.PARAMETER AcceptLang
Browser accept-language header.
 
.PARAMETER Monitor
Monitor to use for display.
 
.PARAMETER Width
Initial width of browser window.
 
.PARAMETER Height
Initial height of browser window.
 
.PARAMETER X
Initial X position of browser window.
 
.PARAMETER Y
Initial Y position of browser window.
 
.PARAMETER Interactive
Enable interactive browser features.
 
.PARAMETER Private
Open in private/incognito mode.
 
.PARAMETER Force
Force enable debugging port.
 
.PARAMETER Edge
Open in Microsoft Edge.
 
.PARAMETER Chrome
Open in Google Chrome.
 
.PARAMETER Chromium
Open in Chromium-based browser.
 
.PARAMETER Firefox
Open in Firefox.
 
.PARAMETER All
Open in all browsers.
 
.PARAMETER FullScreen
Open in fullscreen mode.
 
.PARAMETER Left
Place window on left side.
 
.PARAMETER Right
Place window on right side.
 
.PARAMETER Top
Place window on top.
 
.PARAMETER Bottom
Place window on bottom.
 
.PARAMETER Centered
Center the window.
 
.PARAMETER ApplicationMode
Hide browser controls.
 
.PARAMETER NoBrowserExtensions
Disable browser extensions.
 
.PARAMETER DisablePopupBlocker
Disable popup blocker.
 
.PARAMETER RestoreFocus
Restore PowerShell focus.
 
.PARAMETER NewWindow
Create new browser window.
 
.PARAMETER OnlyReturnHtml
Only return HTML.
 
.PARAMETER InputObject
Accepts search results from a previous -PassThru call to regenerate the view.
 
.PARAMETER ShowOnlyPictures
Show only pictures in a rounded rectangle, no text below.
 
.PARAMETER SessionOnly
Use alternative settings stored in session for AI preferences like Language,
Image collections, etc.
 
.PARAMETER ClearSession
Clear alternative settings stored in session for AI preferences like Language,
Image collections, etc.
 
.PARAMETER PreferencesDatabasePath
Database path for preference data files.
 
.PARAMETER SkipSession
Dont use alternative settings stored in session for AI preferences like
Language, Image collections, etc.
 
.EXAMPLE
Find-IndexedImage -Keywords "cat","dog" -ShowInBrowser -NoNudity
 
.EXAMPLE
lii "cat","dog" -ShowInBrowser -NoNudity
#>

###############################################################################
function Find-IndexedImage {

    [CmdletBinding()]
    [OutputType([Object[]], [System.Collections.Generic.List[Object]], [string])]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'fromInput')]
    [Alias("findindexedimages", "lii")]

    param(
        ###############################################################################
        [Parameter(
            Position = 0,
            Mandatory = $false,
            HelpMessage = "Will match any of all the possible meta data types."
        )]
        [string[]] $Any = @(),
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = ("The path to the image database file. If not " +
                        "specified, a default path is used.")
        )]
        [string] $DatabaseFilePath,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Array of directory paths to search for images"
        )]
        [ValidateNotNullOrEmpty()]
        [Alias("imagespath", "directories", "imgdirs", "imagedirectory")]
        [string[]] $ImageDirectories,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = ("Array of directory path-like search strings to " +
                        "filter images by path (SQL LIKE patterns, e.g. " +
                        "'%\\2024\\%')")
        )]
        [string[]] $PathLike = @(),
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Language for descriptions and keywords."
        )]
        [ValidateSet(
            "Afrikaans", "Akan", "Albanian", "Amharic", "Arabic", "Armenian",
            "Azerbaijani", "Basque", "Belarusian", "Bemba", "Bengali", "Bihari",
            "Bork, bork, bork!", "Bosnian", "Breton", "Bulgarian", "Cambodian",
            "Catalan", "Cherokee", "Chichewa", "Chinese (Simplified)",
            "Chinese (Traditional)", "Corsican", "Croatian", "Czech", "Danish",
            "Dutch", "Elmer Fudd", "English", "Esperanto", "Estonian", "Ewe",
            "Faroese", "Filipino", "Finnish", "French", "Frisian", "Ga",
            "Galician", "Georgian", "German", "Greek", "Guarani", "Gujarati",
            "Hacker", "Haitian Creole", "Hausa", "Hawaiian", "Hebrew", "Hindi",
            "Hungarian", "Icelandic", "Igbo", "Indonesian", "Interlingua",
            "Irish", "Italian", "Japanese", "Javanese", "Kannada", "Kazakh",
            "Kinyarwanda", "Kirundi", "Klingon", "Kongo", "Korean",
            "Krio (Sierra Leone)", "Kurdish", "Kurdish (Soranî)", "Kyrgyz",
            "Laothian", "Latin", "Latvian", "Lingala", "Lithuanian", "Lozi",
            "Luganda", "Luo", "Macedonian", "Malagasy", "Malay", "Malayalam",
            "Maltese", "Maori", "Marathi", "Mauritian Creole", "Moldavian",
            "Mongolian", "Montenegrin", "Nepali", "Nigerian Pidgin",
            "Northern Sotho", "Norwegian", "Norwegian (Nynorsk)", "Occitan",
            "Oriya", "Oromo", "Pashto", "Persian", "Pirate", "Polish",
            "Portuguese (Brazil)", "Portuguese (Portugal)", "Punjabi", "Quechua",
            "Romanian", "Romansh", "Runyakitara", "Russian", "Scots Gaelic",
            "Serbian", "Serbo-Croatian", "Sesotho", "Setswana",
            "Seychellois Creole", "Shona", "Sindhi", "Sinhalese", "Slovak",
            "Slovenian", "Somali", "Spanish", "Spanish (Latin American)",
            "Sundanese", "Swahili", "Swedish", "Tajik", "Tamil", "Tatar",
            "Telugu", "Thai", "Tigrinya", "Tonga", "Tshiluba", "Tumbuka",
            "Turkish", "Turkmen", "Twi", "Uighur", "Ukrainian", "Urdu", "Uzbek",
            "Vietnamese", "Welsh", "Wolof", "Xhosa", "Yiddish", "Yoruba", "Zulu"
        )]
        [string] $Language,
        ###############################################################################
        [parameter(
            Mandatory = $false,
            HelpMessage = ("The directory containing face images organized by " +
                        "person folders. If not specified, uses the " +
                        "configured faces directory preference.")
        )]
        [string] $FacesDirectory,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "The description text to look for, wildcards allowed."
        )]
        [string[]] $DescriptionSearch = @(),
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "The keywords to look for, wildcards allowed."
        )]
        [string[]] $Keywords = @(),
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "People to look for, wildcards allowed."
        )]
        [string[]] $People = @(),
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Objects to look for, wildcards allowed."
        )]
        [string[]] $Objects = @(),
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Scenes to look for, wildcards allowed."
        )]
        [string[]] $Scenes = @(),
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Picture types to filter by, wildcards allowed."
        )]
        [string[]] $PictureType = @(),
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Style types to filter by, wildcards allowed."
        )]
        [string[]] $StyleType = @(),
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Overall moods to filter by, wildcards allowed."
        )]
        [string[]] $OverallMood = @(),
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            ValueFromPipeline = $true,
            HelpMessage = ("Accepts search results from a previous -PassThru " +
                        "call to regenerate the view.")
        )]
        [System.Object[]] $InputObject,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Embed images as base64."
        )]
        [switch] $EmbedImages,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Force rebuild of the image index database."
        )]
        [switch] $ForceIndexRebuild,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Switch to disable fallback behavior."
        )]
        [switch] $NoFallback,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Switch to skip database initialization and rebuilding."
        )]
        [switch] $NeverRebuild,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Filter images that contain nudity."
        )]
        [switch] $HasNudity,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Filter images that do NOT contain nudity."
        )]
        [switch] $NoNudity,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Filter images that contain explicit content."
        )]
        [switch] $HasExplicitContent,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Filter images that do NOT contain explicit content."
        )]
        [switch] $NoExplicitContent,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Show results in a browser gallery."
        )]
        [Alias("show", "s")]
        [switch] $ShowInBrowser,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Return image data as objects."
        )]
        [switch] $PassThru,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Title for the image gallery."
        )]
        [string] $Title,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Description for the image gallery."
        )]
        [string] $Description,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Browser accept-language header."
        )]
        [string] $AcceptLang,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Monitor to use for display."
        )]
        [int] $Monitor,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Initial width of browser window."
        )]
        [int] $Width,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Initial height of browser window."
        )]
        [int] $Height,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Initial X position of browser window."
        )]
        [int] $X,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Initial Y position of browser window."
        )]
        [int] $Y,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = ("Show only pictures in a rounded rectangle, no " +
                        "text below.")
        )]
        [Alias("NoMetadata", "OnlyPictures")]
        [switch] $ShowOnlyPictures,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Enable interactive browser features."
        )]
        [Alias("i", "editimages")]
        [switch] $Interactive,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Open in private/incognito mode."
        )]
        [switch] $Private,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Force enable debugging port."
        )]
        [switch] $Force,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Open in Microsoft Edge."
        )]
        [switch] $Edge,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Open in Google Chrome."
        )]
        [switch] $Chrome,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Open in Chromium-based browser."
        )]
        [switch] $Chromium,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Open in Firefox."
        )]
        [switch] $Firefox,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Open in all browsers."
        )]
        [switch] $All,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Open in fullscreen mode."
        )]
        [switch] $FullScreen,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Place window on left side."
        )]
        [switch] $Left,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Place window on right side."
        )]
        [switch] $Right,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Place window on top."
        )]
        [switch] $Top,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Place window on bottom."
        )]
        [switch] $Bottom,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Center the window."
        )]
        [switch] $Centered,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Hide browser controls."
        )]
        [switch] $ApplicationMode,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Disable browser extensions."
        )]
        [switch] $NoBrowserExtensions,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Disable popup blocker."
        )]
        [switch] $DisablePopupBlocker,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Restore PowerShell focus."
        )]
        [switch] $RestoreFocus,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Create new browser window."
        )]
        [switch] $NewWindow,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Only return HTML."
        )]
        [switch] $OnlyReturnHtml,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = ("Use alternative settings stored in session for AI " +
                        "preferences like Language, Image collections, etc")
        )]
        [switch] $SessionOnly,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = ("Clear alternative settings stored in session for " +
                        "AI preferences like Language, Image collections, etc")
        )]
        [switch] $ClearSession,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Database path for preference data files"
        )]
        [string] $PreferencesDatabasePath,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = ("Dont use alternative settings stored in session " +
                        "for AI preferences like Language, Image collections, " +
                        "etc")
        )]
        [Alias("FromPreferences")]
        [switch] $SkipSession
        ###############################################################################
    )

    ###############################################################################
    begin {

        # handle OnlyReturnHtml mode - forces ShowInBrowser with no interactivity
        if ($OnlyReturnHtml) {

            $Interactive = $false
            $ShowInBrowser = $true
        }

        # copy function parameters for ai meta language retrieval
        $params = GenXdev.Helpers\Copy-IdenticalParamValues `
            -BoundParameters $PSBoundParameters `
            -FunctionName "GenXdev.AI\Get-AIMetaLanguage" `
            -DefaultValues (
                Microsoft.PowerShell.Utility\Get-Variable -Scope Local -ErrorAction SilentlyContinue
            )

        # determine the language for metadata retrieval
        $Language = GenXdev.AI\Get-AIMetaLanguage @params

        # initialize result tracking information
        $info = @{
            resultCount = 0
        }

        # create results collection for browser display mode
        [System.Collections.Generic.List[Object]] $results = @()

        # process 'any' parameter to spread across all search types
        if ($null -ne $Any -and
            $Any.Length -gt 0) {

            # add wildcards to any terms that don't already have them
            $Any = @($Any | Microsoft.PowerShell.Core\ForEach-Object {

                $entry = $_.Trim()

                if ($entry.IndexOfAny([char[]]@('*', '?')) -lt 0) {

                    "*$entry*"
                }
                else {
                    $_
                }
            })

            # distribute any terms across all search parameter types
            $DescriptionSearch = $null -ne $DescriptionSearch ?
                ($DescriptionSearch + $Any) :
                $Any

            $Keywords = $null -ne $Keywords ?
                ($Keywords + $Any) :
                $Any

            $People = $null -ne $People ?
                ($People + $Any) :
                $Any

            $Objects = $null -ne $Objects ?
                ($Objects + $Any) :
                $Any

            $Scenes = $null -ne $Scenes ?
                ($Scenes + $Any) :
                $Any

            $PictureType = $null -ne $PictureType ?
                ($PictureType + $Any) :
                $Any

            $StyleType = $null -ne $StyleType ?
                ($StyleType + $Any) :
                $Any

            $OverallMood = $null -ne $OverallMood ?
                ($OverallMood + $Any) :
                $Any
        }
        [System.Collections.Generic.List[Object]] $results = $null

        [bool] $fromInput = $false
    }
    ###############################################################################
    process {

        if ($null -ne $InputObject) {

            @($InputObject) | Microsoft.PowerShell.Core\ForEach-Object -ErrorAction SilentlyContinue {

                $item = $_ | GenXdev.Helpers\ConvertTo-HashTable

                if ($null -eq $results) {

                    # initialize results collection if not already done
                    $results = [System.Collections.Generic.List[Object]]::new()
                    $fromInput = $true
                }

                if ($item -is [System.Collections.IEnumerable] -and
                    (-not $InputObject.ContainsKey("Path"))) {

                        $item | Microsoft.PowerShell.Core\ForEach-Object {

                            $secondItem = $_ | GenXdev.Helpers\ConvertTo-HashTable
                            if ($secondItem.ContainsKey("Path")) {

                            $null = $results.Add($_)
                            $Info.resultCount++
                        }
                    }
                }
                else {

                    # add unique input objects to the results collection
                    $null = $results.Add($_)
                    $Info.resultCount++
                }
            }
        }

        # if we processed input objects, skip database search and use end block for processing
        if ($fromInput) {
            return
        }

        # helper function to convert database result to image object
        function ConvertTo-ImageObject {
            param(
                [Parameter(Mandatory)]
                $DbResult,
                [switch]$EmbedImages
            )

            # determine the path - convert to data URL if embedding is enabled and data exists
            $imagePath = $DbResult.path
            if (
                $EmbedImages -and $null -ne $DbResult.image_data -and
                $DbResult.image_data.Length -gt 0
            ) {
                try {
                    # determine MIME type from file extension
                    $extension = [System.IO.Path]::GetExtension($DbResult.path).ToLower()
                    $mimeType = switch ($extension) {
                        '.jpg'  { 'image/jpeg' }
                        '.jpeg' { 'image/jpeg' }
                        '.png'  { 'image/png' }
                        '.gif'  { 'image/gif' }
                        '.bmp'  { 'image/bmp' }
                        '.webp' { 'image/webp' }
                        '.tiff' { 'image/tiff' }
                        '.tif'  { 'image/tiff' }
                        default { 'image/jpeg' }  # fallback
                    }

                    # convert bytes to base64 data URL
                    $base64 = [Convert]::ToBase64String($DbResult.image_data)
                    $imagePath = (
                        "data:$mimeType;base64,$base64"
                    )

                    # output verbose message for conversion
                    Microsoft.PowerShell.Utility\Write-Verbose (
                        "Converted embedded image to data URL: $($DbResult.path) -> " +
                        "$mimeType ($($DbResult.image_data.Length) bytes)"
                    )
                }
                catch {
                    # output warning if conversion fails
                    Microsoft.PowerShell.Utility\Write-Warning (
                        "Failed to convert embedded image data to base64 for: " +
                        "$($DbResult.path) - $($_.Exception.Message)"
                    )

                    # fallback to original path
                    $imagePath = $DbResult.path
                }
            }

            # return a custom object with all image metadata
            return [System.Collections.Hashtable]@{
                path = $imagePath
                keywords = if ($DbResult.description_keywords) {
                    $DbResult.description_keywords |
                        Microsoft.PowerShell.Utility\ConvertFrom-Json
                } else { @() }
                people = if ($DbResult.people_json) {
                    $peopleObj = $DbResult.people_json |
                        Microsoft.PowerShell.Utility\ConvertFrom-Json

                    # ensure predictions array is present
                    if (-not $peopleObj.PSObject.Properties['predictions']) {
                        $peopleObj | Microsoft.PowerShell.Utility\Add-Member `
                            -MemberType NoteProperty `
                            -Name predictions `
                            -Value @()
                    }
                    $peopleObj
                } else {
                    [PSCustomObject]@{
                        count = $DbResult.people_count
                        faces = if ($DbResult.people_faces) {
                            $DbResult.people_faces |
                                Microsoft.PowerShell.Utility\ConvertFrom-Json
                        } else { @() }
                        predictions = @()
                    }
                }
                description = if ($DbResult.description_json) {
                    $DbResult.description_json |
                        Microsoft.PowerShell.Utility\ConvertFrom-Json
                } else {
                    [PSCustomObject]@{
                        has_explicit_content = [bool]$DbResult.has_explicit_content
                        short_description = $DbResult.short_description
                        long_description = $DbResult.long_description
                        has_nudity = [bool]$DbResult.has_nudity
                        picture_type = $DbResult.picture_type
                        overall_mood_of_image = $DbResult.overall_mood_of_image
                        style_type = $DbResult.style_type
                        keywords = if ($DbResult.description_keywords) {
                            $DbResult.description_keywords |
                                Microsoft.PowerShell.Utility\ConvertFrom-Json
                        } else { @() }
                    }
                }
                scenes = if ($DbResult.scenes_json) {
                    $DbResult.scenes_json |
                        Microsoft.PowerShell.Utility\ConvertFrom-Json
                } else {
                    [PSCustomObject]@{
                        success = $true
                        scene = $DbResult.scene_label
                        processed_at = $DbResult.scene_processed_at
                        confidence = $DbResult.scene_confidence
                        label = $DbResult.scene_label
                        confidence_percentage = $DbResult.scene_confidence_percentage
                    }
                }
                objects = if ($DbResult.objects_json) {
                    $objectsObj = $DbResult.objects_json |
                        Microsoft.PowerShell.Utility\ConvertFrom-Json

                    # ensure objects array is present
                    if (-not $objectsObj.PSObject.Properties['objects']) {
                        $objectsObj | Microsoft.PowerShell.Utility\Add-Member `
                            -MemberType NoteProperty `
                            -Name objects `
                            -Value @()
                    }
                    $objectsObj
                } else {
                    [PSCustomObject]@{
                        objects = if ($DbResult.objects_list) {
                            $DbResult.objects_list |
                                Microsoft.PowerShell.Utility\ConvertFrom-Json
                        } else { @() }
                        object_counts = if ($DbResult.object_counts) {
                            $DbResult.object_counts |
                                Microsoft.PowerShell.Utility\ConvertFrom-Json
                        } else { @{} }
                        count = $DbResult.objects_count
                    }
                }
            }
        }

        # determine database file path if not provided
        $params = GenXdev.Helpers\Copy-IdenticalParamValues `
            -BoundParameters $PSBoundParameters `
            -FunctionName "GenXdev.AI\Get-ImageDatabasePath" `
            -DefaultValues (
                Microsoft.PowerShell.Utility\Get-Variable -Scope Local -ErrorAction SilentlyContinue
            )

        $DatabaseFilePath = GenXdev.AI\Get-ImageDatabasePath @params

        if ($null -eq $DatabaseFilePath) {
            Microsoft.PowerShell.Utility\Write-Error (
                "Failed to retrieve database file path."
            )
            return
        }

        Microsoft.PowerShell.Utility\Write-Verbose (
            "Using image database: $DatabaseFilePath"
        )

        # helper function to convert PowerShell wildcards to SQLite LIKE patterns
        function ConvertTo-SqliteLikePattern {
            param(
                [string]$Pattern,
                [switch]$ForceWildcards
            )
            $escaped = $Pattern
            if ($ForceWildcards -and ($escaped.indexOfAny(@('*', '?')) -lt 0)) {

                $escaped = "*$escaped*"
            }

            # escape literal square brackets first
            $escaped = $escaped.Replace('[', '[[]')

            # escape literal percent signs
            $escaped = $escaped.Replace('%', '[%]')

            # escape literal underscores
            $escaped = $escaped.Replace('_', '[_]')

            # * becomes % (zero or more chars)
            $escaped = $escaped.Replace('*', '%')

            # ? becomes _ (exactly one char)
            $escaped = $escaped.Replace('?', '_')
            return $escaped
        }

        # helper function to optimize search conditions and avoid table scans
        function Get-OptimizedSearchCondition {
            param(
                [string]$ColumnName,
                [string]$TableAlias,
                [string]$SearchTerm,
                [string]$ParamName,
                [hashtable]$Parameters,
                [switch]$ForceWildcards
            )

            # check if this is a wildcard pattern
            if ($SearchTerm.Contains('*') -or $SearchTerm.Contains('?')) {
                $pattern = ConvertTo-SqliteLikePattern -Pattern $SearchTerm -ForceWildcards:$ForceWildcards

                # optimize for patterns that start with known text (no leading wildcard)
                if (-not $pattern.StartsWith('%')) {
                    # use prefix index for patterns like "cat*" -> "cat%"
                    $condition = (
                        "$TableAlias.$ColumnName LIKE @$ParamName COLLATE NOCASE"
                    )
                    $Parameters[$ParamName] = $pattern
                } else {
                    # fallback to case-insensitive LIKE for patterns with leading wildcards
                    $condition = (
                        "$TableAlias.$ColumnName LIKE @$ParamName COLLATE NOCASE"
                    )
                    $Parameters[$ParamName] = $pattern
                }
            } else {
                # exact match - most efficient, uses exact indexes
                $condition = (
                    "$TableAlias.$ColumnName = @$ParamName COLLATE NOCASE"
                )
                $Parameters[$ParamName] = $SearchTerm
            }
            return $condition
        }

        # build the SQL query with optimized joins and indexes
        $sqlQuery = "SELECT DISTINCT i.* FROM Images i"
        $joinClauses = @()
        $whereClauses = @()
        $parameters = @{}
        $paramCounter = 0

        # add description search with optimized indexed lookup (NO TABLE SCANS)
        if ($DescriptionSearch -and $DescriptionSearch.Count -gt 0) {

            $descriptionConditions = @()
            foreach ($description in $DescriptionSearch) {
                $paramName = "Description$paramCounter"
                $condition = Get-OptimizedSearchCondition `
                    -ColumnName "short_description" `
                    -TableAlias "i" `
                    -SearchTerm $description `
                    -ParamName $paramName `
                    -Parameters $parameters
                $descriptionConditions += $condition
                $paramCounter++
                $paramName = "Description$paramCounter"
                $condition = Get-OptimizedSearchCondition `
                    -ColumnName "long_description" `
                    -TableAlias "i" `
                    -SearchTerm $description `
                    -ParamName $paramName `
                    -Parameters $parameters
                $descriptionConditions += $condition
                $paramCounter++
            }
            $whereClauses += ("(" + ($descriptionConditions -join " OR ") + ")")
        }

        # add keyword search with optimized indexed lookup (NO TABLE SCANS)
        if ($Keywords -and $Keywords.Count -gt 0) {
            $keywordConditions = @()
            foreach ($keyword in $Keywords) {
                $paramName = "keyword$paramCounter"
                $condition = Get-OptimizedSearchCondition `
                    -ColumnName "keyword" `
                    -TableAlias "ik" `
                    -SearchTerm $keyword `
                    -ParamName $paramName `
                    -Parameters $parameters
                $keywordConditions += $condition
                $paramCounter++
            }
            $whereClauses += ("(EXISTS (SELECT * FROM ImageKeywords ik WHERE ik.image_id = i.id AND (" +
                ($keywordConditions -join " OR ") + ")))")
        }

        # add people search with optimized indexed lookup (NO TABLE SCANS)
        if ($People -and $People.Count -gt 0) {
            $peopleConditions = @()
            foreach ($person in $People) {
                $paramName = "person$paramCounter"
                $condition = Get-OptimizedSearchCondition `
                    -ColumnName "person_name" `
                    -TableAlias "ip" `
                    -SearchTerm $person `
                    -ParamName $paramName `
                    -Parameters $parameters
                $peopleConditions += $condition
                $paramCounter++
            }
            $whereClauses += ("(EXISTS (SELECT * FROM ImagePeople ip WHERE ip.image_id = i.id AND (" +
                ($peopleConditions -join " OR ") + ")))")
        }

        # add objects search with optimized indexed lookup (NO TABLE SCANS)
        if ($Objects -and $Objects.Count -gt 0) {
            $objectConditions = @()
            foreach ($obj in $Objects) {
                $paramName = "object$paramCounter"
                $condition = Get-OptimizedSearchCondition `
                    -ColumnName "object_name" `
                    -TableAlias "io" `
                    -SearchTerm $obj `
                    -ParamName $paramName `
                    -Parameters $parameters
                $objectConditions += $condition
                $paramCounter++
            }
            $whereClauses += ("(EXISTS (SELECT * FROM ImageObjects io WHERE io.image_id = i.id AND (" +
                ($objectConditions -join " OR ") + ")))")
        }

        # add scenes search with optimized indexed lookup (NO TABLE SCANS)
        if ($Scenes -and $Scenes.Count -gt 0) {
            $sceneConditions = @()
            foreach ($scene in $Scenes) {
                $paramName = "scene$paramCounter"
                $condition = Get-OptimizedSearchCondition `
                    -ColumnName "scene_name" `
                    -TableAlias "isc" `
                    -SearchTerm $scene `
                    -ParamName $paramName `
                    -Parameters $parameters
                $sceneConditions += $condition
                $paramCounter++
            }
            $whereClauses += ("(EXISTS (SELECT * FROM ImageScenes isc WHERE isc.image_id = i.id AND (" +
                ($sceneConditions -join " OR ") + ")))")
        }

        # add picture type filter with optimized indexed column (NO TABLE SCANS)
        if ($PictureType -and $PictureType.Count -gt 0) {
            $pictureTypeConditions = @()
            foreach ($type in $PictureType) {
                $paramName = "pictype$paramCounter"
                $condition = Get-OptimizedSearchCondition `
                    -ColumnName "picture_type" `
                    -TableAlias "i" `
                    -SearchTerm $type `
                    -ParamName $paramName `
                    -Parameters $parameters
                $pictureTypeConditions += $condition
                $paramCounter++
            }
            $whereClauses += ("(" + ($pictureTypeConditions -join " OR ") + ")")
        }

        # add style type filter with optimized indexed column (NO TABLE SCANS)
        if ($StyleType -and $StyleType.Count -gt 0) {
            $styleTypeConditions = @()
            foreach ($style in $StyleType) {
                $paramName = "styletype$paramCounter"
                $condition = Get-OptimizedSearchCondition `
                    -ColumnName "style_type" `
                    -TableAlias "i" `
                    -SearchTerm $style `
                    -ParamName $paramName `
                    -Parameters $parameters
                $styleTypeConditions += $condition
                $paramCounter++
            }
            $whereClauses += ("(" + ($styleTypeConditions -join " OR ") + ")")
        }

        # add mood filter with optimized indexed column (NO TABLE SCANS)
        if ($OverallMood -and $OverallMood.Count -gt 0) {
            $moodConditions = @()
            foreach ($mood in $OverallMood) {
                $paramName = "mood$paramCounter"
                $condition = Get-OptimizedSearchCondition `
                    -ColumnName "overall_mood_of_image" `
                    -TableAlias "i" `
                    -SearchTerm $mood `
                    -ParamName $paramName `
                    -Parameters $parameters
                $moodConditions += $condition
                $paramCounter++
            }
            $whereClauses += ("(" + ($moodConditions -join " OR ") + ")")
        }

        # add nudity filters with indexed boolean columns
        if ($HasNudity) {
            $whereClauses += "i.has_nudity = 1"
        }
        if ($NoNudity) {
            $whereClauses += "i.has_nudity = 0"
        }

        # add explicit content filters with indexed boolean columns
        if ($HasExplicitContent) {
            $whereClauses += "i.has_explicit_content = 1"
        }
        if ($NoExplicitContent) {
            $whereClauses += "i.has_explicit_content = 0"
        }

        # add path-like search with optimized LIKE lookup (NO TABLE SCANS)
        if ($PathLike -and $PathLike.Count -gt 0) {
            $pathLikeConditions = @()
            foreach ($pathPattern in $PathLike) {
                $paramName = "pathlike$paramCounter"

                # convert file: URLs to local paths if needed
                if ($pathPattern -like 'file:*') {
                    $localPath = $pathPattern.Substring(5)

                    # decode URL encoding if present
                    $localPath = [System.Uri]::UnescapeDataString($localPath)
                } else {
                    $localPath = $pathPattern
                }

                $filter = GenXdev.FileSystem\Expand-Path $localPath
                $sqlitePattern = ConvertTo-SqliteLikePattern -Pattern $filter -ForceWildcards
                $pathLikeConditions += "i.path LIKE @$paramName COLLATE NOCASE"
                $parameters[$paramName] = $sqlitePattern
                $paramCounter++
            }

            $whereClauses += ("(" + ($pathLikeConditions -join " OR ") + ")")
        }

        # build the complete query with index optimization hints
        $sqlQuery = "SELECT DISTINCT i.* FROM Images i"

        if ($joinClauses.Count -gt 0) {
            $sqlQuery += " " + ($joinClauses -join " ")
        }
        if ($whereClauses.Count -gt 0) {
            $sqlQuery += " WHERE " + ($whereClauses -join " OR ")
        }

        # use indexed ordering for optimal performance
        $sqlQuery += " ORDER BY i.path"

        Microsoft.PowerShell.Utility\Write-Verbose (
            "Executing NO-TABLE-SCAN optimized SQL query: $sqlQuery"
        )
        Microsoft.PowerShell.Utility\Write-Verbose (
            "With parameters: $($parameters | Microsoft.PowerShell.Utility\ConvertTo-Json -Compress)"
        )

        # execute the query with parameters
        $startTime = Microsoft.PowerShell.Utility\Get-Date

        # for ShowInBrowser we need to collect all results first, otherwise we stream them
        if ($ShowInBrowser) {

            $dbResults = GenXdev.Data\Invoke-SQLiteQuery `
                -DatabaseFilePath $DatabaseFilePath `
                -Queries $sqlQuery `
                -SqlParameters $parameters
            $queryTime = (Microsoft.PowerShell.Utility\Get-Date) - $startTime

            Microsoft.PowerShell.Utility\Write-Verbose (
                "Index-optimized database query completed in " +
                "$($queryTime.TotalMilliseconds)ms, found $($dbResults.Count) " +
                "results (no table scans)"
            )

            # convert database results to image objects compatible with Show-FoundImagesInBrowser
            foreach ($dbResult in $dbResults) {
                $imageObj = ConvertTo-ImageObject -DbResult $dbResult -EmbedImages:$EmbedImages
                $Info.resultCount++

                if ($null -eq $results) {

                    # initialize results collection if not already done
                    $results = [System.Collections.Generic.List[Object]]::new()
                }

                $null = $results.Add($imageObj)
            }
        } else {
            # stream results for memory efficiency - process each record as it comes from the database
            $Info.resultCount = 0
            GenXdev.Data\Invoke-SQLiteQuery `
                -DatabaseFilePath $DatabaseFilePath `
                -Queries $sqlQuery `
                -SqlParameters $parameters |
                Microsoft.PowerShell.Core\ForEach-Object {
                    $Info.resultCount++
                    $imageObj = ConvertTo-ImageObject -DbResult $_ -EmbedImages:$EmbedImages
                    Microsoft.PowerShell.Utility\Write-Output $imageObj
                    $info.resultCount++
                }

            $queryTime = (Microsoft.PowerShell.Utility\Get-Date) - $startTime
            Microsoft.PowerShell.Utility\Write-Verbose (
                "Index-optimized database query completed in " +
                "$($queryTime.TotalMilliseconds)ms, streamed $resultCount " +
                "results (no table scans)"
            )
        }
    }

    ###############################################################################
    end {
        # handle input object processing from pipeline
        # $fromInput is set to true in process block when InputObject is provided
        if ($null -ne $results -and $fromInput) {

            # copy parameters for find-image call
            $params = GenXdev.Helpers\Copy-IdenticalParamValues `
                -BoundParameters $PSBoundParameters `
                -FunctionName "GenXdev.AI\Find-Image" `
                -DefaultValues (
                    Microsoft.PowerShell.Utility\Get-Variable -Scope Local -ErrorAction SilentlyContinue
                )

            $params.InputObject = $results
            # if InputObject is provided, convert each item to an image object
            GenXdev.AI\Find-Image @params

            return
        }
        # this end block only executes for ShowInBrowser mode since streaming mode exits early

        # provide appropriate message if no results were found in streaming mode
        if ($Info.resultCount -eq 0) {
            $searchCriteria = [System.Collections.Generic.List[string]]::new()
            if ($Keywords -and $Keywords.Count -gt 0) {
                $searchCriteria.Add("keywords: $($Keywords -join ', ')")
            }
            if ($People -and $People.Count -gt 0) {
                $searchCriteria.Add("people: $($People -join ', ')")
            }
            if ($Objects -and $Objects.Count -gt 0) {
                $searchCriteria.Add("objects: $($Objects -join ', ')")
            }
            if ($Scenes -and $Scenes.Count -gt 0) {
                $searchCriteria.Add("scenes: $($Scenes -join ', ')")
            }
            if ($PictureType -and $PictureType.Count -gt 0) {
                $searchCriteria.Add("picture types: $($PictureType -join ', ')")
            }
            if ($StyleType -and $StyleType.Count -gt 0) {
                $searchCriteria.Add("style types: $($StyleType -join ', ')")
            }
            if ($OverallMood -and $OverallMood.Count -gt 0) {
                $searchCriteria.Add("overall moods: $($OverallMood -join ', ')")
            }
            if ($HasNudity) {
                $searchCriteria.Add("has nudity")
            }
            if ($NoNudity) {
                $searchCriteria.Add("no nudity")
            }
            if ($HasExplicitContent) {
                $searchCriteria.Add("has explicit content")
            }
            if ($NoExplicitContent) {
                $searchCriteria.Add("no explicit content")
            }
            if ($PathLike -and $PathLike.Count -gt 0) {
                $searchCriteria.Add("path-like: $($PathLike -join ', ')")
            }

            if ($searchCriteria.Count -gt 0) {
                Microsoft.PowerShell.Utility\Write-Host (
                    "No images found matching search criteria: " +
                    "$($searchCriteria -join ', ')"
                ) -ForegroundColor Yellow
            }
            else {
                Microsoft.PowerShell.Utility\Write-Host (
                    "No images found in database"
                ) -ForegroundColor Yellow
            }
        }

        # if ShowInBrowser is requested, display the gallery
        if ($ShowInBrowser) {

            if ([String]::IsNullOrWhiteSpace($Title)) {
                $Title = "🚀 Fast Indexed Image Search Results"
            }

            if ([String]::IsNullOrWhiteSpace($Description)) {
                $searchInfo = "Database search completed in " +
                    "$($queryTime.TotalMilliseconds)ms | Found $($results.Count) images"
                $Description = "$($MyInvocation.Statement) | $searchInfo"
            }

            # copy all the gallery-related parameters
            $galleryParams = GenXdev.Helpers\Copy-IdenticalParamValues `
                -BoundParameters $PSBoundParameters `
                -FunctionName "GenXdev.AI\Show-FoundImagesInBrowser" `
                -DefaultValues (
                    Microsoft.PowerShell.Utility\Get-Variable -Scope Local -ErrorAction SilentlyContinue
            );

            $galleryParams.InputObject = $results

            # pass the results to Show-FoundImagesInBrowser
            GenXdev.AI\Show-FoundImagesInBrowser @galleryParams

            if ($PassThru) {

                $results | Microsoft.PowerShell.Core\ForEach-Object {

                    Microsoft.PowerShell.Utility\Write-Output $_
                }
            }
        }
    }
}
###############################################################################