Skip to content

Commit 1990e00

Browse files
committed
Improve dashboard_services search relevance
i have improved the dashboard_services engine by implementing a weighted scoring system to filter and rank services based on query relevance. This includes matches in name, description, server, container, and group. Additionally, we have added support for displaying service icons in results and refactored the code for better clarity and maintainability.
1 parent 12b02b0 commit 1990e00

File tree

1 file changed

+99
-44
lines changed

1 file changed

+99
-44
lines changed

python/searxng-addons/dashboard_services.py

Lines changed: 99 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
1. Copy file to `/usr/local/searxng/searx/engines/dashboard_services.py`
88
2. Add the following configuration to your `settings.yml` file:
99
10-
```yaml
10+
\```yaml
1111
engines:
1212
- name: selfhosted
1313
engine: dashboard_services
@@ -18,7 +18,7 @@
1818
enable_http: true
1919
enable_http2: true
2020
weight: 0.5 # Higher priority than regular search engines
21-
```
21+
\```
2222
2323
For use with https://github.com/searxng/searxng
2424
"""
@@ -40,8 +40,8 @@
4040
}
4141

4242
# Engine configuration
43-
engine_type = "online"
44-
categories = ["general"]
43+
engine_type = 'online'
44+
categories = ['general']
4545
disabled = False
4646
timeout = 10.0
4747
paging = False
@@ -50,21 +50,26 @@
5050
# API endpoint
5151
base_url = f"{HOMEPAGE_BASE_URL}/api/services"
5252

53+
# Store the current query
54+
_current_query = ""
5355

5456
def request(query, params):
5557
"""Build the request parameters for the dashboard services API."""
56-
params["url"] = base_url
57-
params["method"] = "GET"
58-
params["headers"] = {
59-
"Accept": "application/json",
60-
"User-Agent": "SearXNG Dashboard Services Engine",
58+
global _current_query
59+
_current_query = query.lower() # Store for filtering in response
60+
61+
params['url'] = base_url
62+
params['method'] = 'GET'
63+
params['headers'] = {
64+
'Accept': 'application/json',
65+
'User-Agent': 'SearXNG Dashboard Services Engine'
6166
}
6267
return params
6368

64-
65-
def response(resp) -> EngineResults:
69+
def response(resp):
6670
"""Parse the API response and return search results."""
67-
results = EngineResults()
71+
global _current_query
72+
results = []
6873

6974
try:
7075
# Check if response is empty
@@ -75,49 +80,90 @@ def response(resp) -> EngineResults:
7580
# Parse JSON response
7681
json_data = loads(resp.text)
7782

78-
# Get query from the original request (simplified)
79-
query = ""
80-
if hasattr(resp, "url") and "?" in str(resp.url):
81-
# Try to extract query from URL params if available
82-
pass
83+
# Get the query for filtering
84+
query = _current_query
85+
if not query:
86+
print("Dashboard Services Engine: No query available")
87+
return results # No query, no results
88+
89+
# Collect all matching services with their scores
90+
matched_services = []
8391

8492
# Process each group in the response
8593
for group in json_data:
86-
group_name = group.get("name", "Unknown Group")
94+
group_name = group.get('name', 'Unknown Group')
8795

8896
# Process direct services
89-
if "services" in group:
90-
for service in group["services"]:
91-
results.append(_create_service_result(service, group_name))
97+
if 'services' in group:
98+
for service in group['services']:
99+
score = _calculate_match_score(service, group_name, query)
100+
if score > 0: # Only include if there's a match
101+
matched_services.append({
102+
'service': service,
103+
'group_name': group_name,
104+
'score': score
105+
})
92106

93107
# Process nested groups
94-
if "groups" in group:
95-
for subgroup in group["groups"]:
96-
subgroup_name = subgroup.get("name", "Unknown Subgroup")
97-
if "services" in subgroup:
98-
for service in subgroup["services"]:
99-
results.append(
100-
_create_service_result(
101-
service, f"{group_name} > {subgroup_name}"
102-
)
103-
)
108+
if 'groups' in group:
109+
for subgroup in group['groups']:
110+
subgroup_name = subgroup.get('name', 'Unknown Subgroup')
111+
if 'services' in subgroup:
112+
for service in subgroup['services']:
113+
score = _calculate_match_score(service, f"{group_name} > {subgroup_name}", query)
114+
if score > 0: # Only include if there's a match
115+
matched_services.append({
116+
'service': service,
117+
'group_name': f"{group_name} > {subgroup_name}",
118+
'score': score
119+
})
120+
121+
# Sort by score (highest first)
122+
matched_services.sort(key=lambda x: x['score'], reverse=True)
123+
124+
# Create results from sorted matches
125+
for match in matched_services:
126+
results.append(_create_service_result(match['service'], match['group_name']))
104127

105128
except Exception as e:
106129
print(f"Dashboard Services Engine Error: {e}")
107-
print(
108-
f"Response content: {resp.text[:200]}..."
109-
) # Show first 200 chars for debugging
130+
print(f"Response content: {resp.text[:200]}...") # Show first 200 chars for debugging
110131

111132
return results
112133

134+
def _calculate_match_score(service, group_name, query):
135+
"""Calculate a relevance score based on where the query matches."""
136+
score = 0
137+
138+
# Get the values to check, converting to lowercase and handling None values
139+
name = (service.get('name', '') or '').lower()
140+
description = (service.get('description', '') or '').lower()
141+
server = (service.get('server', '') or '').lower()
142+
container = (service.get('container', '') or '').lower()
143+
group_name = (group_name or '').lower()
144+
145+
# Check for matches in different fields with different weights
146+
if query in name:
147+
score += 10 # Highest weight for name match
148+
if query in description:
149+
score += 5 # Medium weight for description match
150+
if query in server:
151+
score += 2 # Lower weight for server/container matches
152+
if query in container:
153+
score += 2
154+
if query in group_name:
155+
score += 3 # Medium-low weight for group match
156+
157+
return score
113158

114159
def _create_service_result(service, group_name):
115160
"""Create a search result from a service object."""
116-
name = service.get("name", "Unknown Service")
117-
description = service.get("description", "No description available")
118-
href = service.get("href", "#")
119-
server = service.get("server", "")
120-
container = service.get("container", "")
161+
name = service.get('name', 'Unknown Service')
162+
description = service.get('description', 'No description available')
163+
href = service.get('href', '#')
164+
server = service.get('server', '')
165+
container = service.get('container', '')
166+
icon = service.get('icon', '')
121167

122168
# Simple content creation
123169
content = description
@@ -126,9 +172,18 @@ def _create_service_result(service, group_name):
126172
if container:
127173
content += f" | Container: {container}"
128174

129-
return {
130-
"url": href,
131-
"title": f"{name} ({group_name})",
132-
"content": content,
133-
"category": "dashboard_services",
175+
result = {
176+
'url': href,
177+
'title': f"{name} ({group_name})",
178+
'content': content,
134179
}
180+
181+
# Add icon if available
182+
if icon:
183+
if icon.startswith('http'):
184+
result['img_src'] = icon
185+
elif icon.startswith('/'):
186+
# Local icon path
187+
result['img_src'] = f"{HOMEPAGE_BASE_URL}{icon}"
188+
189+
return result

0 commit comments

Comments
 (0)