Initial commit

This commit is contained in:
oliviamn 2025-05-15 10:14:45 +08:00
commit 598617025f
4 changed files with 262 additions and 0 deletions

114
app.js Normal file
View File

@ -0,0 +1,114 @@
// --- Configuration ---
// IMPORTANT: For production, use a SEARCH-ONLY API Key.
// NEVER expose your Admin API Key in client-side code.
// Best practice is to proxy requests through your backend
// or use Typesense Scoped Search Keys.
const TYPESENSE_CONFIG = {
nodes: [{
host: '192.168.2.203', // Or your Typesense host
port: '8108',
protocol: 'http'
}],
apiKey: 'imdiowenxodjioqwenlj', // Generate a search-only API key in Typesense
connectionTimeoutSeconds: 2
};
const typesenseClient = new Typesense.Client(TYPESENSE_CONFIG);
// --- DOM Elements ---
const searchInput = document.getElementById('user-search-input');
const suggestionsList = document.getElementById('suggestions-list');
const selectedUserIdEl = document.getElementById('selected-user-id');
const selectedUserNameEl = document.getElementById('selected-user-name');
// --- Debounce Function (to limit API calls) ---
let debounceTimer;
function debounce(func, delay) {
return function(...args) {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => func.apply(this, args), delay);
};
}
// --- Fetch and Display Suggestions ---
async function fetchSuggestions(query) {
if (!query.trim()) {
suggestionsList.innerHTML = '';
suggestionsList.style.display = 'none';
return;
}
const searchParameters = {
'q': query,
'query_by': 'user_name', // Field to search in
'per_page': 5, // Number of suggestions to show
// 'sort_by': '_text_match:desc', // Default, or 'user_name_lowercase_sort:asc' if you use that field
'infix': 'always', // Ensure infix search is active if not default for the field
// 'filter_by': 'is_active:=true' // Example: if you have an is_active boolean field
};
try {
const searchResults = await typesenseClient.collections('users').documents().search(searchParameters);
displaySuggestions(searchResults.hits || []);
} catch (error) {
console.error('Typesense search error:', error);
suggestionsList.innerHTML = '<li>Error fetching suggestions</li>';
suggestionsList.style.display = 'block';
}
}
function displaySuggestions(hits) {
suggestionsList.innerHTML = ''; // Clear previous suggestions
if (hits.length === 0) {
suggestionsList.style.display = 'none';
return;
}
hits.forEach(hit => {
const user = hit.document; // The actual document data
const li = document.createElement('li');
// Create a highlighted version of the name if available
let displayName = user.user_name;
if (hit.highlights && hit.highlights.length > 0) {
const nameHighlight = hit.highlights.find(h => h.field === 'user_name');
if (nameHighlight && nameHighlight.snippet) {
displayName = nameHighlight.snippet; // Use Typesense's highlighted version
}
}
li.innerHTML = displayName; // Display name (can be highlighted)
// Store full user data (or just ID) for selection
li.dataset.userId = user.user_id;
li.dataset.userName = user.user_name; // Store the original, non-highlighted name
li.addEventListener('click', () => {
searchInput.value = user.user_name; // Fill input with selected name
selectedUserIdEl.textContent = user.user_id;
selectedUserNameEl.textContent = user.user_name;
suggestionsList.innerHTML = ''; // Clear suggestions
suggestionsList.style.display = 'none';
// You now have user.user_id and user.user_name
console.log('Selected:', { id: user.user_id, name: user.user_name });
// Here, you might want to submit a form, make another API call, etc.
});
suggestionsList.appendChild(li);
});
suggestionsList.style.display = 'block';
}
// --- Event Listeners ---
searchInput.addEventListener('input', debounce((event) => {
fetchSuggestions(event.target.value);
}, 300)); // 300ms debounce
// Optional: Hide suggestions when clicking outside
document.addEventListener('click', (event) => {
if (!searchInput.contains(event.target) && !suggestionsList.contains(event.target)) {
suggestionsList.style.display = 'none';
}
});
console.log("Autocomplete App Initialized. Make sure your Typesense server is running and accessible.");
console.log("And ensure you've replaced 'YOUR_SEARCH_ONLY_API_KEY' with a valid key.");

42
index.html Normal file
View File

@ -0,0 +1,42 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Typesense Autocomplete</title>
<style>
body { font-family: sans-serif; margin: 20px; }
#user-search-input { padding: 10px; font-size: 16px; width: 300px; margin-bottom: 5px; }
#suggestions-list {
list-style-type: none;
padding: 0;
margin: 0;
border: 1px solid #ccc;
border-top: none;
width: 310px; /* Match input width + padding */
}
#suggestions-list li {
padding: 10px;
cursor: pointer;
}
#suggestions-list li:hover {
background-color: #f0f0f0;
}
#selected-user { margin-top: 20px; }
</style>
</head>
<body>
<h1>User Autocomplete</h1>
<input type="text" id="user-search-input" placeholder="Type a user name...">
<ul id="suggestions-list"></ul>
<div id="selected-user">
<p>Selected User ID: <span id="selected-user-id">N/A</span></p>
<p>Selected User Name: <span id="selected-user-name">N/A</span></p>
</div>
<script src="https://cdn.jsdelivr.net/npm/typesense@2.0.3/dist/typesense.min.js"></script>
<script src="app.js"></script>
</body>
</html>

69
typesense_data_add.py Normal file
View File

@ -0,0 +1,69 @@
# Using Python client for schema creation
import typesense
# Sample user data
users_data = [
{'user_id': 'uid101', 'user_name': 'Alice Wonderland'},
{'user_id': 'uid102', 'user_name': 'Bob The Builder'},
{'user_id': 'uid103', 'user_name': 'Charlie Chaplin'},
{'user_id': 'uid104', 'user_name': 'Diana Prince'},
{'user_id': 'uid105', 'user_name': 'Edward Scissorhands'},
{'user_id': 'uid106', 'user_name': 'alice cooper'}, # For case testing
{'user_id': 'uid107', 'user_name': '任小寅'},
{'user_id': 'uid108', 'user_name': '任大寅'},
{'user_id': 'uid109', 'user_name': '张三'},
{'user_id': 'uid110', 'user_name': '李四'},
{'user_id': 'uid111', 'user_name': '王五'},
{'user_id': 'uid112', 'user_name': '赵六'},
{'user_id': 'uid113', 'user_name': '孙七'},
{'user_id': 'uid114', 'user_name': '周八'},
{'user_id': 'uid115', 'user_name': '吴九'},
{'user_id': 'uid116', 'user_name': '郑十'},
]
users_schema = {
'name': 'users',
'fields': [
{'name': 'user_id', 'type': 'string', 'index': True, 'facet': False},
{'name': 'user_name', 'type': 'string', 'index': True, 'facet': False, 'infix': True},
# Optional: for cleaner prefix sorting if needed, otherwise _text_match often suffices
{'name': 'user_name_lowercase_sort', 'type': 'string', 'sort': True, 'optional': True}
]
}
client = typesense.Client({
'nodes': [{
'host': '192.168.2.203', # Or your Typesense host
'port': '8108',
'protocol': 'http'
}],
'api_key': 'imdiowenxodjioqwenlj', # Use admin key for schema operations
'connection_timeout_seconds': 2
})
# Prepare documents for Typesense (add the optional sort field if you defined it)
documents_to_index = []
for user in users_data:
doc = {
'user_id': user['user_id'],
'user_name': user['user_name'],
}
# If using the optional sort field:
if 'user_name_lowercase_sort' in [f['name'] for f in users_schema['fields']]:
doc['user_name_lowercase_sort'] = user['user_name'].lower()
documents_to_index.append(doc)
# Index the documents (batch import is recommended for larger datasets)
try:
results = client.collections['users'].documents.import_(documents_to_index, {'action': 'upsert'})
print("Documents indexed successfully.")
# print(results) # You can inspect the results if needed
successful_imports = sum(1 for r in results if r.get('success', False))
print(f"Successfully imported {successful_imports}/{len(results)} documents.")
for r in results:
if not r.get('success', False):
print(f"Failed import: {r.get('error', 'Unknown error')} - Document: {r.get('document', '{}')}")
except Exception as e:
print(f"Error indexing documents: {e}")

37
typesense_schema.py Normal file
View File

@ -0,0 +1,37 @@
# Using Python client for schema creation
import typesense
client = typesense.Client({
'nodes': [{
'host': '192.168.2.203', # Or your Typesense host
'port': '8108',
'protocol': 'http'
}],
'api_key': 'imdiowenxodjioqwenlj', # Use admin key for schema operations
'connection_timeout_seconds': 2
})
# Define the schema
users_schema = {
'name': 'users',
'fields': [
{'name': 'user_id', 'type': 'string', 'index': True, 'facet': False},
{'name': 'user_name', 'type': 'string', 'index': True, 'facet': False, 'infix': True},
# Optional: for cleaner prefix sorting if needed, otherwise _text_match often suffices
{'name': 'user_name_lowercase_sort', 'type': 'string', 'sort': True, 'optional': True}
]
}
# Delete collection if it already exists (for development)
try:
client.collections['users'].delete()
print("Collection 'users' deleted.")
except Exception as e:
print(f"Collection 'users' does not exist or could not be deleted: {e}")
# Create the collection
try:
client.collections.create(users_schema)
print("Collection 'users' created successfully.")
except Exception as e:
print(f"Error creating collection 'users': {e}")