HEX
Server: Microsoft-IIS/10.0
System: Windows NT ITPWINWEBSVR22 10.0 build 20348 (Windows Server 2022) AMD64
User: www.conferencesearch.co.uk (0)
PHP: 8.3.30
Disabled: NONE
Upload Files
File: D:/web/hyperflight/apps/search-log-analysis/index.asp
<%
Option Explicit
Response.Buffer = True
Response.CodePage = 65001
Response.CharSet = "utf-8"

' ======================================================================
' Search Log Analysis
' Bootstrap 5 / Classic ASP / MySQL
' ResultCount reporting added 24 July 2026
' ======================================================================

Const adCmdText = 1
Const adParamInput = 1
Const adVarChar = 200

Dim conn, cmd
Dim rsSummary, rsSources, rsDaily, rsTerms, rsNoResults, rsProducts, rsVisitors, rsRecent
Dim startDate, endDate, sourceFilter, resultStatus, keyword, topLimit, recentLimit
Dim whereSql, sourceSql, resultSql, keywordSql
Dim totalSearches, uniqueSessions, uniqueTerms
Dim incrementalSearches, productSelections, distinctProducts, selectionRate
Dim resultRecordedCount, noResultSearches, withResultSearches, noResultRate, averageResultCount
Dim groupRecordedCount, groupNoResultCount, groupNoResultRate
Dim dateRangeDays, validationMessage
Dim sourceName, sourceLabel, sourceBadge, sourceCount, sourceSessions, sourceSelections
Dim sourcePercent, dayValue, searchTerm, productCode, productName
Dim searchCount, sessionCount, selectedCount, lastUsed, visitorIP
Dim eventID, sessionID, searchSource, lastUpdated, resultValue
Dim dateLabel, rangeLabel, noResultPosition

Function IsoDate(ByVal value)
    IsoDate = Year(value) & "-" & Right("0" & Month(value), 2) & "-" & Right("0" & Day(value), 2)
End Function

Function IsValidIsoDate(ByVal value)
    Dim parts, testDate
    IsValidIsoDate = False
    If Len(value) <> 10 Then Exit Function
    parts = Split(value, "-")
    If UBound(parts) <> 2 Then Exit Function
    If Not IsNumeric(parts(0)) Or Not IsNumeric(parts(1)) Or Not IsNumeric(parts(2)) Then Exit Function

    On Error Resume Next
    testDate = DateSerial(CInt(parts(0)), CInt(parts(1)), CInt(parts(2)))
    If Err.Number = 0 Then
        If Year(testDate) = CInt(parts(0)) And Month(testDate) = CInt(parts(1)) And Day(testDate) = CInt(parts(2)) Then
            IsValidIsoDate = True
        End If
    End If
    Err.Clear
    On Error GoTo 0
End Function

Function ParseIsoDate(ByVal value)
    Dim parts
    parts = Split(value, "-")
    ParseIsoDate = DateSerial(CInt(parts(0)), CInt(parts(1)), CInt(parts(2)))
End Function

Function Html(ByVal value)
    If IsNull(value) Then
        Html = ""
    Else
        Html = Server.HTMLEncode(CStr(value))
    End If
End Function

Function Number0(ByVal value)
    If IsNull(value) Or value = "" Then
        Number0 = "0"
    Else
        Number0 = FormatNumber(CDbl(value), 0)
    End If
End Function

Function SourceDescription(ByVal value)
    Select Case LCase(value)
        Case "button"
            SourceDescription = "Search button"
        Case "incremental"
            SourceDescription = "Incremental selection"
        Case "url"
            SourceDescription = "URL/query-string"
        Case Else
            SourceDescription = "Legacy"
    End Select
End Function

Function SourceBadgeClass(ByVal value)
    Select Case LCase(value)
        Case "button"
            SourceBadgeClass = "text-bg-primary"
        Case "incremental"
            SourceBadgeClass = "text-bg-success"
        Case "url"
            SourceBadgeClass = "text-bg-warning"
        Case Else
            SourceBadgeClass = "text-bg-secondary"
    End Select
End Function

Function ResultCountBadge(ByVal value)
    If IsNull(value) Then
        ResultCountBadge = "<span class=""text-muted"">&mdash;</span>"
    ElseIf CLng(value) = 0 Then
        ResultCountBadge = "<span class=""badge text-bg-danger"">0</span>"
    Else
        ResultCountBadge = "<span class=""badge text-bg-success"">" & Number0(value) & "</span>"
    End If
End Function

Sub AddCommonParameters(ByRef commandObject, ByVal dateFrom, ByVal dateTo, ByVal searchText)
    commandObject.Parameters.Append commandObject.CreateParameter("@DateFrom", adVarChar, adParamInput, 10, dateFrom)
    commandObject.Parameters.Append commandObject.CreateParameter("@DateTo", adVarChar, adParamInput, 10, dateTo)
    If searchText <> "" Then
        commandObject.Parameters.Append commandObject.CreateParameter("@Keyword", adVarChar, adParamInput, 110, "%" & searchText & "%")
    End If
End Sub

' ----------------------------------------------------------------------
' Filters and defaults
' ----------------------------------------------------------------------
startDate = Trim(Request.QueryString("StartDate"))
endDate = Trim(Request.QueryString("EndDate"))
sourceFilter = LCase(Trim(Request.QueryString("Source")))
resultStatus = LCase(Trim(Request.QueryString("ResultStatus")))
keyword = Trim(Request.QueryString("Keyword"))
topLimit = Trim(Request.QueryString("TopLimit"))
recentLimit = Trim(Request.QueryString("RecentLimit"))

If startDate = "" Then startDate = IsoDate(DateAdd("d", -6, Date()))
If endDate = "" Then endDate = IsoDate(Date())
If sourceFilter = "" Then sourceFilter = "human"
If resultStatus = "" Then resultStatus = "all"
If topLimit = "" Or Not IsNumeric(topLimit) Then topLimit = 25
If recentLimit = "" Or Not IsNumeric(recentLimit) Then recentLimit = 100

topLimit = CInt(topLimit)
recentLimit = CInt(recentLimit)
If topLimit <> 10 And topLimit <> 25 And topLimit <> 50 And topLimit <> 100 Then topLimit = 25
If recentLimit <> 50 And recentLimit <> 100 And recentLimit <> 250 And recentLimit <> 500 Then recentLimit = 100

Select Case sourceFilter
    Case "human", "all", "legacy", "button", "incremental", "url"
        ' Valid.
    Case Else
        sourceFilter = "human"
End Select

Select Case resultStatus
    Case "all", "recorded", "zero", "positive", "unrecorded"
        ' Valid.
    Case Else
        resultStatus = "all"
End Select

validationMessage = ""
If Not IsValidIsoDate(startDate) Or Not IsValidIsoDate(endDate) Then
    validationMessage = "Please enter valid start and end dates."
ElseIf ParseIsoDate(startDate) > ParseIsoDate(endDate) Then
    validationMessage = "The start date must not be later than the end date."
Else
    dateRangeDays = DateDiff("d", ParseIsoDate(startDate), ParseIsoDate(endDate)) + 1
    If dateRangeDays > 366 Then
        validationMessage = "Please select a date range of 366 days or fewer."
    End If
End If

sourceSql = ""
Select Case sourceFilter
    Case "human"
        sourceSql = " AND sl.SearchSource IN ('legacy','button','incremental') "
    Case "legacy", "button", "incremental", "url"
        sourceSql = " AND sl.SearchSource = '" & sourceFilter & "' "
End Select

resultSql = ""
Select Case resultStatus
    Case "recorded"
        resultSql = " AND sl.ResultCount IS NOT NULL "
    Case "zero"
        resultSql = " AND sl.ResultCount = 0 "
    Case "positive"
        resultSql = " AND sl.ResultCount > 0 "
    Case "unrecorded"
        resultSql = " AND sl.ResultCount IS NULL "
End Select

keywordSql = ""
If keyword <> "" Then keywordSql = " AND sl.Search LIKE ? "

whereSql = " WHERE sl.LastUpdated >= ? " & _
           "AND sl.LastUpdated < DATE_ADD(?, INTERVAL 1 DAY) " & _
           "AND TRIM(IFNULL(sl.Search, '')) <> '' " & _
           sourceSql & resultSql & keywordSql

rangeLabel = startDate & " to " & endDate

' ----------------------------------------------------------------------
' Run report queries
' ----------------------------------------------------------------------
If validationMessage = "" Then
    Set conn = Server.CreateObject("ADODB.Connection")
    conn.Open "DSN=MySQL_hyperflight;"

    ' Summary
    Set cmd = Server.CreateObject("ADODB.Command")
    Set cmd.ActiveConnection = conn
    cmd.CommandType = adCmdText
    cmd.CommandText = _
        "SELECT " & _
        "COUNT(*) AS TotalSearches, " & _
        "COUNT(DISTINCT sl.SessionID) AS UniqueSessions, " & _
        "COUNT(DISTINCT TRIM(sl.Search)) AS UniqueTerms, " & _
        "SUM(CASE WHEN sl.SearchSource = 'incremental' THEN 1 ELSE 0 END) AS IncrementalSearches, " & _
        "SUM(CASE WHEN sl.ProductCode IS NOT NULL AND sl.ProductCode <> '' THEN 1 ELSE 0 END) AS ProductSelections, " & _
        "COUNT(DISTINCT CASE WHEN sl.ProductCode IS NOT NULL AND sl.ProductCode <> '' THEN sl.ProductCode END) AS DistinctProducts, " & _
        "SUM(CASE WHEN sl.ResultCount IS NOT NULL THEN 1 ELSE 0 END) AS ResultRecordedCount, " & _
        "SUM(CASE WHEN sl.ResultCount = 0 THEN 1 ELSE 0 END) AS NoResultSearches, " & _
        "SUM(CASE WHEN sl.ResultCount > 0 THEN 1 ELSE 0 END) AS WithResultSearches, " & _
        "AVG(sl.ResultCount) AS AverageResultCount " & _
        "FROM searchlog sl " & whereSql
    AddCommonParameters cmd, startDate, endDate, keyword
    Set rsSummary = cmd.Execute()

    totalSearches = 0
    uniqueSessions = 0
    uniqueTerms = 0
    incrementalSearches = 0
    productSelections = 0
    distinctProducts = 0
    resultRecordedCount = 0
    noResultSearches = 0
    withResultSearches = 0
    noResultRate = 0
    averageResultCount = Null

    If Not rsSummary.EOF Then
        If Not IsNull(rsSummary("TotalSearches")) Then totalSearches = CLng(rsSummary("TotalSearches"))
        If Not IsNull(rsSummary("UniqueSessions")) Then uniqueSessions = CLng(rsSummary("UniqueSessions"))
        If Not IsNull(rsSummary("UniqueTerms")) Then uniqueTerms = CLng(rsSummary("UniqueTerms"))
        If Not IsNull(rsSummary("IncrementalSearches")) Then incrementalSearches = CLng(rsSummary("IncrementalSearches"))
        If Not IsNull(rsSummary("ProductSelections")) Then productSelections = CLng(rsSummary("ProductSelections"))
        If Not IsNull(rsSummary("DistinctProducts")) Then distinctProducts = CLng(rsSummary("DistinctProducts"))
        If Not IsNull(rsSummary("ResultRecordedCount")) Then resultRecordedCount = CLng(rsSummary("ResultRecordedCount"))
        If Not IsNull(rsSummary("NoResultSearches")) Then noResultSearches = CLng(rsSummary("NoResultSearches"))
        If Not IsNull(rsSummary("WithResultSearches")) Then withResultSearches = CLng(rsSummary("WithResultSearches"))
        If Not IsNull(rsSummary("AverageResultCount")) Then averageResultCount = CDbl(rsSummary("AverageResultCount"))
    End If
    rsSummary.Close
    Set rsSummary = Nothing
    Set cmd = Nothing

    selectionRate = 0
    If incrementalSearches > 0 Then selectionRate = (productSelections / incrementalSearches) * 100

    noResultRate = 0
    If resultRecordedCount > 0 Then noResultRate = (noResultSearches / resultRecordedCount) * 100

    ' Source breakdown
    Set cmd = Server.CreateObject("ADODB.Command")
    Set cmd.ActiveConnection = conn
    cmd.CommandType = adCmdText
    cmd.CommandText = _
        "SELECT sl.SearchSource, COUNT(*) AS SearchCount, " & _
        "COUNT(DISTINCT sl.SessionID) AS SessionCount, " & _
        "SUM(CASE WHEN sl.ProductCode IS NOT NULL AND sl.ProductCode <> '' THEN 1 ELSE 0 END) AS SelectedCount, " & _
        "SUM(CASE WHEN sl.ResultCount IS NOT NULL THEN 1 ELSE 0 END) AS ResultRecordedCount, " & _
        "SUM(CASE WHEN sl.ResultCount = 0 THEN 1 ELSE 0 END) AS NoResultCount " & _
        "FROM searchlog sl " & whereSql & _
        "GROUP BY sl.SearchSource ORDER BY SearchCount DESC"
    AddCommonParameters cmd, startDate, endDate, keyword
    Set rsSources = cmd.Execute()
    Set cmd = Nothing

    ' Daily activity
    Set cmd = Server.CreateObject("ADODB.Command")
    Set cmd.ActiveConnection = conn
    cmd.CommandType = adCmdText
    cmd.CommandText = _
        "SELECT DATE(sl.LastUpdated) AS SearchDate, COUNT(*) AS SearchCount, " & _
        "COUNT(DISTINCT sl.SessionID) AS SessionCount, " & _
        "SUM(CASE WHEN sl.ProductCode IS NOT NULL AND sl.ProductCode <> '' THEN 1 ELSE 0 END) AS SelectedCount, " & _
        "SUM(CASE WHEN sl.ResultCount IS NOT NULL THEN 1 ELSE 0 END) AS ResultRecordedCount, " & _
        "SUM(CASE WHEN sl.ResultCount = 0 THEN 1 ELSE 0 END) AS NoResultCount " & _
        "FROM searchlog sl " & whereSql & _
        "GROUP BY DATE(sl.LastUpdated) ORDER BY SearchDate DESC"
    AddCommonParameters cmd, startDate, endDate, keyword
    Set rsDaily = cmd.Execute()
    Set cmd = Nothing

    ' Top search terms
    Set cmd = Server.CreateObject("ADODB.Command")
    Set cmd.ActiveConnection = conn
    cmd.CommandType = adCmdText
    cmd.CommandText = _
        "SELECT TRIM(sl.Search) AS SearchTerm, COUNT(*) AS SearchCount, " & _
        "COUNT(DISTINCT sl.SessionID) AS SessionCount, " & _
        "SUM(CASE WHEN sl.ProductCode IS NOT NULL AND sl.ProductCode <> '' THEN 1 ELSE 0 END) AS SelectedCount, " & _
        "SUM(CASE WHEN sl.ResultCount IS NOT NULL THEN 1 ELSE 0 END) AS ResultRecordedCount, " & _
        "SUM(CASE WHEN sl.ResultCount = 0 THEN 1 ELSE 0 END) AS NoResultCount, " & _
        "AVG(sl.ResultCount) AS AverageResultCount, " & _
        "MAX(sl.LastUpdated) AS LastUsed " & _
        "FROM searchlog sl " & whereSql & _
        "GROUP BY TRIM(sl.Search) " & _
        "ORDER BY SearchCount DESC, SessionCount DESC, SearchTerm ASC " & _
        "LIMIT " & topLimit
    AddCommonParameters cmd, startDate, endDate, keyword
    Set rsTerms = cmd.Execute()
    Set cmd = Nothing

    ' Most common searches returning no results
    Set cmd = Server.CreateObject("ADODB.Command")
    Set cmd.ActiveConnection = conn
    cmd.CommandType = adCmdText
    cmd.CommandText = _
        "SELECT TRIM(sl.Search) AS SearchTerm, COUNT(*) AS NoResultCount, " & _
        "COUNT(DISTINCT sl.SessionID) AS SessionCount, MAX(sl.LastUpdated) AS LastUsed " & _
        "FROM searchlog sl " & whereSql & _
        "AND sl.ResultCount = 0 " & _
        "GROUP BY TRIM(sl.Search) " & _
        "ORDER BY NoResultCount DESC, SessionCount DESC, SearchTerm ASC " & _
        "LIMIT " & topLimit
    AddCommonParameters cmd, startDate, endDate, keyword
    Set rsNoResults = cmd.Execute()
    Set cmd = Nothing

    ' Most selected products
    Set cmd = Server.CreateObject("ADODB.Command")
    Set cmd.ActiveConnection = conn
    cmd.CommandType = adCmdText
    cmd.CommandText = _
        "SELECT sl.ProductCode, MAX(p.ProductName) AS ProductName, COUNT(*) AS SearchCount, " & _
        "COUNT(DISTINCT sl.SessionID) AS SessionCount, MAX(sl.LastUpdated) AS LastUsed " & _
        "FROM searchlog sl " & _
        "LEFT JOIN products p ON p.ProductCode = sl.ProductCode " & _
        whereSql & _
        "AND sl.ProductCode IS NOT NULL AND sl.ProductCode <> '' " & _
        "GROUP BY sl.ProductCode " & _
        "ORDER BY SearchCount DESC, sl.ProductCode ASC " & _
        "LIMIT " & topLimit
    AddCommonParameters cmd, startDate, endDate, keyword
    Set rsProducts = cmd.Execute()
    Set cmd = Nothing

    ' Highest-volume visitor IPs
    Set cmd = Server.CreateObject("ADODB.Command")
    Set cmd.ActiveConnection = conn
    cmd.CommandType = adCmdText
    cmd.CommandText = _
        "SELECT IFNULL(NULLIF(sl.VisitorIP, ''), '(blank)') AS VisitorIP, COUNT(*) AS SearchCount, " & _
        "COUNT(DISTINCT sl.SessionID) AS SessionCount, " & _
        "COUNT(DISTINCT TRIM(sl.Search)) AS UniqueTerms, " & _
        "SUM(CASE WHEN sl.ResultCount IS NOT NULL THEN 1 ELSE 0 END) AS ResultRecordedCount, " & _
        "SUM(CASE WHEN sl.ResultCount = 0 THEN 1 ELSE 0 END) AS NoResultCount, " & _
        "MAX(sl.LastUpdated) AS LastUsed " & _
        "FROM searchlog sl " & whereSql & _
        "GROUP BY IFNULL(NULLIF(sl.VisitorIP, ''), '(blank)') " & _
        "ORDER BY SearchCount DESC, VisitorIP ASC LIMIT " & topLimit
    AddCommonParameters cmd, startDate, endDate, keyword
    Set rsVisitors = cmd.Execute()
    Set cmd = Nothing

    ' Recent detail
    Set cmd = Server.CreateObject("ADODB.Command")
    Set cmd.ActiveConnection = conn
    cmd.CommandType = adCmdText
    cmd.CommandText = _
        "SELECT sl.ID, sl.LastUpdated, sl.SearchSource, sl.Search, sl.ResultCount, sl.ProductCode, " & _
        "sl.SessionID, sl.VisitorIP, p.ProductName " & _
        "FROM searchlog sl " & _
        "LEFT JOIN products p ON p.ProductCode = sl.ProductCode " & _
        whereSql & _
        "ORDER BY sl.LastUpdated DESC, sl.ID DESC LIMIT " & recentLimit
    AddCommonParameters cmd, startDate, endDate, keyword
    Set rsRecent = cmd.Execute()
    Set cmd = Nothing
End If
%>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Search Log Analysis</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        body { background: #f5f6f8; }
        .report-wrap { max-width: 1500px; }
        .metric-value { font-size: 1.8rem; font-weight: 700; line-height: 1.1; }
        .metric-label { color: #6c757d; font-size: 0.9rem; }
        .table th { white-space: nowrap; }
        .search-text { min-width: 260px; max-width: 620px; overflow-wrap: anywhere; }
        .product-name { min-width: 240px; }
        .small-note { font-size: 0.875rem; color: #6c757d; }
        .source-breakdown-table { font-size: 0.925rem; }
        .source-breakdown-table th,
        .source-breakdown-table td { padding-left: 0.55rem; padding-right: 0.55rem; }
        .source-breakdown-table .source-cell { min-width: 130px; }
        .sticky-table-head thead th { position: sticky; top: 0; z-index: 2; }
        .card-header h2 { font-size: 1.05rem; margin: 0; }
    </style>
</head>
<body class="pb-5">
<div class="container-fluid report-wrap py-4 px-3 px-lg-4">

    <div class="card shadow-sm mb-4">
        <div class="card-header bg-primary text-white py-3">
            <div class="d-flex flex-column flex-lg-row justify-content-between gap-2 align-items-lg-center">
                <div>
                    <h1 class="h4 mb-1">Search Log Analysis</h1>
                    <div class="small opacity-75">Search behaviour, result counts, no-result searches and incremental product selections</div>
                </div>
                <span class="badge text-bg-light"><%=Html(rangeLabel)%></span>
            </div>
        </div>
        <div class="card-body">
            <form method="get" action="" class="row g-3 align-items-end">
                <div class="col-sm-6 col-lg-2">
                    <label for="StartDate" class="form-label fw-semibold">Start date</label>
                    <input type="date" class="form-control" id="StartDate" name="StartDate" value="<%=Html(startDate)%>" required>
                </div>
                <div class="col-sm-6 col-lg-2">
                    <label for="EndDate" class="form-label fw-semibold">End date</label>
                    <input type="date" class="form-control" id="EndDate" name="EndDate" value="<%=Html(endDate)%>" required>
                </div>
                <div class="col-sm-6 col-lg-2">
                    <label for="Source" class="form-label fw-semibold">Search source</label>
                    <select class="form-select" id="Source" name="Source">
                        <option value="human" <% If sourceFilter = "human" Then Response.Write "selected" %>>Non-URL searches</option>
                        <option value="all" <% If sourceFilter = "all" Then Response.Write "selected" %>>All sources</option>
                        <option value="button" <% If sourceFilter = "button" Then Response.Write "selected" %>>Search button</option>
                        <option value="incremental" <% If sourceFilter = "incremental" Then Response.Write "selected" %>>Incremental selection</option>
                        <option value="legacy" <% If sourceFilter = "legacy" Then Response.Write "selected" %>>Legacy</option>
                        <option value="url" <% If sourceFilter = "url" Then Response.Write "selected" %>>URL/query-string</option>
                    </select>
                </div>
                <div class="col-sm-6 col-lg-2">
                    <label for="ResultStatus" class="form-label fw-semibold">Result status</label>
                    <select class="form-select" id="ResultStatus" name="ResultStatus">
                        <option value="all" <% If resultStatus = "all" Then Response.Write "selected" %>>All statuses</option>
                        <option value="recorded" <% If resultStatus = "recorded" Then Response.Write "selected" %>>Count recorded</option>
                        <option value="zero" <% If resultStatus = "zero" Then Response.Write "selected" %>>No results</option>
                        <option value="positive" <% If resultStatus = "positive" Then Response.Write "selected" %>>One or more results</option>
                        <option value="unrecorded" <% If resultStatus = "unrecorded" Then Response.Write "selected" %>>Not recorded</option>
                    </select>
                </div>
                <div class="col-sm-6 col-lg-2">
                    <label for="Keyword" class="form-label fw-semibold">Search contains</label>
                    <input type="search" class="form-control" id="Keyword" name="Keyword" value="<%=Html(keyword)%>" maxlength="100" placeholder="Optional term">
                </div>
                <div class="col-sm-6 col-lg-1">
                    <label for="TopLimit" class="form-label fw-semibold">Top</label>
                    <select class="form-select" id="TopLimit" name="TopLimit">
                        <option value="10" <% If topLimit = 10 Then Response.Write "selected" %>>10</option>
                        <option value="25" <% If topLimit = 25 Then Response.Write "selected" %>>25</option>
                        <option value="50" <% If topLimit = 50 Then Response.Write "selected" %>>50</option>
                        <option value="100" <% If topLimit = 100 Then Response.Write "selected" %>>100</option>
                    </select>
                </div>
                <div class="col-sm-6 col-lg-1 d-grid">
                    <input type="hidden" name="RecentLimit" value="<%=recentLimit%>">
                    <button type="submit" class="btn btn-primary fw-semibold">Run</button>
                </div>
            </form>
        </div>
    </div>

    <% If validationMessage <> "" Then %>
        <div class="alert alert-warning"><%=Html(validationMessage)%></div>
    <% Else %>

        <% If sourceFilter = "all" Or sourceFilter = "url" Then %>
            <div class="alert alert-warning shadow-sm">
                <strong>URL searches need separate interpretation.</strong>
                The sample data shows that many are crawler-generated or unrelated query-string requests, so they should not normally be treated as customer search demand.
            </div>
        <% End If %>

        <div class="alert alert-info shadow-sm">
            <strong>Data note:</strong> Search source and selected product tracking were added on 23 July 2026. Result count tracking was added on 24 July 2026. A result count of <strong>0</strong> means no products were found; a positive number is the number of products found; and a blank value means the count was not recorded. Incremental selections and legacy records normally have no result count.
        </div>

        <div class="row g-3 mb-4">
            <div class="col-6 col-xl">
                <div class="card shadow-sm h-100"><div class="card-body">
                    <div class="metric-value"><%=Number0(totalSearches)%></div>
                    <div class="metric-label">Search events</div>
                </div></div>
            </div>
            <div class="col-6 col-xl">
                <div class="card shadow-sm h-100"><div class="card-body">
                    <div class="metric-value"><%=Number0(uniqueSessions)%></div>
                    <div class="metric-label">Sessions</div>
                </div></div>
            </div>
            <div class="col-6 col-xl">
                <div class="card shadow-sm h-100"><div class="card-body">
                    <div class="metric-value"><%=Number0(uniqueTerms)%></div>
                    <div class="metric-label">Distinct terms</div>
                </div></div>
            </div>
            <div class="col-6 col-xl">
                <div class="card shadow-sm h-100"><div class="card-body">
                    <div class="metric-value"><%=Number0(productSelections)%></div>
                    <div class="metric-label">Product selections</div>
                </div></div>
            </div>
            <div class="col-6 col-xl">
                <div class="card shadow-sm h-100"><div class="card-body">
                    <div class="metric-value"><%=Number0(distinctProducts)%></div>
                    <div class="metric-label">Products selected</div>
                </div></div>
            </div>
            <div class="col-6 col-xl">
                <div class="card shadow-sm h-100"><div class="card-body">
                    <div class="metric-value"><%=FormatNumber(selectionRate, 1)%>%</div>
                    <div class="metric-label">Incremental selection rate</div>
                </div></div>
            </div>
        </div>

        <div class="row g-3 mb-4">
            <div class="col-6 col-xl">
                <div class="card shadow-sm h-100 border-primary"><div class="card-body">
                    <div class="metric-value"><%=Number0(resultRecordedCount)%></div>
                    <div class="metric-label">Result counts recorded</div>
                </div></div>
            </div>
            <div class="col-6 col-xl">
                <div class="card shadow-sm h-100 border-danger"><div class="card-body">
                    <div class="metric-value text-danger"><%=Number0(noResultSearches)%></div>
                    <div class="metric-label">Searches returning no results</div>
                </div></div>
            </div>
            <div class="col-6 col-xl">
                <div class="card shadow-sm h-100 border-success"><div class="card-body">
                    <div class="metric-value text-success"><%=Number0(withResultSearches)%></div>
                    <div class="metric-label">Searches returning results</div>
                </div></div>
            </div>
            <div class="col-6 col-xl">
                <div class="card shadow-sm h-100"><div class="card-body">
                    <div class="metric-value"><%=FormatNumber(noResultRate, 1)%>%</div>
                    <div class="metric-label">No-result rate</div>
                </div></div>
            </div>
            <div class="col-6 col-xl">
                <div class="card shadow-sm h-100"><div class="card-body">
                    <div class="metric-value"><% If IsNull(averageResultCount) Then Response.Write "&mdash;" Else Response.Write FormatNumber(averageResultCount, 1) %></div>
                    <div class="metric-label">Average results returned</div>
                </div></div>
            </div>
        </div>

        <div class="row g-4 mb-4">
            <div class="col-xl-6">
                <div class="card shadow-sm h-100">
                    <div class="card-header bg-dark text-white"><h2>Search source breakdown</h2></div>
                    <div class="table-responsive">
                        <table class="table table-sm table-hover align-middle mb-0 source-breakdown-table">
                            <thead class="table-light">
                                <tr><th class="source-cell">Source</th><th class="text-end">Searches</th><th class="text-end">Sessions</th><th class="text-end" title="Result counts recorded">Recorded</th><th class="text-end">No results</th><th class="text-end" title="No-result rate">Rate</th><th class="text-end">Selections</th><th class="text-end">Share</th></tr>
                            </thead>
                            <tbody>
                            <% If rsSources.EOF Then %>
                                <tr><td colspan="8" class="text-center text-muted py-4">No searches found.</td></tr>
                            <% Else %>
                                <% Do While Not rsSources.EOF
                                    sourceName = rsSources("SearchSource") & ""
                                    sourceCount = CLng(rsSources("SearchCount"))
                                    sourceSessions = CLng(rsSources("SessionCount"))
                                    sourceSelections = 0
                                    If Not IsNull(rsSources("SelectedCount")) Then sourceSelections = CLng(rsSources("SelectedCount"))
                                    groupRecordedCount = 0
                                    If Not IsNull(rsSources("ResultRecordedCount")) Then groupRecordedCount = CLng(rsSources("ResultRecordedCount"))
                                    groupNoResultCount = 0
                                    If Not IsNull(rsSources("NoResultCount")) Then groupNoResultCount = CLng(rsSources("NoResultCount"))
                                    groupNoResultRate = 0
                                    If groupRecordedCount > 0 Then groupNoResultRate = (groupNoResultCount / groupRecordedCount) * 100
                                    sourcePercent = 0
                                    If totalSearches > 0 Then sourcePercent = CInt((sourceCount / totalSearches) * 100)
                                %>
                                <tr>
                                    <td class="source-cell"><span class="badge <%=SourceBadgeClass(sourceName)%>"><%=Html(SourceDescription(sourceName))%></span></td>
                                    <td class="text-end"><%=Number0(sourceCount)%></td>
                                    <td class="text-end"><%=Number0(sourceSessions)%></td>
                                    <td class="text-end"><%=Number0(groupRecordedCount)%></td>
                                    <td class="text-end"><%=Number0(groupNoResultCount)%></td>
                                    <td class="text-end"><%=FormatNumber(groupNoResultRate, 1)%>%</td>
                                    <td class="text-end"><%=Number0(sourceSelections)%></td>
                                    <td class="text-end"><%=sourcePercent%>%</td>
                                </tr>
                                <% rsSources.MoveNext : Loop %>
                            <% End If %>
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>

            <div class="col-xl-6">
                <div class="card shadow-sm h-100">
                    <div class="card-header bg-dark text-white"><h2>Daily activity</h2></div>
                    <div class="table-responsive" style="max-height: 360px;">
                        <table class="table table-hover mb-0 sticky-table-head">
                            <thead class="table-light"><tr><th>Date</th><th class="text-end">Searches</th><th class="text-end">Sessions</th><th class="text-end">Counts recorded</th><th class="text-end">No results</th><th class="text-end">No-result rate</th><th class="text-end">Selections</th></tr></thead>
                            <tbody>
                            <% If rsDaily.EOF Then %>
                                <tr><td colspan="7" class="text-center text-muted py-4">No daily activity found.</td></tr>
                            <% Else %>
                                <% Do While Not rsDaily.EOF
                                    groupRecordedCount = 0
                                    If Not IsNull(rsDaily("ResultRecordedCount")) Then groupRecordedCount = CLng(rsDaily("ResultRecordedCount"))
                                    groupNoResultCount = 0
                                    If Not IsNull(rsDaily("NoResultCount")) Then groupNoResultCount = CLng(rsDaily("NoResultCount"))
                                    groupNoResultRate = 0
                                    If groupRecordedCount > 0 Then groupNoResultRate = (groupNoResultCount / groupRecordedCount) * 100
                                %>
                                <tr>
                                    <td><%=Html(rsDaily("SearchDate"))%></td>
                                    <td class="text-end"><%=Number0(rsDaily("SearchCount"))%></td>
                                    <td class="text-end"><%=Number0(rsDaily("SessionCount"))%></td>
                                    <td class="text-end"><%=Number0(groupRecordedCount)%></td>
                                    <td class="text-end"><%=Number0(groupNoResultCount)%></td>
                                    <td class="text-end"><%=FormatNumber(groupNoResultRate, 1)%>%</td>
                                    <td class="text-end"><%=Number0(rsDaily("SelectedCount"))%></td>
                                </tr>
                                <% rsDaily.MoveNext : Loop %>
                            <% End If %>
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
        </div>

        <div class="card shadow-sm mb-4">
            <div class="card-header bg-primary text-white"><h2>Top search terms</h2></div>
            <div class="table-responsive">
                <table class="table table-striped table-hover align-middle mb-0">
                    <thead class="table-light"><tr><th>#</th><th>Search term</th><th class="text-end">Searches</th><th class="text-end">Sessions</th><th class="text-end">Counts recorded</th><th class="text-end">No results</th><th class="text-end">No-result rate</th><th class="text-end">Average results</th><th class="text-end">Selections</th><th>Last used</th></tr></thead>
                    <tbody>
                    <% If rsTerms.EOF Then %>
                        <tr><td colspan="10" class="text-center text-muted py-4">No search terms found.</td></tr>
                    <% Else %>
                        <% Dim termPosition : termPosition = 0
                           Do While Not rsTerms.EOF
                            termPosition = termPosition + 1
                            groupRecordedCount = 0
                            If Not IsNull(rsTerms("ResultRecordedCount")) Then groupRecordedCount = CLng(rsTerms("ResultRecordedCount"))
                            groupNoResultCount = 0
                            If Not IsNull(rsTerms("NoResultCount")) Then groupNoResultCount = CLng(rsTerms("NoResultCount"))
                            groupNoResultRate = 0
                            If groupRecordedCount > 0 Then groupNoResultRate = (groupNoResultCount / groupRecordedCount) * 100
                        %>
                        <tr>
                            <td class="text-muted"><%=termPosition%></td>
                            <td class="search-text fw-semibold"><%=Html(rsTerms("SearchTerm"))%></td>
                            <td class="text-end"><%=Number0(rsTerms("SearchCount"))%></td>
                            <td class="text-end"><%=Number0(rsTerms("SessionCount"))%></td>
                            <td class="text-end"><%=Number0(groupRecordedCount)%></td>
                            <td class="text-end"><%=Number0(groupNoResultCount)%></td>
                            <td class="text-end"><%=FormatNumber(groupNoResultRate, 1)%>%</td>
                            <td class="text-end"><% If IsNull(rsTerms("AverageResultCount")) Then Response.Write "&mdash;" Else Response.Write FormatNumber(rsTerms("AverageResultCount"), 1) %></td>
                            <td class="text-end"><%=Number0(rsTerms("SelectedCount"))%></td>
                            <td class="text-nowrap"><%=Html(rsTerms("LastUsed"))%></td>
                        </tr>
                        <% rsTerms.MoveNext : Loop %>
                    <% End If %>
                    </tbody>
                </table>
            </div>
        </div>

        <div class="row g-4 mb-4">
            <div class="col-xl-7">
                <div class="card shadow-sm h-100 border-danger">
                    <div class="card-header bg-danger text-white"><h2>Most common searches returning no results</h2></div>
                    <div class="table-responsive">
                        <table class="table table-striped table-hover align-middle mb-0">
                            <thead class="table-light"><tr><th>#</th><th>Search term</th><th class="text-end">No-result searches</th><th class="text-end">Sessions</th><th>Last no-result search</th></tr></thead>
                            <tbody>
                            <% If rsNoResults.EOF Then %>
                                <tr><td colspan="5" class="text-center text-muted py-4">No searches returning no results match the current filters.</td></tr>
                            <% Else %>
                                <% noResultPosition = 0
                                   Do While Not rsNoResults.EOF
                                    noResultPosition = noResultPosition + 1
                                %>
                                <tr>
                                    <td class="text-muted"><%=noResultPosition%></td>
                                    <td class="search-text fw-semibold"><%=Html(rsNoResults("SearchTerm"))%></td>
                                    <td class="text-end fw-semibold text-danger"><%=Number0(rsNoResults("NoResultCount"))%></td>
                                    <td class="text-end"><%=Number0(rsNoResults("SessionCount"))%></td>
                                    <td class="text-nowrap"><%=Html(rsNoResults("LastUsed"))%></td>
                                </tr>
                                <% rsNoResults.MoveNext : Loop %>
                            <% End If %>
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>

            <div class="col-xl-5">
                <div class="card shadow-sm h-100">
                    <div class="card-header bg-success text-white"><h2>Most selected products</h2></div>
                    <div class="table-responsive">
                        <table class="table table-striped table-hover align-middle mb-0">
                            <thead class="table-light"><tr><th>Product</th><th class="text-end">Selections</th><th class="text-end">Sessions</th><th>Last selected</th></tr></thead>
                            <tbody>
                            <% If rsProducts.EOF Then %>
                                <tr><td colspan="4" class="text-center text-muted py-4">No incremental product selections found.</td></tr>
                            <% Else %>
                                <% Do While Not rsProducts.EOF
                                    productCode = rsProducts("ProductCode") & ""
                                    productName = rsProducts("ProductName") & ""
                                %>
                                <tr>
                                    <td>
                                        <a href="/products.asp?code=<%=Server.URLEncode(productCode)%>" target="_blank" rel="noopener" class="fw-semibold"><%=Html(productCode)%></a>
                                        <% If productName <> "" Then %><div class="small text-muted product-name"><%=Html(productName)%></div><% End If %>
                                    </td>
                                    <td class="text-end"><%=Number0(rsProducts("SearchCount"))%></td>
                                    <td class="text-end"><%=Number0(rsProducts("SessionCount"))%></td>
                                    <td class="text-nowrap"><%=Html(rsProducts("LastUsed"))%></td>
                                </tr>
                                <% rsProducts.MoveNext : Loop %>
                            <% End If %>
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
        </div>

        <div class="card shadow-sm mb-4">
            <div class="card-header bg-secondary text-white"><h2>Highest-volume visitor IPs</h2></div>
            <div class="table-responsive">
                <table class="table table-hover align-middle mb-0">
                    <thead class="table-light"><tr><th>Visitor IP</th><th class="text-end">Searches</th><th class="text-end">Sessions</th><th class="text-end">Distinct terms</th><th class="text-end">Counts recorded</th><th class="text-end">No results</th><th class="text-end">No-result rate</th><th>Last used</th></tr></thead>
                    <tbody>
                    <% If rsVisitors.EOF Then %>
                        <tr><td colspan="8" class="text-center text-muted py-4">No visitor IP data found.</td></tr>
                    <% Else %>
                        <% Do While Not rsVisitors.EOF
                            groupRecordedCount = 0
                            If Not IsNull(rsVisitors("ResultRecordedCount")) Then groupRecordedCount = CLng(rsVisitors("ResultRecordedCount"))
                            groupNoResultCount = 0
                            If Not IsNull(rsVisitors("NoResultCount")) Then groupNoResultCount = CLng(rsVisitors("NoResultCount"))
                            groupNoResultRate = 0
                            If groupRecordedCount > 0 Then groupNoResultRate = (groupNoResultCount / groupRecordedCount) * 100
                        %>
                        <tr>
                            <td class="font-monospace"><%=Html(rsVisitors("VisitorIP"))%></td>
                            <td class="text-end"><%=Number0(rsVisitors("SearchCount"))%></td>
                            <td class="text-end"><%=Number0(rsVisitors("SessionCount"))%></td>
                            <td class="text-end"><%=Number0(rsVisitors("UniqueTerms"))%></td>
                            <td class="text-end"><%=Number0(groupRecordedCount)%></td>
                            <td class="text-end"><%=Number0(groupNoResultCount)%></td>
                            <td class="text-end"><%=FormatNumber(groupNoResultRate, 1)%>%</td>
                            <td class="text-nowrap"><%=Html(rsVisitors("LastUsed"))%></td>
                        </tr>
                        <% rsVisitors.MoveNext : Loop %>
                    <% End If %>
                    </tbody>
                </table>
            </div>
        </div>

        <div class="card shadow-sm">
            <div class="card-header bg-dark text-white">
                <div class="d-flex flex-wrap justify-content-between align-items-center gap-2">
                    <h2>Recent search detail</h2>
                    <form method="get" action="" class="d-flex align-items-center gap-2">
                        <input type="hidden" name="StartDate" value="<%=Html(startDate)%>">
                        <input type="hidden" name="EndDate" value="<%=Html(endDate)%>">
                        <input type="hidden" name="Source" value="<%=Html(sourceFilter)%>">
                        <input type="hidden" name="ResultStatus" value="<%=Html(resultStatus)%>">
                        <input type="hidden" name="Keyword" value="<%=Html(keyword)%>">
                        <input type="hidden" name="TopLimit" value="<%=topLimit%>">
                        <label for="RecentLimit" class="small text-nowrap">Rows:</label>
                        <select class="form-select form-select-sm" id="RecentLimit" name="RecentLimit" onchange="this.form.submit()">
                            <option value="50" <% If recentLimit = 50 Then Response.Write "selected" %>>50</option>
                            <option value="100" <% If recentLimit = 100 Then Response.Write "selected" %>>100</option>
                            <option value="250" <% If recentLimit = 250 Then Response.Write "selected" %>>250</option>
                            <option value="500" <% If recentLimit = 500 Then Response.Write "selected" %>>500</option>
                        </select>
                    </form>
                </div>
            </div>
            <div class="table-responsive" style="max-height: 720px;">
                <table class="table table-striped table-hover align-middle mb-0 sticky-table-head">
                    <thead class="table-light">
                        <tr><th>Date/time</th><th>Source</th><th>Search</th><th class="text-end">Results</th><th>Selected product</th><th>Session</th><th>Visitor IP</th></tr>
                    </thead>
                    <tbody>
                    <% If rsRecent.EOF Then %>
                        <tr><td colspan="7" class="text-center text-muted py-4">No searches found.</td></tr>
                    <% Else %>
                        <% Do While Not rsRecent.EOF
                            searchSource = rsRecent("SearchSource") & ""
                            productCode = rsRecent("ProductCode") & ""
                            productName = rsRecent("ProductName") & ""
                        %>
                        <tr>
                            <td class="text-nowrap"><%=Html(rsRecent("LastUpdated"))%></td>
                            <td><span class="badge <%=SourceBadgeClass(searchSource)%>"><%=Html(SourceDescription(searchSource))%></span></td>
                            <td class="search-text"><%=Html(rsRecent("Search"))%></td>
                            <td class="text-end"><%=ResultCountBadge(rsRecent("ResultCount"))%></td>
                            <td>
                                <% If productCode <> "" Then %>
                                    <a href="/products.asp?code=<%=Server.URLEncode(productCode)%>" target="_blank" rel="noopener" class="fw-semibold"><%=Html(productCode)%></a>
                                    <% If productName <> "" Then %><div class="small text-muted"><%=Html(productName)%></div><% End If %>
                                <% Else %>
                                    <span class="text-muted">—</span>
                                <% End If %>
                            </td>
                            <td class="font-monospace"><%=Html(rsRecent("SessionID"))%></td>
                            <td class="font-monospace"><%=Html(rsRecent("VisitorIP"))%></td>
                        </tr>
                        <% rsRecent.MoveNext : Loop %>
                    <% End If %>
                    </tbody>
                </table>
            </div>
        </div>

        <div class="small-note mt-3">
            The no-result rate is calculated only from records where <code>ResultCount</code> is populated. A value of 0 means no products were found; a positive value is the number of products found; and a blank value means the count was not recorded. Incremental selection rate is calculated as product selections divided by incremental search events.
        </div>

    <%
        rsSources.Close : Set rsSources = Nothing
        rsDaily.Close : Set rsDaily = Nothing
        rsTerms.Close : Set rsTerms = Nothing
        rsNoResults.Close : Set rsNoResults = Nothing
        rsProducts.Close : Set rsProducts = Nothing
        rsVisitors.Close : Set rsVisitors = Nothing
        rsRecent.Close : Set rsRecent = Nothing
        conn.Close : Set conn = Nothing
    End If
    %>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>