Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
D
Distributed-Search-Engine
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
tammam.alsoleman
Distributed-Search-Engine
Commits
ee63c939
Commit
ee63c939
authored
Jan 22, 2026
by
tammam.alsoleman
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
edit to show the execution time
parent
eb5ae0d6
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
97 additions
and
118 deletions
+97
-118
SearchClient.java
src/main/java/com/distributed/search/grpc/SearchClient.java
+3
-2
index.html
src/main/resources/index.html
+94
-116
No files found.
src/main/java/com/distributed/search/grpc/SearchClient.java
View file @
ee63c939
...
@@ -50,7 +50,7 @@ public class SearchClient {
...
@@ -50,7 +50,7 @@ public class SearchClient {
if
(
stubs
.
isEmpty
())
{
if
(
stubs
.
isEmpty
())
{
return
Collections
.
emptyList
();
return
Collections
.
emptyList
();
}
}
long
startTime
=
System
.
currentTimeMillis
();
// Phase 1: Aggregate Global Counts
// Phase 1: Aggregate Global Counts
Map
<
String
,
Integer
>
globalTermCounts
=
new
HashMap
<>();
Map
<
String
,
Integer
>
globalTermCounts
=
new
HashMap
<>();
int
filesPerWorker
=
(
int
)
Math
.
ceil
((
double
)
allFiles
.
size
()
/
stubs
.
size
());
int
filesPerWorker
=
(
int
)
Math
.
ceil
((
double
)
allFiles
.
size
()
/
stubs
.
size
());
...
@@ -101,7 +101,8 @@ public class SearchClient {
...
@@ -101,7 +101,8 @@ public class SearchClient {
}
catch
(
Exception
e
)
{
System
.
err
.
println
(
"Worker "
+
address
+
" Phase 2 error"
);
}
}
catch
(
Exception
e
)
{
System
.
err
.
println
(
"Worker "
+
address
+
" Phase 2 error"
);
}
currentFileIndex
+=
count
;
currentFileIndex
+=
count
;
}
}
long
endTime
=
System
.
currentTimeMillis
();
System
.
out
.
println
(
">>> Distributed Search took: "
+
(
endTime
-
startTime
)
+
" ms"
);
// --- return the sorted list ---
// --- return the sorted list ---
finalResults
.
sort
((
a
,
b
)
->
Double
.
compare
(
b
.
getScore
(),
a
.
getScore
()));
finalResults
.
sort
((
a
,
b
)
->
Double
.
compare
(
b
.
getScore
(),
a
.
getScore
()));
return
finalResults
;
return
finalResults
;
...
...
src/main/resources/index.html
View file @
ee63c939
...
@@ -3,15 +3,14 @@
...
@@ -3,15 +3,14 @@
<head>
<head>
<meta
charset=
"UTF-8"
>
<meta
charset=
"UTF-8"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
>
<title>
Distributed Engine |
Smart Search
</title>
<title>
Distributed Engine |
Performance Dashboard
</title>
<!--
External Resources: FontAwesome for icons and Inter Font for modern typography
-->
<!--
UI Enhancement: FontAwesome & Inter Font
-->
<link
rel=
"stylesheet"
href=
"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
>
<link
rel=
"stylesheet"
href=
"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
>
<link
href=
"https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&display=swap"
rel=
"stylesheet"
>
<link
href=
"https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&display=swap"
rel=
"stylesheet"
>
<style>
<style>
:root
{
:root
{
/* Professional Navy Palette */
--primary-dark
:
#0f172a
;
--primary-dark
:
#0f172a
;
--primary-navy
:
#1e3a8a
;
--primary-navy
:
#1e3a8a
;
--accent-blue
:
#3b82f6
;
--accent-blue
:
#3b82f6
;
...
@@ -19,6 +18,7 @@
...
@@ -19,6 +18,7 @@
--text-main
:
#1e293b
;
--text-main
:
#1e293b
;
--text-muted
:
#64748b
;
--text-muted
:
#64748b
;
--card-border
:
#e2e8f0
;
--card-border
:
#e2e8f0
;
--success-green
:
#059669
;
}
}
body
{
body
{
...
@@ -27,7 +27,6 @@
...
@@ -27,7 +27,6 @@
display
:
flex
;
flex-direction
:
column
;
align-items
:
center
;
min-height
:
100vh
;
display
:
flex
;
flex-direction
:
column
;
align-items
:
center
;
min-height
:
100vh
;
}
}
/* Header with Navy Gradient */
header
{
header
{
width
:
100%
;
width
:
100%
;
background
:
linear-gradient
(
135deg
,
var
(
--primary-dark
)
0%
,
var
(
--primary-navy
)
100%
);
background
:
linear-gradient
(
135deg
,
var
(
--primary-dark
)
0%
,
var
(
--primary-navy
)
100%
);
...
@@ -38,7 +37,6 @@
...
@@ -38,7 +37,6 @@
header
h1
{
margin
:
0
;
font-size
:
2.6rem
;
font-weight
:
700
;
letter-spacing
:
-0.5px
;
}
header
h1
{
margin
:
0
;
font-size
:
2.6rem
;
font-weight
:
700
;
letter-spacing
:
-0.5px
;
}
header
p
{
opacity
:
0.8
;
font-weight
:
300
;
margin-top
:
8px
;
font-size
:
1.1rem
;
}
header
p
{
opacity
:
0.8
;
font-weight
:
300
;
margin-top
:
8px
;
font-size
:
1.1rem
;
}
/* Main Container */
.main-container
{
.main-container
{
width
:
90%
;
max-width
:
850px
;
background
:
white
;
width
:
90%
;
max-width
:
850px
;
background
:
white
;
border-radius
:
12px
;
padding
:
40px
;
box-shadow
:
0
20px
25px
-5px
rgba
(
0
,
0
,
0
,
0.05
);
border-radius
:
12px
;
padding
:
40px
;
box-shadow
:
0
20px
25px
-5px
rgba
(
0
,
0
,
0
,
0.05
);
...
@@ -52,10 +50,7 @@
...
@@ -52,10 +50,7 @@
font-size
:
1rem
;
transition
:
all
0.3s
;
outline
:
none
;
font-size
:
1rem
;
transition
:
all
0.3s
;
outline
:
none
;
}
}
input
[
type
=
"text"
]
:focus
{
input
[
type
=
"text"
]
:focus
{
border-color
:
var
(
--accent-blue
);
box-shadow
:
0
0
0
4px
rgba
(
59
,
130
,
246
,
0.1
);
}
border-color
:
var
(
--accent-blue
);
box-shadow
:
0
0
0
4px
rgba
(
59
,
130
,
246
,
0.1
);
}
button
#searchBtn
{
button
#searchBtn
{
padding
:
0
35px
;
background
:
var
(
--primary-navy
);
color
:
white
;
padding
:
0
35px
;
background
:
var
(
--primary-navy
);
color
:
white
;
...
@@ -65,66 +60,42 @@
...
@@ -65,66 +60,42 @@
button
#searchBtn
:hover
{
background
:
var
(
--primary-dark
);
transform
:
translateY
(
-1px
);
}
button
#searchBtn
:hover
{
background
:
var
(
--primary-dark
);
transform
:
translateY
(
-1px
);
}
/* Search History Chips */
/* History Tags */
.history-container
{
.history-container
{
display
:
flex
;
align-items
:
center
;
gap
:
10px
;
margin-bottom
:
25px
;
font-size
:
0.85rem
;
color
:
var
(
--text-muted
);
flex-wrap
:
wrap
;
}
display
:
flex
;
align-items
:
center
;
gap
:
10px
;
margin-bottom
:
25px
;
.history-chip
{
background
:
#f1f5f9
;
color
:
var
(
--primary-navy
);
padding
:
4px
12px
;
border-radius
:
20px
;
cursor
:
pointer
;
border
:
1px
solid
#e2e8f0
;
}
font-size
:
0.85rem
;
color
:
var
(
--text-muted
);
flex-wrap
:
wrap
;
.history-chip
:hover
{
background
:
var
(
--primary-navy
);
color
:
white
;
}
}
.history-chip
{
background
:
#f1f5f9
;
color
:
var
(
--primary-navy
);
padding
:
4px
12px
;
border-radius
:
20px
;
cursor
:
pointer
;
transition
:
all
0.2s
;
border
:
1px
solid
#e2e8f0
;
font-weight
:
500
;
}
.history-chip
:hover
{
background
:
var
(
--primary-navy
);
color
:
white
;
border-color
:
var
(
--primary-navy
);
}
.clear-history
{
color
:
#ef4444
;
cursor
:
pointer
;
font-size
:
0.75rem
;
text-decoration
:
underline
;
margin-left
:
5px
;
}
/* Stats & Benchmark Section */
/* Stats Bar */
.stats-bar
{
.stats-bar
{
display
:
flex
;
justify-content
:
space-between
;
padding
:
14px
20px
;
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
padding
:
14px
20px
;
background
:
#f8fafc
;
border-radius
:
6px
;
margin-bottom
:
25px
;
background
:
#f8fafc
;
border-radius
:
6px
;
margin-bottom
:
25px
;
font-size
:
0.9rem
;
color
:
var
(
--text-muted
);
display
:
none
;
font-size
:
0.9rem
;
color
:
var
(
--text-muted
);
display
:
none
;
border-left
:
4px
solid
var
(
--primary-navy
);
border-left
:
4px
solid
var
(
--primary-navy
);
}
}
/* Results List & Cards */
.log-btn
{
background
:
#e2e8f0
;
border
:
none
;
padding
:
5px
10px
;
border-radius
:
4px
;
font-size
:
0.75rem
;
cursor
:
pointer
;
color
:
var
(
--primary-navy
);
font-weight
:
600
;
}
.
results-list
{
display
:
flex
;
flex-direction
:
column
;
gap
:
12px
;
}
.
log-btn
:hover
{
background
:
#cbd5e1
;
}
.result-card
{
.perf-log-box
{
padding
:
18px
;
border
:
1px
solid
var
(
--card-border
);
border-radius
:
10px
;
margin-top
:
30px
;
padding
:
20px
;
background
:
#fafafa
;
border-radius
:
8px
;
border
:
1px
dashed
#cbd5e1
;
text-align
:
left
;
display
:
none
;
transition
:
all
0.3s
;
display
:
flex
;
align-items
:
center
;
gap
:
18px
;
animation
:
slideUp
0.4s
ease
forwards
;
}
}
.perf-log-box
h3
{
margin-top
:
0
;
font-size
:
1rem
;
color
:
var
(
--primary-navy
);
}
.perf-table
{
width
:
100%
;
border-collapse
:
collapse
;
font-size
:
0.85rem
;
margin-top
:
10px
;
}
.perf-table
th
{
text-align
:
left
;
padding
:
8px
;
border-bottom
:
2px
solid
#e2e8f0
;
color
:
var
(
--text-muted
);
}
.perf-table
td
{
padding
:
8px
;
border-bottom
:
1px
solid
#f1f5f9
;
}
.result-card
:hover
{
/* Results Styling */
border-color
:
var
(
--accent-blue
);
background-color
:
#fcfdfe
;
transform
:
translateX
(
5px
);
.results-list
{
display
:
flex
;
flex-direction
:
column
;
gap
:
12px
;
}
}
.result-card
{
padding
:
18px
;
border
:
1px
solid
var
(
--card-border
);
border-radius
:
10px
;
display
:
flex
;
align-items
:
center
;
gap
:
18px
;
animation
:
slideUp
0.4s
ease
forwards
;
}
.icon-box
{
background
:
#eff6ff
;
color
:
var
(
--primary-navy
);
padding
:
12px
;
border-radius
:
8px
;
font-size
:
1.3rem
;
}
.icon-box
{
.res-title
{
font-weight
:
700
;
color
:
var
(
--primary-dark
);
}
background
:
#eff6ff
;
color
:
var
(
--primary-navy
);
padding
:
12px
;
.res-score
{
color
:
var
(
--success-green
);
font-weight
:
700
;
}
border-radius
:
8px
;
font-size
:
1.3rem
;
border
:
1px
solid
#dbeafe
;
}
.res-content
{
flex
:
1
;
}
.res-title
{
font-weight
:
700
;
color
:
var
(
--primary-dark
);
font-size
:
1.05rem
;
margin-bottom
:
4px
;
}
.res-meta
{
font-size
:
0.85rem
;
color
:
var
(
--text-muted
);
display
:
flex
;
gap
:
15px
;
}
.res-score
{
color
:
#059669
;
font-weight
:
700
;
}
/* Pagination Controls */
.pagination
{
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
gap
:
12px
;
margin-top
:
30px
;
display
:
none
;
}
.pagination
{
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
gap
:
12px
;
margin-top
:
30px
;
display
:
none
;
}
.nav-btn
{
background
:
white
;
border
:
1px
solid
var
(
--card-border
);
padding
:
10px
22px
;
border-radius
:
6px
;
cursor
:
pointer
;
font-weight
:
600
;
color
:
var
(
--primary-navy
);
}
.nav-btn
{
background
:
white
;
border
:
1px
solid
var
(
--card-border
);
padding
:
10px
22px
;
border-radius
:
6px
;
cursor
:
pointer
;
font-weight
:
600
;
color
:
var
(
--primary-navy
);
transition
:
all
0.2s
;
}
.nav-btn
:hover:not
(
:disabled
)
{
background
:
var
(
--primary-navy
);
color
:
white
;
}
.nav-btn
:hover:not
(
:disabled
)
{
background
:
var
(
--primary-navy
);
color
:
white
;
}
.nav-btn
:disabled
{
opacity
:
0.4
;
cursor
:
not-allowed
;
}
/* Animation Keyframes */
@keyframes
slideUp
{
from
{
opacity
:
0
;
transform
:
translateY
(
15px
);
}
to
{
opacity
:
1
;
transform
:
translateY
(
0
);
}
}
@keyframes
slideUp
{
from
{
opacity
:
0
;
transform
:
translateY
(
15px
);
}
to
{
opacity
:
1
;
transform
:
translateY
(
0
);
}
}
.loader
{
display
:
none
;
border
:
3px
solid
#f3f3f3
;
border-top
:
3px
solid
var
(
--primary-navy
);
border-radius
:
50%
;
width
:
25px
;
height
:
25px
;
animation
:
spin
1s
linear
infinite
;
margin
:
20px
auto
;
}
.loader
{
display
:
none
;
border
:
3px
solid
#f3f3f3
;
border-top
:
3px
solid
var
(
--primary-navy
);
border-radius
:
50%
;
width
:
25px
;
height
:
25px
;
animation
:
spin
1s
linear
infinite
;
margin
:
20px
auto
;
}
@keyframes
spin
{
0
%
{
transform
:
rotate
(
0deg
);
}
100
%
{
transform
:
rotate
(
360deg
);
}
}
@keyframes
spin
{
0
%
{
transform
:
rotate
(
0deg
);
}
100
%
{
transform
:
rotate
(
360deg
);
}
}
</style>
</style>
...
@@ -133,66 +104,76 @@
...
@@ -133,66 +104,76 @@
<header>
<header>
<h1><i
class=
"fas fa-server"
></i>
ClusterSearch
</h1>
<h1><i
class=
"fas fa-server"
></i>
ClusterSearch
</h1>
<p>
Intelligent Distributed TF-IDF Text
Retrieval System
</p>
<p>
Performance-driven Distributed TF-IDF
Retrieval System
</p>
</header>
</header>
<div
class=
"main-container"
>
<div
class=
"main-container"
>
<!--
Search Input Section
-->
<!--
Input wrapper
-->
<div
class=
"search-wrapper"
>
<div
class=
"search-wrapper"
>
<input
type=
"text"
id=
"query"
placeholder=
"
Type keywords to search (e.g., windows drivers)
..."
onkeypress=
"if(event.key === 'Enter') newSearch()"
>
<input
type=
"text"
id=
"query"
placeholder=
"
Enter keywords to evaluate performance
..."
onkeypress=
"if(event.key === 'Enter') newSearch()"
>
<button
id=
"searchBtn"
onclick=
"newSearch()"
>
<button
id=
"searchBtn"
onclick=
"newSearch()"
>
<i
class=
"fas fa-bolt"
></i>
Search
<i
class=
"fas fa-bolt"
></i>
Search
</button>
</button>
</div>
</div>
<!-- Search History
Tags
-->
<!-- Search History -->
<div
class=
"history-container"
id=
"historyBox"
>
<div
class=
"history-container"
id=
"historyBox"
>
<span>
Recent
searches
:
</span>
<span>
Recent:
</span>
<div
id=
"historyList"
style=
"display: flex; gap: 8px;"
></div>
<div
id=
"historyList"
style=
"display: flex; gap: 8px;"
></div>
<span
class=
"clear-history
"
onclick=
"clearHistory()"
>
[Clear]
</span>
<span
style=
"color:#ef4444; cursor:pointer; font-size:0.7rem;
"
onclick=
"clearHistory()"
>
[Clear]
</span>
</div>
</div>
<!--
Search Statist
ics Bar -->
<!--
Metr
ics Bar -->
<div
class=
"stats-bar"
id=
"statsBar"
>
<div
class=
"stats-bar"
id=
"statsBar"
>
<span><i
class=
"fas fa-check-circle"
></i>
Results:
<strong
id=
"totalRes"
style=
"color:var(--primary-dark)"
>
0
</strong></span>
<div><i
class=
"fas fa-check-circle"
></i>
Results:
<strong
id=
"totalRes"
style=
"color:var(--primary-dark)"
>
0
</strong></div>
<span><i
class=
"fas fa-tachometer-alt"
></i>
Cluster Latency:
<strong
id=
"timeTaken"
style=
"color:var(--primary-dark)"
>
0
</strong>
ms
</span>
<div><i
class=
"fas fa-tachometer-alt"
></i>
Latency:
<strong
id=
"timeTaken"
style=
"color:var(--primary-dark)"
>
0
</strong>
ms
</div>
<button
class=
"log-btn"
onclick=
"addPerformanceToLog()"
><i
class=
"fas fa-save"
></i>
Log Run
</button>
</div>
</div>
<!-- Feedback Area (Loading / Results / Errors) -->
<div
class=
"loader"
id=
"loader"
></div>
<div
class=
"loader"
id=
"loader"
></div>
<div
class=
"results-list"
id=
"results"
></div>
<div
class=
"results-list"
id=
"results"
></div>
<!-- Navigation Area (Next / Previous Page) -->
<div
class=
"pagination"
id=
"pagination"
>
<div
class=
"pagination"
id=
"pagination"
>
<button
class=
"nav-btn"
id=
"prevBtn"
onclick=
"changePage(-1)"
>
Previous
</button>
<button
class=
"nav-btn"
id=
"prevBtn"
onclick=
"changePage(-1)"
>
Previous
</button>
<span
id=
"pageInfo"
style=
"font-weight: 600; color: var(--text-muted);"
>
Page 1
</span>
<span
id=
"pageInfo"
>
Page 1
</span>
<button
class=
"nav-btn"
id=
"nextBtn"
onclick=
"changePage(1)"
>
Next
</button>
<button
class=
"nav-btn"
id=
"nextBtn"
onclick=
"changePage(1)"
>
Next
</button>
</div>
</div>
<!-- Performance Comparison Log (The Benchmarking Feature) -->
<div
class=
"perf-log-box"
id=
"perfLogBox"
>
<h3><i
class=
"fas fa-chart-bar"
></i>
Performance Benchmark Log
</h3>
<p
style=
"font-size: 0.75rem; color:gray; margin-bottom: 10px;"
>
Use this table to compare search speeds across different node counts.
</p>
<table
class=
"perf-table"
>
<thead>
<tr>
<th>
#
</th>
<th>
Query
</th>
<th>
Count
</th>
<th>
Latency
</th>
<th>
Timestamp
</th>
</tr>
</thead>
<tbody
id=
"perfTableBody"
></tbody>
</table>
</div>
</div>
</div>
<script>
<script>
let
allResults
=
[];
// Stores the full result set from server
let
allResults
=
[];
let
currentPage
=
1
;
// Current page tracker
let
currentPage
=
1
;
const
pageSize
=
10
;
// Items to show per page
const
pageSize
=
10
;
let
runCounter
=
0
;
// Initialize UI
document
.
addEventListener
(
'DOMContentLoaded'
,
renderHistory
);
document
.
addEventListener
(
'DOMContentLoaded'
,
renderHistory
);
/**
* Resets the search state and triggers a new search
*/
async
function
newSearch
()
{
async
function
newSearch
()
{
const
query
=
document
.
getElementById
(
'query'
).
value
.
trim
();
const
query
=
document
.
getElementById
(
'query'
).
value
.
trim
();
if
(
!
query
)
return
;
if
(
!
query
)
return
;
saveToHistory
(
query
);
saveToHistory
(
query
);
currentPage
=
1
;
currentPage
=
1
;
await
performSearch
(
query
);
await
performSearch
(
query
);
}
}
/**
* Communicates with the Frontend HTTP API to get results from the Leader
*/
async
function
performSearch
(
query
)
{
async
function
performSearch
(
query
)
{
const
resultsDiv
=
document
.
getElementById
(
'results'
);
const
resultsDiv
=
document
.
getElementById
(
'results'
);
const
loader
=
document
.
getElementById
(
'loader'
);
const
loader
=
document
.
getElementById
(
'loader'
);
...
@@ -207,30 +188,24 @@
...
@@ -207,30 +188,24 @@
const
startTime
=
performance
.
now
();
const
startTime
=
performance
.
now
();
try
{
try
{
// Forward query to our FrontendApplication Java server
const
response
=
await
fetch
(
`/api/search?query=
${
encodeURIComponent
(
query
)}
`
);
const
response
=
await
fetch
(
`/api/search?query=
${
encodeURIComponent
(
query
)}
`
);
if
(
!
response
.
ok
)
throw
new
Error
(
"
Leader node did not respond
."
);
if
(
!
response
.
ok
)
throw
new
Error
(
"
Search failed at Leader node
."
);
allResults
=
await
response
.
json
();
allResults
=
await
response
.
json
();
const
endTime
=
performance
.
now
();
const
endTime
=
performance
.
now
();
// Update stats
document
.
getElementById
(
'totalRes'
).
innerText
=
allResults
.
length
;
document
.
getElementById
(
'totalRes'
).
innerText
=
allResults
.
length
;
document
.
getElementById
(
'timeTaken'
).
innerText
=
(
endTime
-
startTime
).
toFixed
(
0
);
document
.
getElementById
(
'timeTaken'
).
innerText
=
(
endTime
-
startTime
).
toFixed
(
0
);
loader
.
style
.
display
=
"none"
;
loader
.
style
.
display
=
"none"
;
statsBar
.
style
.
display
=
"flex"
;
statsBar
.
style
.
display
=
"flex"
;
render
();
// Draw the current page
render
();
}
catch
(
e
)
{
}
catch
(
e
)
{
loader
.
style
.
display
=
"none"
;
loader
.
style
.
display
=
"none"
;
resultsDiv
.
innerHTML
=
`<div style="text-align:center; color:#dc2626; padding:20px;">
resultsDiv
.
innerHTML
=
`<div style="text-align:center; color:#dc2626; padding:20px;">Error:
${
e
.
message
}
</div>`
;
<i class="fas fa-wifi-slash"></i> Error:
${
e
.
message
}
</div>`
;
}
}
}
}
/**
* Renders a specific page of results (10 at a time)
*/
function
render
()
{
function
render
()
{
const
resultsDiv
=
document
.
getElementById
(
'results'
);
const
resultsDiv
=
document
.
getElementById
(
'results'
);
const
start
=
(
currentPage
-
1
)
*
pageSize
;
const
start
=
(
currentPage
-
1
)
*
pageSize
;
...
@@ -238,15 +213,13 @@
...
@@ -238,15 +213,13 @@
resultsDiv
.
innerHTML
=
""
;
resultsDiv
.
innerHTML
=
""
;
if
(
allResults
.
length
===
0
)
{
if
(
allResults
.
length
===
0
)
{
resultsDiv
.
innerHTML
=
"<p style='text-align:center;
color:gray;'>No relevant documents found in the cluster
.</p>"
;
resultsDiv
.
innerHTML
=
"<p style='text-align:center;
'>No matching files
.</p>"
;
return
;
return
;
}
}
pageData
.
forEach
((
item
,
idx
)
=>
{
pageData
.
forEach
((
item
,
idx
)
=>
{
// Handle both Protobuf (with _) and standard JSON field names
const
name
=
item
.
documentName_
||
item
.
documentName
||
"File"
;
const
name
=
item
.
documentName_
||
item
.
documentName
||
"Unknown"
;
const
score
=
item
.
score_
||
item
.
score
||
0
;
const
score
=
item
.
score_
||
item
.
score
||
0
;
resultsDiv
.
innerHTML
+=
`
resultsDiv
.
innerHTML
+=
`
<div class="result-card">
<div class="result-card">
<div class="icon-box"><i class="fas fa-file-invoice"></i></div>
<div class="icon-box"><i class="fas fa-file-invoice"></i></div>
...
@@ -254,13 +227,12 @@
...
@@ -254,13 +227,12 @@
<div class="res-title">Document #
${
name
}
</div>
<div class="res-title">Document #
${
name
}
</div>
<div class="res-meta">
<div class="res-meta">
<span>Global Rank: <strong>
${
start
+
idx
+
1
}
</strong></span>
<span>Global Rank: <strong>
${
start
+
idx
+
1
}
</strong></span>
<span>Relativity
Score
: <span class="res-score">
${(
score
*
100
).
toFixed
(
4
)}
%</span></span>
<span>Relativity: <span class="res-score">
${(
score
*
100
).
toFixed
(
4
)}
%</span></span>
</div>
</div>
</div>
</div>
</div>`
;
</div>`
;
});
});
// Update Pagination Controls
document
.
getElementById
(
'pagination'
).
style
.
display
=
"flex"
;
document
.
getElementById
(
'pagination'
).
style
.
display
=
"flex"
;
document
.
getElementById
(
'pageInfo'
).
innerText
=
`Page
${
currentPage
}
of
${
Math
.
ceil
(
allResults
.
length
/
pageSize
)}
`
;
document
.
getElementById
(
'pageInfo'
).
innerText
=
`Page
${
currentPage
}
of
${
Math
.
ceil
(
allResults
.
length
/
pageSize
)}
`
;
document
.
getElementById
(
'prevBtn'
).
disabled
=
(
currentPage
===
1
);
document
.
getElementById
(
'prevBtn'
).
disabled
=
(
currentPage
===
1
);
...
@@ -268,49 +240,55 @@
...
@@ -268,49 +240,55 @@
}
}
/**
/**
*
Handles search history logic using localStorage
*
Logic to record the current search performance for benchmarking
*/
*/
function
addPerformanceToLog
()
{
const
logBox
=
document
.
getElementById
(
'perfLogBox'
);
const
tableBody
=
document
.
getElementById
(
'perfTableBody'
);
const
query
=
document
.
getElementById
(
'query'
).
value
;
const
count
=
document
.
getElementById
(
'totalRes'
).
innerText
;
const
time
=
document
.
getElementById
(
'timeTaken'
).
innerText
;
const
now
=
new
Date
().
toLocaleTimeString
();
logBox
.
style
.
display
=
"block"
;
runCounter
++
;
const
row
=
`<tr>
<td>
${
runCounter
}
</td>
<td><strong>
${
query
}
</strong></td>
<td>
${
count
}
docs</td>
<td style="color:var(--primary-navy); font-weight:700;">
${
time
}
ms</td>
<td>
${
now
}
</td>
</tr>`
;
tableBody
.
innerHTML
=
row
+
tableBody
.
innerHTML
;
// Prepend to show newest on top
}
function
saveToHistory
(
query
)
{
function
saveToHistory
(
query
)
{
let
history
=
JSON
.
parse
(
localStorage
.
getItem
(
'searchHistory'
)
||
'[]'
);
let
history
=
JSON
.
parse
(
localStorage
.
getItem
(
'searchHistory'
)
||
'[]'
);
history
=
history
.
filter
(
item
=>
item
!==
query
);
// Remove duplicates
history
=
history
.
filter
(
item
=>
item
!==
query
);
history
.
unshift
(
query
);
// Add to front
history
.
unshift
(
query
);
localStorage
.
setItem
(
'searchHistory'
,
JSON
.
stringify
(
history
.
slice
(
0
,
5
)));
// Keep last 5
localStorage
.
setItem
(
'searchHistory'
,
JSON
.
stringify
(
history
.
slice
(
0
,
5
)));
renderHistory
();
renderHistory
();
}
}
function
renderHistory
()
{
function
renderHistory
()
{
const
list
=
document
.
getElementById
(
'historyList'
);
const
list
=
document
.
getElementById
(
'historyList'
);
const
history
=
JSON
.
parse
(
localStorage
.
getItem
(
'searchHistory'
)
||
'[]'
);
const
history
=
JSON
.
parse
(
localStorage
.
getItem
(
'searchHistory'
)
||
'[]'
);
list
.
innerHTML
=
""
;
list
.
innerHTML
=
""
;
if
(
history
.
length
===
0
)
{
if
(
history
.
length
===
0
)
{
document
.
getElementById
(
'historyBox'
).
style
.
display
=
"none"
;
return
;
}
document
.
getElementById
(
'historyBox'
).
style
.
display
=
"none"
;
return
;
}
document
.
getElementById
(
'historyBox'
).
style
.
display
=
"flex"
;
document
.
getElementById
(
'historyBox'
).
style
.
display
=
"flex"
;
history
.
forEach
(
item
=>
{
history
.
forEach
(
item
=>
{
const
chip
=
document
.
createElement
(
'span'
);
const
chip
=
document
.
createElement
(
'span'
);
chip
.
className
=
'history-chip'
;
chip
.
className
=
'history-chip'
;
chip
.
innerText
=
item
;
chip
.
innerText
=
item
;
chip
.
onclick
=
()
=>
{
chip
.
onclick
=
()
=>
{
document
.
getElementById
(
'query'
).
value
=
item
;
newSearch
();
};
document
.
getElementById
(
'query'
).
value
=
item
;
newSearch
();
};
list
.
appendChild
(
chip
);
list
.
appendChild
(
chip
);
});
});
}
}
function
clearHistory
()
{
function
clearHistory
()
{
localStorage
.
removeItem
(
'searchHistory'
);
renderHistory
();
}
localStorage
.
removeItem
(
'searchHistory'
);
function
changePage
(
step
)
{
currentPage
+=
step
;
render
();
window
.
scrollTo
({
top
:
0
,
behavior
:
'smooth'
});
}
renderHistory
();
}
function
changePage
(
step
)
{
currentPage
+=
step
;
render
();
window
.
scrollTo
({
top
:
0
,
behavior
:
'smooth'
});
}
</script>
</script>
</body>
</body>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment