This repository has been archived by the owner on Dec 29, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.js
158 lines (150 loc) · 4.02 KB
/
search.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/*
* prefix [+search+] to all logs
*/
const sLog = function() {
args = [];
args.push('[+search+] ');
// Note: arguments is part of the prototype
for(let i = 0; i < arguments.length; i++) {
args.push(arguments[i]);
}
console.log.apply(console, args);
};
/*
* loads queries.json file if it exists
* returns true if it is loaded, false otherwise
*/
async function loadQueries() {
if (ipfsearch.queries !== undefined) {
if (ipfsearch.queries === 0) {
return false;
}
return true;
}
try {
await fs.statSync(`./${ipfs.host}/queries.json`);
} catch (e) {
ipfsearch.queries = 0;
return false;
}
const queriesFile = await fs.readFileSync(`./${ipfs.host}/queries.json`, 'utf8');
ipfsearch.queries = JSON.parse(queriesFile);
return true;
}
/*
* determine topic of query
* in practice we have to determine this ourselves
*/
async function getTopic(query) {
if (await loadQueries()){
const topic = ipfsearch.queries.topics[query];
if (topic !== undefined) {
return topic;
}
}
sLog(`Error: Can't determine topic of ${query}`);
}
/*
* search in an index
*/
async function searchIndex(topic, query) {
if (global.indices[topic] === undefined) {
// we don't have that topic locally
return [];
}
stats.localSearches += 1;
return global.indices[topic].search(query);
}
/*
* search in the network
*/
async function searchNetwork(topic, query) {
stats.onlineSearches += 1;
const ownerId = Listener.sub(topic);
// tell listener to catch results
Listener.listenFor(topic, query);
// ask the network
Publisher.pubQuery(topic, query);
// wait for an answer
await util.timeout(cfg.searchWait);
// unsub
await Listener.unsub(topic, ownerId);
// gather results
return Listener.stopListening(topic, query);
}
/*
* search for a given query
*/
async function search(query, score) {
sLog(`Searching '${query}' with a minimum score of ${score} ...`);
stats.queries += 1;
// search locally
const topic = await getTopic(query);
let results = await searchIndex(topic, query);
if (results.length > 0 && results[0].score >= score) {
sLog(`Local results for '${query}' were sufficient.`);
} else {
const before = results.length;
sLog(`Local results (${before}) for '${query}' insufficient, asking the network ...`);
results = results.concat(await searchNetwork(topic, query));
sLog(`Received ${results.length - before} additional results from the network`);
}
return results;
}
/*
* some quick testing of our search functionality
*/
async function searchTests() {
if (!await loadQueries()) {
return; // no queries to do
}
const searches = ipfsearch.queries.queries.map(async q => {
const r = await search(q.q, q.s);
stats.searches.push({'q': q.q, 's': q.s, 'r': r});
return r;
});
await Promise.all(searches)
.then(results => {
sLog('Searches finished.');
});
}
/*
* determine which channels to join
*/
async function getRelevantTopics() {
if (cfg.maxChannels < 1) {
sLog(`Error: maxChannels too low (${cfg.maxChannels})`);
}
const contents = await fs.readdirSync(`./${ipfs.host}/`);
const counts = [];
await Promise.all(contents.map(async (content) => {
if (fs.statSync(`./${ipfs.host}/${content}`).isDirectory()) {
const files = await fs.readdirSync(`./${ipfs.host}/${content}/`);
counts.push({'t': content, 'c': files.length});
}
}));
let total = 0;
for (i in counts) {
total += counts[i].c;
}
sLog(`Hosting ${total} files ...`);
counts.sort((a,b) => {return b.c-a.c});
const r = [];
for (i in counts) {
if (counts[i].c >= total*cfg.topicThreshold) {
r.push(counts[i].t);
}
if (r.length >= cfg.maxChannels) {
break;
}
}
if (r.length === 0) {
sLog(`Error: topicThreshold (${cfg.topicThreshold}) not met for any topic.`);
}
stats.providedSearches = r;
return r;
}
module.exports.searchIndex = searchIndex;
module.exports.search = search;
module.exports.searchTests = searchTests;
module.exports.getRelevantTopics = getRelevantTopics;