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.
 
When no selection/filter criteria are specified, the function automatically
sets PathLike to search the current directory. With -NoRecurse, it searches
only the current directory; without -NoRecurse, it searches recursively.
 
Parameter Logic:
- Within each parameter type (Keywords, People, Objects, etc.): Uses OR logic
  Example: -Keywords "cat","dog" finds images with EITHER cat OR dog
- Between different parameter types: Uses AND logic
  Example: -Keywords "cat" -People "John" finds images with cat AND John
- EXIF range parameters: Provide [min, max] values for filtering ranges
- String parameters: Support wildcard matching with * and ? (converted to SQL LIKE)
 
.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 SendKeyEscape
Escape control characters and modifiers when sending keys.
 
.PARAMETER SendKeyHoldKeyboardFocus
Prevents returning keyboard focus to PowerShell after sending keys.
 
.PARAMETER SendKeyUseShiftEnter
Use Shift+Enter instead of Enter when sending keys.
 
.PARAMETER SendKeyDelayMilliSeconds
Delay between different input strings in milliseconds when sending keys.
 
.PARAMETER NoBorders
Remove window borders and title bar for a cleaner appearance.
 
.PARAMETER SideBySide
Place browser window side by side with PowerShell on the same monitor.
 
.PARAMETER Title
Title for the image gallery.
 
.PARAMETER Description
Description for the image gallery.
 
.PARAMETER FocusWindow
Focus the browser window after opening.
 
.PARAMETER SetForeground
Set the browser window to foreground after opening.
 
.PARAMETER Maximize
Maximize the browser window after positioning.
 
.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.
 
.PARAMETER FocusWindow
Focus the browser window when opening.
 
.PARAMETER SetForeground
Bring browser window to foreground.
 
.PARAMETER KeysToSend
Array of key combinations to send to the browser after opening (e.g., @('F11', 'Ctrl+Shift+I')).
 
.PARAMETER MinConfidenceRatio
Minimum confidence ratio (0.0-1.0) for filtering people, scenes, and objects
by confidence. Only returns data for people, scenes, and objects with confidence
greater than or equal to this value. When specified, filters out low-confidence
detection results from people, scenes, and objects data while keeping the image.
 
.PARAMETER Append
When used with InputObject, first outputs all InputObject content, then
processes as if InputObject was not set. Allows appending search results to
existing collections.
 
.EXAMPLE
Find-IndexedImage -Keywords "cat","dog" -ShowInBrowser -NoNudity
 
.EXAMPLE
lii "cat","dog" -ShowInBrowser -NoNudity
 
.EXAMPLE
Find-IndexedImage
Searches all images in the current directory and subdirectories (no filter criteria specified).
 
.EXAMPLE
Find-IndexedImage -NoRecurse
Searches only images in the current directory, without recursing into subdirectories.
#>

###############################################################################
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'
        )]
        [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.'
        )]
        [Alias('pt')]
        [switch]$PassThru,

        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Escape control characters and modifiers when sending keys'
        )]
        [Alias('Escape')]
        [switch] $SendKeyEscape,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Prevents returning keyboard focus to PowerShell after sending keys'
        )]
        [Alias('HoldKeyboardFocus')]
        [switch] $SendKeyHoldKeyboardFocus,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Use Shift+Enter instead of Enter when sending keys'
        )]
        [Alias('UseShiftEnter')]
        [switch] $SendKeyUseShiftEnter,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Delay between different input strings in milliseconds when sending keys'
        )]
        [Alias('DelayMilliSeconds')]
        [int] $SendKeyDelayMilliSeconds,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Remove window borders and title bar for a cleaner appearance'
        )]
        [Alias('nb')]
        [switch] $NoBorders,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Place browser window side by side with PowerShell on the same monitor'
        )]
        [Alias('sbs')]
        [switch]$SideBySide,

        ###############################################################################
        [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.'
        )]
        [Alias('lang', 'locale')]
        [string] $AcceptLang,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Monitor to use for display.'
        )]
        [Alias('m', 'mon')]
        [int] $Monitor = -2,
        ###############################################################################
        [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.'
        )]
        [Alias('incognito', 'inprivate')]
        [switch] $Private,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Force enable debugging port.'
        )]
        [switch] $Force,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Open in Microsoft Edge.'
        )]
        [Alias('e')]
        [switch] $Edge,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Open in Google Chrome.'
        )]
        [Alias('ch')]
        [switch] $Chrome,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Open in Chromium-based browser.'
        )]
        [Alias('c')]
        [switch] $Chromium,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Open in Firefox.'
        )]
        [Alias('ff')]
        [switch] $Firefox,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Open in all browsers.'
        )]
        [switch] $All,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Open in fullscreen mode.'
        )]
        [Alias('sw')]
        [switch]$ShowWindow,
        ###############################################################################
        [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.'
        )]
        [Alias('a', 'app', 'appmode')]
        [switch] $ApplicationMode,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Disable browser extensions.'
        )]
        [Alias('de', 'ne', 'NoExtensions')]
        [switch] $NoBrowserExtensions,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Disable popup blocker.'
        )]
        [Alias('allowpopups')]
        [switch] $DisablePopupBlocker,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Restore PowerShell focus.'
        )]
        [Alias('rf', 'bg')]
        [switch]$RestoreFocus,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Create new browser window.'
        )]
        [Alias('nw', 'new')]
        [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,
        ###############################################################################
        ###############################################################################
        [Alias('DatabasePath')]
        [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,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Focus the browser window after opening'
        )]
        [Alias('fw','focus')]
        [switch] $FocusWindow,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Set the browser window to foreground after opening'
        )]
        [Alias('fg')]
        [switch] $SetForeground,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Set the window to foreground after opening'
        )]
        [switch] $Maximize,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Send specified keys to the browser window after opening'
        )]
        [string[]] $KeysToSend,

        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Auto-scroll the page by this many pixels per second (null to disable)'
        )]
        [int]$AutoScrollPixelsPerSecond = $null,

        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Animate rectangles (objects/faces) in visible range, cycling every 300ms'
        )]
        [switch]$AutoAnimateRectangles,

        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Force single column layout (centered, 1/3 screen width)'
        )]
        [switch]$SingleColumnMode = $false,

        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Prefix to prepend to each image path (e.g. for remote URLs)'
        )]
        [string]$ImageUrlPrefix = '',
        ###############################################################################
        <#
        .PARAMETER AllDrives
        Search across all available drives.
        #>

        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Search across all available drives'
        )]
        [switch] $AllDrives,
        ###############################################################################
        <#
        .PARAMETER NoRecurse
        Do not recurse into subdirectories.
        #>

        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Do not recurse into subdirectories'
        )]
        [switch] $NoRecurse,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Filter by camera make in image EXIF metadata (manufacturer name).'
        )]
        [string[]] $MetaCameraMake = @(),
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Filter by camera model in image EXIF metadata.'
        )]
        [string[]] $MetaCameraModel = @(),
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Filter by GPS latitude in image EXIF metadata. Single value or range.'
        )]
        [double[]] $MetaGPSLatitude,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Filter by GPS longitude in image EXIF metadata. Single value or range.'
        )]
        [double[]] $MetaGPSLongitude,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Filter by GPS altitude in image EXIF metadata. Single value or range.'
        )]
        [double[]] $MetaGPSAltitude,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Filter by exposure time in image EXIF metadata (in seconds).'
        )]
        [double[]] $MetaExposureTime,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Filter by F-number (aperture) in image EXIF metadata.'
        )]
        [double[]] $MetaFNumber,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Filter by ISO speed in image EXIF metadata.'
        )]
        [int[]] $MetaISO,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Filter by focal length in image EXIF metadata (in mm).'
        )]
        [double[]] $MetaFocalLength,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Filter by image width in pixels from EXIF metadata.'
        )]
        [int[]] $MetaWidth,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Filter by image height in pixels from EXIF metadata.'
        )]
        [int[]] $MetaHeight,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Filter by date taken from EXIF metadata. Can be a date range.'
        )]
        [DateTime[]] $MetaDateTaken,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Find images near this location (latitude,longitude).'
        )]
        [double[]] $GeoLocation,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Maximum distance in meters from GeoLocation to search for images.'
        )]
        [double] $GeoDistanceInMeters = 1000,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = ('Minimum confidence ratio (0.0-1.0) for filtering ' +
                'people, scenes, and objects by confidence. Only returns data for ' +
                'people, scenes, and objects with confidence greater than or equal ' +
                'to this value.')
        )]
        [ValidateRange(0.0, 1.0)]
        [double] $MinConfidenceRatio,
        ###############################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = ('When used with InputObject, first outputs all ' +
                'InputObject content, then processes as if InputObject was not set. ' +
                'Allows appending search results to existing collections.')
        )]
        [switch] $Append
        ###############################################################################
    )

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

        # handle html only mode - forces show in browser without interactive features
        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 = @()

        # initialize results collection
        [System.Collections.Generic.List[Object]] $results = $null

        # initialize input tracking flag
        [bool] $fromInput = $false

        # detect if no selection/filter criteria are specified
        $hasSelectionCriteria = (
            ($null -ne $Any -and $Any.Count -gt 0) -or
            ($null -ne $InputObject) -or
            ($null -ne $DescriptionSearch -and $DescriptionSearch.Count -gt 0) -or
            ($null -ne $Keywords -and $Keywords.Count -gt 0) -or
            ($null -ne $People -and $People.Count -gt 0) -or
            ($null -ne $Objects -and $Objects.Count -gt 0) -or
            ($null -ne $Scenes -and $Scenes.Count -gt 0) -or
            ($null -ne $PictureType -and $PictureType.Count -gt 0) -or
            ($null -ne $StyleType -and $StyleType.Count -gt 0) -or
            ($null -ne $OverallMood -and $OverallMood.Count -gt 0) -or
            ($null -ne $PathLike -and $PathLike.Count -gt 0) -or
            $HasNudity -or $NoNudity -or $HasExplicitContent -or $NoExplicitContent -or
            ($null -ne $MetaCameraMake -and $MetaCameraMake.Count -gt 0) -or
            ($null -ne $MetaCameraModel -and $MetaCameraModel.Count -gt 0) -or
            ($null -ne $MetaGPSLatitude -and $MetaGPSLatitude.Count -gt 0) -or
            ($null -ne $MetaGPSLongitude -and $MetaGPSLongitude.Count -gt 0) -or
            ($null -ne $MetaGPSAltitude -and $MetaGPSAltitude.Count -gt 0) -or
            ($null -ne $MetaExposureTime -and $MetaExposureTime.Count -gt 0) -or
            ($null -ne $MetaFNumber -and $MetaFNumber.Count -gt 0) -or
            ($null -ne $MetaISO -and $MetaISO.Count -gt 0) -or
            ($null -ne $MetaFocalLength -and $MetaFocalLength.Count -gt 0) -or
            ($null -ne $MetaWidth -and $MetaWidth.Count -gt 0) -or
            ($null -ne $MetaHeight -and $MetaHeight.Count -gt 0) -or
            ($null -ne $MetaDateTaken -and $MetaDateTaken.Count -gt 0) -or
            ($null -ne $GeoLocation -and $GeoLocation.Count -eq 2) -or
            ($PSBoundParameters.ContainsKey('MinConfidenceRatio'))
        )

        # if no selection criteria are specified, set PathLike to current directory wildcard
        if (-not $hasSelectionCriteria) {
            # get current directory for limiting results
            $currentDirExpanded = GenXdev.FileSystem\Expand-Path '.\'

            # search only current directory (non-recursive)
            $PathLike = @("$currentDirExpanded\*")
            $ImageDirectories = @($currentDirExpanded)
            Microsoft.PowerShell.Utility\Write-Verbose (
                "No selection criteria, searching current directory only: $currentDirExpanded"
            )
        }

        $done = @{}
    }
    ###############################################################################
    process {

        # handle append mode - output InputObject first, then process as normal
        if ($Append -and $null -ne $InputObject) {

            # first, output all InputObject content using Write-Output
            $InputObject | Microsoft.PowerShell.Utility\Write-Output
            $done."$(($InputObject.Path))" = $true;

            # then clear InputObject and continue processing as if it wasn't set
            $InputObject = $null
        }

        # handle input objects from pipeline
        if ($null -ne $InputObject) {

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

                # convert pipeline input to hashtable
                $item = $_ | GenXdev.Helpers\ConvertTo-HashTable

                # initialize results collection if needed
                if ($null -eq $results) {

                    $results = [System.Collections.Generic.List[Object]]::new()
                    $fromInput = $true
                }

                # handle collection items
                if ($item -is [System.Collections.IEnumerable] -and
                    (-not $InputObject.ContainsKey('Path'))) {

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

                        # convert nested items to hashtables
                        $secondItem = $_ |
                            GenXdev.Helpers\ConvertTo-HashTable

                        # add items with valid paths to results
                        if ($secondItem.ContainsKey('Path')) {

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

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

        # if we processed input objects, skip database search
        if ($fromInput) {
            return
        }

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

            # determine image path and handle base64 embedding if requested
            $imagePath = $DbResult.path

            # convert to base64 data url if embedding is enabled and data exists
            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()

                    # select appropriate mime type based on file extension
                    $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-Verbose (
                        'Failed to convert embedded image data to base64 for: ' +
                        "$($DbResult.path) - $($_.Exception.Message)"
                    )

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

            # build complete metadata object with exif data structure
            $metadataObject = @{
                Camera = @{
                    Make = $DbResult.camera_make
                    Model = $DbResult.camera_model
                }
                GPS = @{
                    Latitude = $DbResult.gps_latitude
                    Longitude = $DbResult.gps_longitude
                    Altitude = $DbResult.gps_altitude
                }
                Exposure = @{
                    ExposureTime = $DbResult.exposure_time
                    FNumber = $DbResult.f_number
                    ISOSpeedRatings = $DbResult.iso_speed
                    FocalLength = $DbResult.focal_length
                    Flash = $DbResult.flash
                }
                DateTime = @{
                    DateTimeOriginal = $DbResult.date_time_original
                    DateTimeDigitized = $DbResult.date_time_digitized
                }
                Basic = @{
                    BitsPerSample = $DbResult.bits_per_sample
                    Orientation = $DbResult.orientation
                    HorizontalResolution = $DbResult.x_resolution
                    VerticalResolution = $DbResult.y_resolution
                }
                Author = @{
                    Artist = $DbResult.artist
                    Copyright = $DbResult.copyright
                }
                Other = @{
                    Software = $DbResult.software
                    ColorSpace = $DbResult.color_space
                    ResolutionUnit = $DbResult.resolution_unit
                }
            }

            # Parse keywords from database
            $keywords = if ($DbResult.description_keywords) {
                try {
                    $keywordArray = $DbResult.description_keywords |
                        Microsoft.PowerShell.Utility\ConvertFrom-Json
                    [string[]]$keywordArray
                } catch {
                    [string[]]@()
                }
            } else {
                [string[]]@()
            }

            # Build people hashtable with proper structure
            $peopleData = if ($DbResult.people_json) {
                try {
                    $peopleObj = $DbResult.people_json |
                        Microsoft.PowerShell.Utility\ConvertFrom-Json

                    @{
                        count = if ($peopleObj.PSObject.Properties['count']) {
                            $peopleObj.count
                        } else {
                            $DbResult.people_count
                        }
                        faces = if ($peopleObj.PSObject.Properties['faces']) {
                            $peopleObj.faces
                        } elseif ($DbResult.people_faces) {
                            try {
                                $DbResult.people_faces |
                                    Microsoft.PowerShell.Utility\ConvertFrom-Json
                            } catch {
                                @()
                            }
                        } else {
                            @()
                        }
                        predictions = if ($peopleObj.PSObject.Properties['predictions']) {
                            $peopleObj.predictions
                        } else {
                            @()
                        }
                    }
                } catch {
                    @{
                        count = $DbResult.people_count
                        faces = if ($DbResult.people_faces) {
                            try {
                                $DbResult.people_faces |
                                    Microsoft.PowerShell.Utility\ConvertFrom-Json
                            } catch {
                                @()
                            }
                        } else { @() }
                        predictions = @()
                    }
                }
            } else {
                @{
                    count = $DbResult.people_count
                    faces = @()
                    predictions = @()
                }
            }

            # Build description hashtable
            $descriptionData = if ($DbResult.description_json) {
                try {
                    $descObj = $DbResult.description_json |
                        Microsoft.PowerShell.Utility\ConvertFrom-Json

                    $desc = @{
                        has_explicit_content = [bool]$DbResult.has_explicit_content
                        has_nudity = [bool]$DbResult.has_nudity
                        picture_type = if ($DbResult.picture_type) { $DbResult.picture_type } else { '' }
                        overall_mood_of_image = if ($DbResult.overall_mood_of_image) { $DbResult.overall_mood_of_image } else { '' }
                        style_type = if ($DbResult.style_type) { $DbResult.style_type } else { '' }
                        keywords = if ($DbResult.description_keywords) {
                            try {
                                $DbResult.description_keywords |
                                    Microsoft.PowerShell.Utility\ConvertFrom-Json
                            } catch {
                                @()
                            }
                        } else { @() }
                    }

                    # Add optional description content properties if available
                    if ($descObj.PSObject.Properties['description']) {
                        $desc['description'] = $descObj.description
                    }
                    if ($descObj.PSObject.Properties['short_description'] -or $DbResult.short_description) {
                        $desc['short_description'] = ($descObj.short_description ?? $DbResult.short_description)
                    }
                    if ($descObj.PSObject.Properties['long_description'] -or $DbResult.long_description) {
                        $desc['long_description'] = ($descObj.long_description ?? $DbResult.long_description)
                    }

                    $desc
                } catch {
                    @{
                        has_explicit_content = [bool]$DbResult.has_explicit_content
                        has_nudity = [bool]$DbResult.has_nudity
                        short_description = if ($DbResult.short_description) { $DbResult.short_description } else { '' }
                        long_description = if ($DbResult.long_description) { $DbResult.long_description } else { '' }
                        picture_type = if ($DbResult.picture_type) { $DbResult.picture_type } else { '' }
                        overall_mood_of_image = if ($DbResult.overall_mood_of_image) { $DbResult.overall_mood_of_image } else { '' }
                        style_type = if ($DbResult.style_type) { $DbResult.style_type } else { '' }
                        keywords = if ($DbResult.description_keywords) {
                            try {
                                $DbResult.description_keywords |
                                    Microsoft.PowerShell.Utility\ConvertFrom-Json
                            } catch {
                                @()
                            }
                        } else { @() }
                    }
                }
            } else {
                @{
                    has_explicit_content = [bool]$DbResult.has_explicit_content
                    has_nudity = [bool]$DbResult.has_nudity
                    short_description = if ($DbResult.short_description) { $DbResult.short_description } else { '' }
                    long_description = if ($DbResult.long_description) { $DbResult.long_description } else { '' }
                    picture_type = if ($DbResult.picture_type) { $DbResult.picture_type } else { '' }
                    overall_mood_of_image = if ($DbResult.overall_mood_of_image) { $DbResult.overall_mood_of_image } else { '' }
                    style_type = if ($DbResult.style_type) { $DbResult.style_type } else { '' }
                    keywords = if ($DbResult.description_keywords) {
                        try {
                            $DbResult.description_keywords |
                                Microsoft.PowerShell.Utility\ConvertFrom-Json
                        } catch {
                            @()
                        }
                    } else { @() }
                }
            }

            # Build scenes hashtable
            $scenesData = if ($DbResult.scenes_json) {
                try {
                    $scenesObj = $DbResult.scenes_json |
                        Microsoft.PowerShell.Utility\ConvertFrom-Json

                    @{
                        success = if ($scenesObj.PSObject.Properties['success']) {
                            $scenesObj.success
                        } else {
                            $true
                        }
                        scene = if ($scenesObj.PSObject.Properties['scene']) {
                            $scenesObj.scene
                        } else {
                            $DbResult.scene_label ?? 'unknown'
                        }
                        label = if ($scenesObj.PSObject.Properties['label']) {
                            $scenesObj.label
                        } else {
                            $DbResult.scene_label ?? $scenesObj.scene ?? 'unknown'
                        }
                        confidence = if ($scenesObj.PSObject.Properties['confidence']) {
                            $scenesObj.confidence
                        } else {
                            $DbResult.scene_confidence ?? 0.0
                        }
                        confidence_percentage = if ($scenesObj.PSObject.Properties['confidence_percentage']) {
                            $scenesObj.confidence_percentage
                        } elseif ($DbResult.scene_confidence_percentage) {
                            $DbResult.scene_confidence_percentage
                        } else {
                            ($DbResult.scene_confidence ?? 0.0) * 100
                        }
                        processed_at = if ($scenesObj.PSObject.Properties['processed_at']) {
                            $scenesObj.processed_at
                        } else {
                            $DbResult.scene_processed_at
                        }
                    }
                } catch {
                    @{
                        success = $true
                        scene = $DbResult.scene_label ?? 'unknown'
                        label = $DbResult.scene_label ?? 'unknown'
                        confidence = $DbResult.scene_confidence ?? 0.0
                        confidence_percentage = $DbResult.scene_confidence_percentage ?? (($DbResult.scene_confidence ?? 0.0) * 100)
                        processed_at = $DbResult.scene_processed_at
                    }
                }
            } else {
                @{
                    success = $true
                    scene = $DbResult.scene_label ?? 'unknown'
                    label = $DbResult.scene_label ?? 'unknown'
                    confidence = $DbResult.scene_confidence ?? 0.0
                    confidence_percentage = $DbResult.scene_confidence_percentage ?? (($DbResult.scene_confidence ?? 0.0) * 100)
                    processed_at = $DbResult.scene_processed_at
                }
            }

            # Build objects hashtable
            $objectsData = if ($DbResult.objects_json) {
                try {
                    $objectsObj = $DbResult.objects_json |
                        Microsoft.PowerShell.Utility\ConvertFrom-Json

                    @{
                        count = if ($objectsObj.PSObject.Properties['count']) {
                            $objectsObj.count
                        } else {
                            $DbResult.objects_count
                        }
                        objects = if ($objectsObj.PSObject.Properties['objects']) {
                            $objectsObj.objects
                        } elseif ($DbResult.objects_list) {
                            try {
                                $DbResult.objects_list |
                                    Microsoft.PowerShell.Utility\ConvertFrom-Json
                            } catch {
                                @()
                            }
                        } else {
                            @()
                        }
                        object_counts = if ($objectsObj.PSObject.Properties['object_counts']) {
                            $objectsObj.object_counts
                        } elseif ($DbResult.object_counts) {
                            try {
                                $DbResult.object_counts |
                                    Microsoft.PowerShell.Utility\ConvertFrom-Json
                            } catch {
                                @{}
                            }
                        } else {
                            @{}
                        }
                    }
                } catch {
                    @{
                        count = $DbResult.objects_count
                        objects = if ($DbResult.objects_list) {
                            try {
                                $DbResult.objects_list |
                                    Microsoft.PowerShell.Utility\ConvertFrom-Json
                            } catch {
                                @()
                            }
                        } else { @() }
                        object_counts = if ($DbResult.object_counts) {
                            try {
                                $DbResult.object_counts |
                                    Microsoft.PowerShell.Utility\ConvertFrom-Json
                            } catch {
                                @{}
                            }
                        } else { @{} }
                    }
                }
            } else {
                @{
                    count = $DbResult.objects_count
                    objects = if ($DbResult.objects_list) {
                        try {
                            $DbResult.objects_list |
                                Microsoft.PowerShell.Utility\ConvertFrom-Json
                        } catch {
                            @()
                        }
                    } else { @() }
                    object_counts = if ($DbResult.object_counts) {
                        try {
                            $DbResult.object_counts |
                                Microsoft.PowerShell.Utility\ConvertFrom-Json
                        } catch {
                            @{}
                        }
                    } else { @{} }
                }
            }

            # Create efficient .NET ImageSearchResult object with cached properties (no file system calls)
            # $result = [GenXdev.Helpers.ImageSearchResult]::CreateWithCachedProperties(
            # $imagePath,
            # $DbResult.width,
            # $DbResult.height,
            # [System.Collections.Hashtable]$peopleData,
            # [System.Collections.Hashtable]$objectsData,
            # [System.Collections.Hashtable]$metadataObject,
            # [System.Collections.Hashtable]$descriptionData,
            # [System.Collections.Hashtable]$scenesData,
            # $keywords,
            # $null, # fileSize - not available from database
            # $null # lastWriteTime - not available from database
            # )
            $result = @{
                Path              = $imagePath
                Width             = $DbResult.width
                Height            = $DbResult.height
                People            = $peopleData
                Objects           = $objectsData
                Metadata          = $metadataObject
                Description       = $descriptionData
                Scenes            = $scenesData
                Keywords          = $keywords
                FileSize          = $DbResult.file_size
                LastWriteTime     = $DbResult.last_write_time
            }

            # Apply proper type name and return formatted object
            # $psObject = [System.Management.Automation.PSObject]::AsPSObject($result)
            # $psObject.TypeNames.Insert(0, "GenXdev.Helpers.ImageSearchResult")
            return $result
        }

    # helper function to apply confidence filtering to image objects (matching Find-Image behavior)
    function Invoke-ConfidenceFiltering {
            param(
                [Parameter(Mandatory)]
                $ImageObject,
                [Parameter(Mandatory)]
                [double] $MinConfidenceRatio
            )

            $confidenceMatch = $null -ne $MinConfidenceRatio;

            # filter scenes by confidence - modify the scenes object directly
            if ($null -ne $confidenceMatch -and
                $null -ne $ImageObject.scenes -and
                $null -ne $ImageObject.scenes.confidence) {

                if ($ImageObject.scenes.confidence -ge $MinConfidenceRatio) {
                    $confidenceMatch = $true
                } else {
                    $confidenceMatch = $false
                    # filter out the scene data by setting it to default
                    $ImageObject.scenes = [PSCustomObject]@{
                        success = $false
                        scene = 'unknown'
                        label = 'unknown'
                        confidence = 0.0
                        confidence_percentage = 0.0
                        processed_at = $null
                    }
                }
            } else  { $confidenceMatch = $true }

            # filter people by confidence - remove people predictions below minimum threshold
            $confidenceMatch = $null -ne $MinConfidenceRatio;
            if ($null -ne $confidenceMatch -and
            $null -ne $ImageObject.people -and
            $null -ne $ImageObject.people.predictions -and
            $ImageObject.people.predictions.Count -gt 0) {

                $filteredPredictions = @()
                foreach ($prediction in $ImageObject.people.predictions) {
                    if ($null -ne $prediction.confidence -and $prediction.confidence -ge $MinConfidenceRatio) {
                        $filteredPredictions += $prediction
                        $confidenceMatch = $true
                    }
                }
                $ImageObject.people.predictions = $filteredPredictions
                $ImageObject.people.count = $filteredPredictions.Count

                # update faces array to match filtered predictions
                $ImageObject.people.faces = @($filteredPredictions | Microsoft.PowerShell.Core\ForEach-Object { $_.label })
            } else  { $confidenceMatch = $true }

            # filter objects by confidence - remove object predictions below minimum threshold
            $confidenceMatch = $null -ne $MinConfidenceRatio;
            if ($null -ne $confidenceMatch -and
                $null -ne $ImageObject.objects -and
                $null -ne $ImageObject.objects.objects -and
                $ImageObject.objects.objects.Count -gt 0) {

                $filteredObjects = @()
                $filteredCounts = @{}

                foreach ($obj in $ImageObject.objects.objects) {
                    if ($null -ne $obj.confidence -and $obj.confidence -ge $MinConfidenceRatio) {
                        $filteredObjects += $obj
                        $confidenceMatch = $true

                        # update object counts for filtered objects
                        if ($filteredCounts.ContainsKey($obj.label)) {
                            $filteredCounts[$obj.label]++
                        } else {
                            $filteredCounts[$obj.label] = 1
                        }
                    }
                }

                $ImageObject.objects.objects = $filteredObjects
                $ImageObject.objects.count = $filteredObjects.Count
                $ImageObject.objects.object_counts = $filteredCounts
            }

            # return whether any confidence match was found
            return $confidenceMatch
        }

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

        # retrieve the database path
        $DatabaseFilePath = GenXdev.AI\Get-ImageIndexPath @params

        # validate database path exists
        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"
        )

        function ConvertTo-SqliteLikePattern {
            param(
                [string]$Pattern,
                [switch]$ForceWildcards
            )
            $escaped = $Pattern

            # Add wildcards if requested and none exist
            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('_', '[_]')

            # Convert * to % for zero or more characters
            $escaped = $escaped.Replace('*', '%')

            # Convert ? to _ for exactly one character
            $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 without leading wildcards
                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 leading wildcards
                    $condition = (
                        "$TableAlias.$ColumnName LIKE @$ParamName COLLATE NOCASE"
                    )
                    $Parameters[$ParamName] = $pattern
                }
            } else {
                # exact match - most efficient using 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
        if ($DescriptionSearch -and $DescriptionSearch.Count -gt 0) {

            $descriptionConditions = @()

            # build conditions for each description search term
            foreach ($description in $DescriptionSearch) {
                $paramName = "Description$paramCounter"

                # search in short description field
                $condition = Get-OptimizedSearchCondition `
                    -ColumnName 'short_description' `
                    -TableAlias 'i' `
                    -SearchTerm $description `
                    -ParamName $paramName `
                    -Parameters $parameters
                $descriptionConditions += $condition
                $paramCounter++

                $paramName = "Description$paramCounter"

                # search in long description field
                $condition = Get-OptimizedSearchCondition `
                    -ColumnName 'long_description' `
                    -TableAlias 'i' `
                    -SearchTerm $description `
                    -ParamName $paramName `
                    -Parameters $parameters
                $descriptionConditions += $condition
                $paramCounter++
            }

            # combine description conditions with or logic
            $whereClauses += ('(' + ($descriptionConditions -join ' OR ') + ')')
        }

        # add keyword search with optimized indexed lookup
        if ($Keywords -and $Keywords.Count -gt 0) {
            $keywordConditions = @()

            # build conditions for each keyword search term
            foreach ($keyword in $Keywords) {
                $paramName = "keyword$paramCounter"

                # search using exists subquery for performance
                $condition = Get-OptimizedSearchCondition `
                    -ColumnName 'keyword' `
                    -TableAlias 'ik' `
                    -SearchTerm $keyword `
                    -ParamName $paramName `
                    -Parameters $parameters
                $keywordConditions += $condition
                $paramCounter++
            }

            # add exists clause to avoid table scans
            $whereClauses += (
                '(EXISTS (SELECT * FROM ImageKeywords ik WHERE ' +
                'ik.image_id = i.id AND (' +
                ($keywordConditions -join ' OR ') + ')))'
            )
        }

        # add people search with optimized indexed lookup
        if ($People -and $People.Count -gt 0) {
            $peopleConditions = @()

            # build conditions for each person search term
            foreach ($person in $People) {
                $paramName = "person$paramCounter"

                # search using optimized condition for person names
                $condition = Get-OptimizedSearchCondition `
                    -ColumnName 'person_name' `
                    -TableAlias 'ip' `
                    -SearchTerm $person `
                    -ParamName $paramName `
                    -Parameters $parameters
                $peopleConditions += $condition
                $paramCounter++
            }

            # add exists clause to avoid table scans
            $whereClauses += (
                '(EXISTS (SELECT * FROM ImagePeople ip WHERE ' +
                'ip.image_id = i.id AND (' +
                ($peopleConditions -join ' OR ') + ')))'
            )
        }

        # add objects search with optimized indexed lookup
        if ($Objects -and $Objects.Count -gt 0) {
            $objectConditions = @()

            # build conditions for each object search term
            foreach ($obj in $Objects) {
                $paramName = "object$paramCounter"

                # search using optimized condition for object names
                $condition = Get-OptimizedSearchCondition `
                    -ColumnName 'object_name' `
                    -TableAlias 'io' `
                    -SearchTerm $obj `
                    -ParamName $paramName `
                    -Parameters $parameters
                $objectConditions += $condition
                $paramCounter++
            }

            # add exists clause to avoid table scans
            $whereClauses += (
                '(EXISTS (SELECT * FROM ImageObjects io WHERE ' +
                'io.image_id = i.id AND (' +
                ($objectConditions -join ' OR ') + ')))'
            )
        }

        # add scenes search with optimized indexed lookup
        if ($Scenes -and $Scenes.Count -gt 0) {
            $sceneConditions = @()

            # build conditions for each scene search term
            foreach ($scene in $Scenes) {
                $paramName = "scene$paramCounter"

                # search using optimized condition for scene names
                $condition = Get-OptimizedSearchCondition `
                    -ColumnName 'scene_name' `
                    -TableAlias 'isc' `
                    -SearchTerm $scene `
                    -ParamName $paramName `
                    -Parameters $parameters
                $sceneConditions += $condition
                $paramCounter++
            }

            # add exists clause to avoid table scans
            $whereClauses += (
                '(EXISTS (SELECT * FROM ImageScenes isc WHERE ' +
                'isc.image_id = i.id AND (' +
                ($sceneConditions -join ' OR ') + ')))'
            )
        }

        # add picture type filter with optimized indexed column
        if ($PictureType -and $PictureType.Count -gt 0) {
            $pictureTypeConditions = @()

            # build conditions for each picture type
            foreach ($type in $PictureType) {
                $paramName = "pictype$paramCounter"

                # search using optimized condition for picture types
                $condition = Get-OptimizedSearchCondition `
                    -ColumnName 'picture_type' `
                    -TableAlias 'i' `
                    -SearchTerm $type `
                    -ParamName $paramName `
                    -Parameters $parameters
                $pictureTypeConditions += $condition
                $paramCounter++
            }

            # combine picture type conditions with or logic
            $whereClauses += ('(' + ($pictureTypeConditions -join ' OR ') + ')')
        }

        # add style type filter with optimized indexed column
        if ($StyleType -and $StyleType.Count -gt 0) {
            $styleTypeConditions = @()

            # build conditions for each style type
            foreach ($style in $StyleType) {
                $paramName = "styletype$paramCounter"

                # search using optimized condition for style types
                $condition = Get-OptimizedSearchCondition `
                    -ColumnName 'style_type' `
                    -TableAlias 'i' `
                    -SearchTerm $style `
                    -ParamName $paramName `
                    -Parameters $parameters
                $styleTypeConditions += $condition
                $paramCounter++
            }

            # combine style type conditions with or logic
            $whereClauses += ('(' + ($styleTypeConditions -join ' OR ') + ')')
        }

        # add mood filter with optimized indexed column
        if ($OverallMood -and $OverallMood.Count -gt 0) {
            $moodConditions = @()

            # build conditions for each mood
            foreach ($mood in $OverallMood) {
                $paramName = "mood$paramCounter"

                # search using optimized condition for moods
                $condition = Get-OptimizedSearchCondition `
                    -ColumnName 'overall_mood_of_image' `
                    -TableAlias 'i' `
                    -SearchTerm $mood `
                    -ParamName $paramName `
                    -Parameters $parameters
                $moodConditions += $condition
                $paramCounter++
            }

            # combine mood conditions with or logic
            $whereClauses += ('(' + ($moodConditions -join ' OR ') + ')')
        }

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

        # add no nudity filter
        if ($NoNudity) {
            $whereClauses += 'i.has_nudity = 0'
        }

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

        # add no explicit content filter
        if ($NoExplicitContent) {
            $whereClauses += 'i.has_explicit_content = 0'
        }

        # add path-like search with optimized like lookup
        if ($PathLike -and $PathLike.Count -gt 0) {
            $pathLikeConditions = @()

            # process each path pattern
            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
                }

                # expand the path pattern
                $filter = GenXdev.FileSystem\Expand-Path $localPath

                # convert to sqlite like pattern
                $sqlitePattern = ConvertTo-SqliteLikePattern `
                    -Pattern $filter `
                    -ForceWildcards

                # add path condition
                $pathLikeConditions += "i.path LIKE @$paramName COLLATE NOCASE"
                $parameters[$paramName] = $sqlitePattern
                $paramCounter++
            }

            # combine path conditions with or logic
            $whereClauses += ('(' + ($pathLikeConditions -join ' OR ') + ')')
        }

        # add camera make filter
        if ($MetaCameraMake -and $MetaCameraMake.Count -gt 0) {
            $cameraMakeConditions = @()

            # build conditions for each camera make
            foreach ($make in $MetaCameraMake) {
                $paramName = "cameraMake$paramCounter"

                # search using optimized condition with wildcard support
                $condition = Get-OptimizedSearchCondition `
                    -ColumnName 'camera_make' `
                    -TableAlias 'i' `
                    -SearchTerm $make `
                    -ParamName $paramName `
                    -Parameters $parameters `
                    -ForceWildcards
                $cameraMakeConditions += $condition
                $paramCounter++
            }

            # combine camera make conditions with or logic
            $whereClauses += ('(' + ($cameraMakeConditions -join ' OR ') + ')')
        }

        # add camera model filter
        if ($MetaCameraModel -and $MetaCameraModel.Count -gt 0) {
            $cameraModelConditions = @()

            # build conditions for each camera model
            foreach ($model in $MetaCameraModel) {
                $paramName = "cameraModel$paramCounter"

                # search using optimized condition with wildcard support
                $condition = Get-OptimizedSearchCondition `
                    -ColumnName 'camera_model' `
                    -TableAlias 'i' `
                    -SearchTerm $model `
                    -ParamName $paramName `
                    -Parameters $parameters `
                    -ForceWildcards
                $cameraModelConditions += $condition
                $paramCounter++
            }

            # combine camera model conditions with or logic
            $whereClauses += ('(' + ($cameraModelConditions -join ' OR ') + ')')
        }

        # add gps latitude range filter
        if ($MetaGPSLatitude -and $MetaGPSLatitude.Count -gt 0) {
            if ($MetaGPSLatitude.Count -eq 1) {
                # apply exact match for single latitude value
                $paramName = "gpsLatitude$paramCounter"
                $whereClauses += "(i.gps_latitude IS NOT NULL AND i.gps_latitude = @$paramName)"
                $parameters[$paramName] = $MetaGPSLatitude[0]
                $paramCounter++
            } else {
                # apply range match for latitude values
                $minLat = [Math]::Min($MetaGPSLatitude[0], $MetaGPSLatitude[1])
                $maxLat = [Math]::Max($MetaGPSLatitude[0], $MetaGPSLatitude[1])

                $paramNameMin = "gpsLatitudeMin$paramCounter"
                $paramNameMax = "gpsLatitudeMax$paramCounter"

                $whereClauses += ("(i.gps_latitude IS NOT NULL AND i.gps_latitude BETWEEN @$paramNameMin " +
                    "AND @$paramNameMax)")
                $parameters[$paramNameMin] = $minLat
                $parameters[$paramNameMax] = $maxLat
                $paramCounter += 2
            }
        }

        # add gps longitude range filter
        if ($MetaGPSLongitude -and $MetaGPSLongitude.Count -gt 0) {
            if ($MetaGPSLongitude.Count -eq 1) {
                # apply exact match for single longitude value
                $paramName = "gpsLongitude$paramCounter"
                $whereClauses += "(i.gps_longitude IS NOT NULL AND i.gps_longitude = @$paramName)"
                $parameters[$paramName] = $MetaGPSLongitude[0]
                $paramCounter++
            } else {
                # apply range match for longitude values
                $minLong = [Math]::Min($MetaGPSLongitude[0],
                    $MetaGPSLongitude[1])
                $maxLong = [Math]::Max($MetaGPSLongitude[0],
                    $MetaGPSLongitude[1])

                $paramNameMin = "gpsLongitudeMin$paramCounter"
                $paramNameMax = "gpsLongitudeMax$paramCounter"

                $whereClauses += ("(i.gps_longitude IS NOT NULL AND i.gps_longitude BETWEEN @$paramNameMin " +
                    "AND @$paramNameMax)")
                $parameters[$paramNameMin] = $minLong
                $parameters[$paramNameMax] = $maxLong
                $paramCounter += 2
            }
        }

        # add gps altitude range filter
        if ($MetaGPSAltitude -and $MetaGPSAltitude.Count -gt 0) {
            if ($MetaGPSAltitude.Count -eq 1) {
                # apply exact match for single altitude value
                $paramName = "gpsAltitude$paramCounter"
                $whereClauses += "(i.gps_altitude IS NOT NULL AND i.gps_altitude = @$paramName)"
                $parameters[$paramName] = $MetaGPSAltitude[0]
                $paramCounter++
            } else {
                # apply range match for altitude values
                $minAlt = [Math]::Min($MetaGPSAltitude[0], $MetaGPSAltitude[1])
                $maxAlt = [Math]::Max($MetaGPSAltitude[0], $MetaGPSAltitude[1])

                $paramNameMin = "gpsAltitudeMin$paramCounter"
                $paramNameMax = "gpsAltitudeMax$paramCounter"

                $whereClauses += ("(i.gps_altitude IS NOT NULL AND i.gps_altitude BETWEEN @$paramNameMin " +
                    "AND @$paramNameMax)")
                $parameters[$paramNameMin] = $minAlt
                $parameters[$paramNameMax] = $maxAlt
                $paramCounter += 2
            }
        }

        # Exposure Time range filter
        if ($MetaExposureTime -and $MetaExposureTime.Count -gt 0) {
            if ($MetaExposureTime.Count -eq 1) {
                # Exact match
                $paramName = "exposureTime$paramCounter"
                $whereClauses += "(i.exposure_time IS NOT NULL AND i.exposure_time = @$paramName)"
                $parameters[$paramName] = $MetaExposureTime[0]
                $paramCounter++
            } else {
                # Range match
                $minExp = [Math]::Min($MetaExposureTime[0], $MetaExposureTime[1])
                $maxExp = [Math]::Max($MetaExposureTime[0], $MetaExposureTime[1])

                $paramNameMin = "exposureTimeMin$paramCounter"
                $paramNameMax = "exposureTimeMax$paramCounter"

                $whereClauses += "(i.exposure_time IS NOT NULL AND i.exposure_time BETWEEN @$paramNameMin AND @$paramNameMax)"
                $parameters[$paramNameMin] = $minExp
                $parameters[$paramNameMax] = $maxExp
                $paramCounter += 2
            }
        }

        # F-Number (aperture) range filter
        if ($MetaFNumber -and $MetaFNumber.Count -gt 0) {
            if ($MetaFNumber.Count -eq 1) {
                # Exact match
                $paramName = "fNumber$paramCounter"
                $whereClauses += "(i.f_number IS NOT NULL AND i.f_number = @$paramName)"
                $parameters[$paramName] = $MetaFNumber[0]
                $paramCounter++
            } else {
                # Range match
                $minF = [Math]::Min($MetaFNumber[0], $MetaFNumber[1])
                $maxF = [Math]::Max($MetaFNumber[0], $MetaFNumber[1])

                $paramNameMin = "fNumberMin$paramCounter"
                $paramNameMax = "fNumberMax$paramCounter"

                $whereClauses += "(i.f_number IS NOT NULL AND i.f_number BETWEEN @$paramNameMin AND @$paramNameMax)"
                $parameters[$paramNameMin] = $minF
                $parameters[$paramNameMax] = $maxF
                $paramCounter += 2
            }
        }

        # ISO Speed range filter
        if ($MetaISO -and $MetaISO.Count -gt 0) {
            if ($MetaISO.Count -eq 1) {
                # Exact match
                $paramName = "iso$paramCounter"
                $whereClauses += "(i.iso_speed IS NOT NULL AND i.iso_speed = @$paramName)"
                $parameters[$paramName] = $MetaISO[0]
                $paramCounter++
            } else {
                # Range match
                $minISO = [Math]::Min($MetaISO[0], $MetaISO[1])
                $maxISO = [Math]::Max($MetaISO[0], $MetaISO[1])

                $paramNameMin = "isoMin$paramCounter"
                $paramNameMax = "isoMax$paramCounter"

                $whereClauses += "(i.iso_speed IS NOT NULL AND i.iso_speed BETWEEN @$paramNameMin AND @$paramNameMax)"
                $parameters[$paramNameMin] = $minISO
                $parameters[$paramNameMax] = $maxISO
                $paramCounter += 2
            }
        }

        # Focal Length range filter
        if ($MetaFocalLength -and $MetaFocalLength.Count -gt 0) {
            if ($MetaFocalLength.Count -eq 1) {
                # Exact match
                $paramName = "focalLength$paramCounter"
                $whereClauses += "(i.focal_length IS NOT NULL AND i.focal_length = @$paramName)"
                $parameters[$paramName] = $MetaFocalLength[0]
                $paramCounter++
            } else {
                # Range match
                $minFL = [Math]::Min($MetaFocalLength[0], $MetaFocalLength[1])
                $maxFL = [Math]::Max($MetaFocalLength[0], $MetaFocalLength[1])

                $paramNameMin = "focalLengthMin$paramCounter"
                $paramNameMax = "focalLengthMax$paramCounter"

                $whereClauses += "(i.focal_length IS NOT NULL AND i.focal_length BETWEEN @$paramNameMin AND @$paramNameMax)"
                $parameters[$paramNameMin] = $minFL
                $parameters[$paramNameMax] = $maxFL
                $paramCounter += 2
            }
        }

        # Image Width range filter
        if ($MetaWidth -and $MetaWidth.Count -gt 0) {
            if ($MetaWidth.Count -eq 1) {
                # Exact match
                $paramName = "width$paramCounter"
                $whereClauses += "i.width = @$paramName"
                $parameters[$paramName] = $MetaWidth[0]
                $paramCounter++
            } else {
                # Range match
                $minWidth = [Math]::Min($MetaWidth[0], $MetaWidth[1])
                $maxWidth = [Math]::Max($MetaWidth[0], $MetaWidth[1])

                $paramNameMin = "widthMin$paramCounter"
                $paramNameMax = "widthMax$paramCounter"

                $whereClauses += "i.width BETWEEN @$paramNameMin AND @$paramNameMax"
                $parameters[$paramNameMin] = $minWidth
                $parameters[$paramNameMax] = $maxWidth
                $paramCounter += 2
            }
        }

        # Image Height range filter
        if ($MetaHeight -and $MetaHeight.Count -gt 0) {
            if ($MetaHeight.Count -eq 1) {
                # Exact match
                $paramName = "height$paramCounter"
                $whereClauses += "i.height = @$paramName"
                $parameters[$paramName] = $MetaHeight[0]
                $paramCounter++
            } else {
                # Range match
                $minHeight = [Math]::Min($MetaHeight[0], $MetaHeight[1])
                $maxHeight = [Math]::Max($MetaHeight[0], $MetaHeight[1])

                $paramNameMin = "heightMin$paramCounter"
                $paramNameMax = "heightMax$paramCounter"

                $whereClauses += "i.height BETWEEN @$paramNameMin AND @$paramNameMax"
                $parameters[$paramNameMin] = $minHeight
                $parameters[$paramNameMax] = $maxHeight
                $paramCounter += 2
            }
        }

        # Date Taken range filter
        if ($MetaDateTaken -and $MetaDateTaken.Count -gt 0) {
            if ($MetaDateTaken.Count -eq 1) {
                # Match for single day - use date part only
                $dateTaken = $MetaDateTaken[0].Date
                $nextDay = $dateTaken.AddDays(1)

                $paramNameStart = "dateTakenStart$paramCounter"
                $paramNameEnd = "dateTakenEnd$paramCounter"

                $whereClauses += "(i.date_taken IS NOT NULL AND i.date_taken BETWEEN @$paramNameStart AND @$paramNameEnd)"
                $parameters[$paramNameStart] = $dateTaken.ToString('yyyy-MM-dd')
                $parameters[$paramNameEnd] = $nextDay.ToString('yyyy-MM-dd')
                $paramCounter += 2
            } else {
                # Range match
                $startDate = $MetaDateTaken[0]
                $endDate = $MetaDateTaken[1]

                # Ensure proper order
                if ($startDate -gt $endDate) {
                    $temp = $startDate
                    $startDate = $endDate
                    $endDate = $temp
                }

                # Include full end day by adding 1 day to end date
                $endDatePlusOneDay = $endDate.AddDays(1)

                $paramNameStart = "dateTakenStart$paramCounter"
                $paramNameEnd = "dateTakenEnd$paramCounter"

                $whereClauses += "(i.date_taken IS NOT NULL AND i.date_taken BETWEEN @$paramNameStart AND @$paramNameEnd)"
                $parameters[$paramNameStart] = $startDate.ToString('yyyy-MM-dd')
                $parameters[$paramNameEnd] = $endDatePlusOneDay.ToString('yyyy-MM-dd')
                $paramCounter += 2
            }
        }

        # Geo Location search with distance calculation
        if ($GeoLocation -and $GeoLocation.Count -eq 2) {
            $lat = $GeoLocation[0]
            $lon = $GeoLocation[1]
            $dist = $GeoDistanceInMeters / 1000 # Convert to kilometers for the calculation

            # Optimized SQL implementation of Haversine formula with spatial indexing support
            # This version uses trigonometric pre-calculations and index-friendly range filtering
            $latRad = $lat * [Math]::PI / 180
            $distKm = $dist / 1000  # Convert meters to kilometers

            # Pre-calculate latitude range for initial filtering
            $latRange = $distKm / 111.12  # 1 degree latitude ≈ 111.12 km
            $minLat = $lat - $latRange
            $maxLat = $lat + $latRange

            # Pre-calculate longitude range (adjusts for latitude)
            $lonRange = $distKm / (111.12 * [Math]::Cos($latRad))
            $minLon = $lon - $lonRange
            $maxLon = $lon + $lonRange

            # First filter by lat/lon ranges, then apply exact Haversine calculation
            $whereClauses += @"
(
    i.gps_latitude IS NOT NULL AND
    i.gps_longitude IS NOT NULL AND
    i.gps_latitude BETWEEN @minLat AND @maxLat AND
    i.gps_longitude BETWEEN @minLon AND @maxLon AND
    (6371.0 * 2 * asin(sqrt(
        power(sin((@geoLat - abs(i.gps_latitude)) * pi()/180/2), 2) +
        cos(@geoLat * pi()/180) * cos(abs(i.gps_latitude) * pi()/180) *
        power(sin((@geoLon - i.gps_longitude) * pi()/180/2), 2)
    ))) <= @geoMaxDist
)
"@

            # Add all parameters for the query
            $parameters["geoLat"] = $lat
            $parameters["geoLon"] = $lon
            $parameters["geoMaxDist"] = $distKm
            $parameters["minLat"] = $minLat
            $parameters["maxLat"] = $maxLat
            $parameters["minLon"] = $minLon
            $parameters["maxLon"] = $maxLon
        }

        # Handle -Any parameter: create OR conditions for all metadata types
        if ($null -ne $Any -and $Any.Length -gt 0) {
            # Add wildcards to any terms that don't already have them
            $processedAnyTerms = @($Any | Microsoft.PowerShell.Core\ForEach-Object {
                $entry = $_.Trim()
                if ($entry.IndexOfAny([char[]]@('*', '?')) -lt 0) {
                    "*$entry*"
                } else {
                    $_
                }
            })

            $anyConditions = @()

            # Build OR conditions for each Any term against all metadata types
            foreach ($anyTerm in $processedAnyTerms) {
                $termConditions = @()

                # Escape the term for SQL LIKE pattern
                $escapedTerm = $anyTerm -replace "'", "''" -replace '%', '\%' -replace '_', '\_'
                $likeTerm = $escapedTerm -replace '\*', '%' -replace '\?', '_'

                # File path condition (known to exist)
                $termConditions += "i.path LIKE '%$likeTerm%'"

                # Description search conditions (known to exist)
                $termConditions += "i.long_description LIKE '%$likeTerm%'"
                $termConditions += "i.short_description LIKE '%$likeTerm%'"

                # EXIF metadata conditions (known to exist)
                $termConditions += "i.picture_type LIKE '%$likeTerm%'"
                $termConditions += "i.style_type LIKE '%$likeTerm%'"
                $termConditions += "i.overall_mood_of_image LIKE '%$likeTerm%'"
                $termConditions += "i.has_nudity LIKE '%$likeTerm%'"
                $termConditions += "i.has_explicit_content LIKE '%$likeTerm%'"

                # Keyword conditions
                $termConditions += "EXISTS (SELECT 1 FROM ImageKeywords ik WHERE ik.image_id = i.id AND ik.keyword LIKE '%$likeTerm%')"

                # People conditions
                $termConditions += "EXISTS (SELECT 1 FROM ImagePeople ip WHERE ip.image_id = i.id AND ip.person_name LIKE '%$likeTerm%')"

                # Objects conditions
                $termConditions += "EXISTS (SELECT 1 FROM ImageObjects io WHERE io.image_id = i.id AND io.object_name LIKE '%$likeTerm%')"

                # Scenes conditions
                $termConditions += "EXISTS (SELECT 1 FROM ImageScenes isc WHERE isc.image_id = i.id AND isc.scene_name LIKE '%$likeTerm%')"

                # Combine all conditions for this term with OR
                $anyConditions += '(' + ($termConditions -join ' OR ') + ')'
            }

            # Add the Any clause to WHERE conditions (OR within Any terms, AND with other filters)
            if ($anyConditions.Count -gt 0) {
                $whereClauses += '(' + ($anyConditions -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) {
            # Combine main search clauses with OR (for descriptions, keywords, etc.)
            # But combine metadata filters with AND for precise filtering
            $sqlQuery += ' WHERE ' + ($whereClauses -join ' AND ')
        }

        # 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

                # apply confidence filtering only if MinConfidenceRatio is explicitly specified
                $includeImage = -not $done."$(($imageObj.Path))";
                $done."$(($imageObj.Path))" = $true;

                if ($PSBoundParameters.ContainsKey('MinConfidenceRatio') -and $null -ne $MinConfidenceRatio) {
                    $confidenceMatch = Invoke-ConfidenceFiltering -ImageObject $imageObj -MinConfidenceRatio $MinConfidenceRatio
                    if (-not $confidenceMatch) {
                        $includeImage = $false
                    }
                }

                if ($includeImage) {
                    $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 {
                    $imageObj = ConvertTo-ImageObject -DbResult $_ -EmbedImages:$EmbedImages

                    # apply confidence filtering only if MinConfidenceRatio is explicitly specified
                    $includeImage = -not $done."$(($imageObj.Path))";
                    $done."$(($imageObj.Path))" = $true;

                    if ($PSBoundParameters.ContainsKey('MinConfidenceRatio') -and $null -ne $MinConfidenceRatio) {
                        $confidenceMatch = Invoke-ConfidenceFiltering -ImageObject $imageObj -MinConfidenceRatio $MinConfidenceRatio
                        if (-not $confidenceMatch) {
                            $includeImage = $false
                        }
                    }

                    if ($includeImage) {
                        # Add to results collection - don't convert here for performance
                        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 $($info.resultCount) " +
                'results (no table scans)'
            )
        }
    }

    ###############################################################################
    end {

        # handle input object processing from pipeline
        if (($null -ne $results -and $fromInput) -or ($filenames.Count -gt 0)) {

            # 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
            )

            # pass results as input object
            $params.InputObject = $results

            # delegate to find-image for pipeline input processing
            GenXdev.AI\Find-Image @params

            return
        }

        # provide appropriate message if no results were found
        if ($Info.resultCount -eq 0) {
            $searchCriteria = [System.Collections.Generic.List[string]]::new()

            # collect search criteria for user feedback
            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 ', ')")
            }

            # add exif metadata search criteria to user feedback
            if ($MetaCameraMake -and $MetaCameraMake.Count -gt 0) {
                $searchCriteria.Add("camera make: $($MetaCameraMake -join ', ')")
            }
            if ($MetaCameraModel -and $MetaCameraModel.Count -gt 0) {
                $searchCriteria.Add("camera model: $($MetaCameraModel -join ', ')")
            }
            if ($MetaGPSLatitude -and $MetaGPSLatitude.Count -gt 0) {
                $searchCriteria.Add("GPS latitude: $($MetaGPSLatitude -join ' to ')")
            }
            if ($MetaGPSLongitude -and $MetaGPSLongitude.Count -gt 0) {
                $searchCriteria.Add("GPS longitude: $($MetaGPSLongitude -join ' to ')")
            }
            if ($MetaGPSAltitude -and $MetaGPSAltitude.Count -gt 0) {
                $searchCriteria.Add("GPS altitude: $($MetaGPSAltitude -join ' to ')")
            }
            if ($MetaExposureTime -and $MetaExposureTime.Count -gt 0) {
                $searchCriteria.Add("exposure time: $($MetaExposureTime -join ' to ')")
            }
            if ($MetaFNumber -and $MetaFNumber.Count -gt 0) {
                $searchCriteria.Add("F-number: $($MetaFNumber -join ' to ')")
            }
            if ($MetaISO -and $MetaISO.Count -gt 0) {
                $searchCriteria.Add("ISO: $($MetaISO -join ' to ')")
            }
            if ($MetaFocalLength -and $MetaFocalLength.Count -gt 0) {
                $searchCriteria.Add("focal length: $($MetaFocalLength -join ' to ')")
            }
            if ($MetaWidth -and $MetaWidth.Count -gt 0) {
                $searchCriteria.Add("width: $($MetaWidth -join ' to ')")
            }
            if ($MetaHeight -and $MetaHeight.Count -gt 0) {
                $searchCriteria.Add("height: $($MetaHeight -join ' to ')")
            }
            if ($MetaDateTaken -and $MetaDateTaken.Count -gt 0) {
                $searchCriteria.Add("date taken: $($MetaDateTaken -join ' to ')")
            }
            if ($GeoLocation -and $GeoLocation.Count -eq 2) {
                $searchCriteria.Add(
                    "near location: $($GeoLocation[0]),$($GeoLocation[1]) " +
                    "within ${GeoDistanceInMeters}m"
                )
            }

            # display appropriate no results message
            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
            }
        }

        # display results in browser gallery if requested
        if ($ShowInBrowser) {

            # set default title if not provided
            if ([String]::IsNullOrWhiteSpace($Title)) {
                $Title = '🚀 Fast Indexed Image Search Results'
            }

            # set default description with performance information
            if ([String]::IsNullOrWhiteSpace($Description)) {
                $searchInfo = 'Database search completed in ' +
                    "$($queryTime.TotalMilliseconds)ms | " +
                    "Found $($results.Count) images"
                $Description = "$($MyInvocation.Statement) | $searchInfo"
            }

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

            # pass results to gallery display function
            $galleryParams.InputObject = $results

            # show the results in browser gallery
            GenXdev.AI\Show-FoundImagesInBrowser @galleryParams

            # return results if passthru is requested - objects already properly formatted
            if ($PassThru) {
                Microsoft.PowerShell.Utility\Write-Output $results
            }
        }
    }
}
###############################################################################