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 DescriptionSearch
The description text to look for, wildcards allowed
 
.PARAMETER Keywords
Array of keywords to search for in image metadata. Supports wildcards. Keywords
are matched against both the description content and the keywords table.
 
.PARAMETER People
Array of people names to search for in image metadata. Supports wildcards. Uses
the optimized ImagePeople lookup table for fast searches.
 
.PARAMETER Objects
Array of object names to search for in image metadata. Supports wildcards. Uses
the ImageObjects lookup table with indexed searches.
 
.PARAMETER Scenes
Array of scene categories to search for in image metadata. Supports wildcards.
Uses the ImageScenes lookup table for efficient scene-based filtering.
 
.PARAMETER PictureType
Array of picture types to filter by (e.g., 'daylight', 'evening', 'indoor',
'outdoor'). Supports wildcards. Uses indexed column searches.
 
.PARAMETER StyleType
Array of style types to filter by (e.g., 'casual', 'formal'). Supports
wildcards. Uses indexed column searches.
 
.PARAMETER OverallMood
Array of overall moods to filter by (e.g., 'calm', 'cheerful', 'sad',
'energetic'). Supports wildcards. Uses indexed column searches.
 
.PARAMETER HasNudity
Switch to filter for images that contain nudity. Uses indexed boolean column.
 
.PARAMETER NoNudity
Switch to filter for images that do NOT contain nudity. Uses indexed boolean column.
 
.PARAMETER HasExplicitContent
Switch to filter for images that contain explicit content. Uses indexed boolean column.
 
.PARAMETER NoExplicitContent
Switch to filter for images that do NOT contain explicit content. Uses indexed boolean column.
 
.PARAMETER DatabaseFilePath
Optional path to the SQLite database file. If not specified, uses the default
location under Storage\allimages.meta.db.
 
.PARAMETER ShowInBrowser
Switch to display the search results in a browser-based masonry layout gallery.
 
.PARAMETER PassThru
Switch to return image data as objects.
 
.PARAMETER Title
The title to display at the top of the image gallery.
 
.PARAMETER Description
The description text to display in the image gallery.
 
.PARAMETER Language
The language for retrieving descriptions and keywords.
 
.PARAMETER AcceptLang
Set the browser accept-lang http header.
 
.PARAMETER Monitor
The monitor to use for displaying the gallery.
 
.PARAMETER Width
The initial width of the web browser window.
 
.PARAMETER Height
The initial height of the web browser window.
 
.PARAMETER X
The initial X position of the web browser window.
 
.PARAMETER Y
The initial Y position of the web browser window.
 
.PARAMETER Interactive
Connects to browser and adds additional buttons like Edit and Delete.
 
.PARAMETER Private
Opens in incognito/private browsing mode.
 
.PARAMETER Force
Force enable debugging port, stopping existing browsers if needed.
 
.PARAMETER Edge
Opens in Microsoft Edge.
 
.PARAMETER Chrome
Opens in Google Chrome.
 
.PARAMETER Chromium
Opens in Microsoft Edge or Google Chrome.
 
.PARAMETER Firefox
Opens in Firefox.
 
.PARAMETER All
Opens in all registered modern browsers.
 
.PARAMETER FullScreen
Opens in fullscreen mode.
 
.PARAMETER Left
Place browser window on the left side of the screen.
 
.PARAMETER Right
Place browser window on the right side of the screen.
 
.PARAMETER Top
Place browser window on the top side of the screen.
 
.PARAMETER Bottom
Place browser window on the bottom side of the screen.
 
.PARAMETER Centered
Place browser window in the center of the screen.
 
.PARAMETER ApplicationMode
Hide the browser controls.
 
.PARAMETER NoBrowserExtensions
Prevent loading of browser extensions.
 
.PARAMETER DisablePopupBlocker
Disable the popup blocker.
 
.PARAMETER RestoreFocus
Restore PowerShell window focus.
 
.PARAMETER NewWindow
Don't re-use existing browser window, instead, create a new one.
 
.PARAMETER OnlyReturnHtml
Only return the generated HTML instead of displaying it in a browser.
 
.PARAMETER EmbedImages
Switch to embed images as base64 data URLs instead of file:// URLs.
 
.PARAMETER ForceIndexRebuild
Force rebuild of the image index database.
 
.PARAMETER PathLike
Array of directory path-like search strings to filter images by path (SQL LIKE
patterns, e.g. '%\2024\%').
 
.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])]
    [Alias("findindexedimages", "lii")]

    param(
        ###############################################################################
        [Parameter(
            Position = 0,
            Mandatory = $false,
            HelpMessage = "Will match any of all the possible meta data types."
        )]
        [string[]] $Any = @(),
        ###############################################################################
        [Parameter(
            Position = 0,
            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 = "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 = "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,
            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 = "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,
            ValueFromPipeline = $true,
            HelpMessage = ("Accepts search results from a previous -PassThru " +
                "call to regenerate the view.")
        )]
        [object[]] $InputObject,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = "Show only pictures in a rounded rectangle, no text below."
        )]
        [Alias("NoMetadata", "OnlyPictures")]
        [switch] $ShowOnlyPictures,
        ########################################################################
        # Use alternative settings stored in session for AI preferences like Language, Image collections, etc
        [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 = "Dont use alternative settings stored in session for AI preferences like Language, Image collections, etc"
        )]
        [Alias("FromPreferences")]
        [switch] $SkipSession
        ########################################################################
    )

    ###############################################################################
    begin {
        $params = GenXdev.Helpers\Copy-IdenticalParamValues `
            -BoundParameters $PSBoundParameters `
            -FunctionName "GenXdev.AI\Get-AIMetaLanguage" `
            -DefaultValues (Microsoft.PowerShell.Utility\Get-Variable -Scope Local -ErrorAction SilentlyContinue)
        $Language = GenXdev.AI\Get-AIMetaLanguage @params -Language (
            [String]::IsNullOrWhiteSpace($Language) ?
            (GenXdev.Helpers\Get-DefaultWebLanguage) :
            $Language
        )

        $info = @{
            resultCount = 0
        }
        [System.Collections.Generic.List[Object]] $results = @()

        if ($null -ne $Any -and
            $Any.Length -gt 0) {

            $Any = @($Any | Microsoft.PowerShell.Core\ForEach-Object {

                $entry = $_.Trim()

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

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

            # if Any parameter is used, treat it as a set of keywords
            $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
        }
    }
    ###############################################################################
    process {

        # handle input object processing from 4
        if ($PSBoundParameters.ContainsKey('InputObject')) {

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

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

            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++
                $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 {
        # 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
            )

            # pass the results to Show-FoundImagesInBrowser
            $null = GenXdev.AI\Show-FoundImagesInBrowser @galleryParams -InputObject $results

            if ($PassThru) {

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

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