進階
NIP-50 搜尋
深入了解 Nostr 的搜尋功能,使用 search 過濾器在中繼器上進行全文搜尋。
8 分鐘
什麼是 NIP-50?
NIP-50 定義了 Nostr 中繼器的搜尋功能,在 REQ 過濾器中加入 search 欄位, 讓客戶端可以進行全文搜尋。這是一個可選功能,需要中繼器支援(在 NIP-11 中宣告)。
注意: 不是所有中繼器都支援 NIP-50。在使用搜尋功能前, 先檢查中繼器的 NIP-11 資訊是否包含 50 在 supported_nips 中。
過濾器格式
// 基本搜尋
["REQ", "search-sub", {
"search": "bitcoin lightning",
"kinds": [1],
"limit": 50
}]
// search 欄位會搜尋事件的 content
// 可以與其他過濾器組合使用 搜尋語法
| 語法 | 說明 | 範例 |
|---|---|---|
| word | 單詞匹配 | bitcoin |
| word1 word2 | AND(必須包含所有詞) | bitcoin lightning |
| "phrase" | 精確短語 | "hello world" |
| -word | 排除詞 | bitcoin -scam |
擴展語法
部分中繼器支援額外的搜尋修飾符:
// 搜尋特定語言
{
"search": "bitcoin language:zh"
}
// 搜尋特定領域(由中繼器定義)
{
"search": "bitcoin domain:finance"
}
// 搜尋特定情感(部分中繼器支援)
{
"search": "bitcoin sentiment:positive"
}
// 注意:擴展語法由各中繼器自行定義
// 使用前請查閱中繼器文件 程式碼範例
基本搜尋
async function searchNotes(relay, query, limit = 50) {
return new Promise((resolve) => {
const results = []
const sub = relay.subscribe([{
kinds: [1],
search: query,
limit: limit
}])
sub.on('event', (event) => {
results.push(event)
})
sub.on('eose', () => {
sub.close()
resolve(results)
})
})
}
// 使用
const notes = await searchNotes(relay, 'bitcoin lightning')
console.log(`找到 ${notes.length} 條結果`) 組合過濾器
// 搜尋特定用戶的內容
{
"kinds": [1],
"authors": ["pubkey1", "pubkey2"],
"search": "bitcoin",
"limit": 100
}
// 搜尋特定時間範圍
{
"kinds": [1],
"search": "announcement",
"since": 1704067200, // 2024-01-01
"until": 1706745600, // 2024-02-01
"limit": 50
}
// 搜尋長文章
{
"kinds": [30023],
"search": "tutorial",
"limit": 20
} 檢查中繼器支援
async function findSearchRelays(relayUrls) {
const searchRelays = []
for (const url of relayUrls) {
const info = await getRelayInfo(url)
if (info?.supported_nips?.includes(50)) {
searchRelays.push({
url,
name: info.name || url
})
}
}
return searchRelays
}
// 只在支援搜尋的中繼器上搜尋
async function smartSearch(relayUrls, query) {
const searchRelays = await findSearchRelays(relayUrls)
if (searchRelays.length === 0) {
throw new Error('沒有找到支援搜尋的中繼器')
}
console.log(`在 ${searchRelays.length} 個中繼器上搜尋`)
// 並行搜尋所有中繼器
const results = await Promise.all(
searchRelays.map(r => searchNotes(r.url, query))
)
// 合併並去重
return deduplicateEvents(results.flat())
} 支援搜尋的中繼器
relay.nostr.band
專業搜尋中繼器
全文搜尋、趨勢nostr.wine
付費中繼器
搜尋、過濾relay.snort.social
Snort 官方中繼器
基本搜尋搜尋結果處理
// 搜尋結果可能包含相關性分數
// 這是中繼器特定的擴展
function sortByRelevance(events) {
// 如果中繼器提供了排序,結果通常已按相關性排序
// 否則可以按時間排序
return events.sort((a, b) => b.created_at - a.created_at)
}
function highlightMatches(content, query) {
const words = query.split(' ').filter(w => !w.startsWith('-'))
let highlighted = content
for (const word of words) {
const regex = new RegExp(`(${word})`, 'gi')
highlighted = highlighted.replace(regex, '<mark>$1</mark>')
}
return highlighted
}
// 顯示搜尋結果
function displayResults(events, query) {
for (const event of events) {
console.log('---')
console.log('ID:', event.id.slice(0, 8))
console.log('內容:', highlightMatches(event.content, query))
console.log('時間:', new Date(event.created_at * 1000))
}
} 限制與考量
技術限制
- • 不是所有中繼器都支援
- • 搜尋語法可能不同
- • 索引可能不完整
- • 結果數量有限制
隱私考量
- • 搜尋詞會發送到中繼器
- • 中繼器可能記錄搜尋
- • 考慮使用多個中繼器分散
- • 敏感搜尋要謹慎
與本地搜尋比較
| 特性 | NIP-50 搜尋 | 本地搜尋 |
|---|---|---|
| 範圍 | 中繼器上所有事件 | 本地快取的事件 |
| 速度 | 取決於網路和中繼器 | 通常更快 |
| 隱私 | 搜尋詞會暴露 | 完全私密 |
| 可用性 | 需要中繼器支援 | 總是可用 |
最佳實踐: 結合使用 NIP-50 搜尋和本地搜尋。對於發現新內容使用中繼器搜尋, 對於已關注用戶的內容使用本地搜尋。
已複製連結