-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.cpp
More file actions
243 lines (208 loc) · 6.61 KB
/
Copy pathindex.cpp
File metadata and controls
243 lines (208 loc) · 6.61 KB
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <chrono>
using namespace std;
const int MAX_BUF_POSTINGS = 128;
struct PostingEntry
{
string term;
int docId;
int freq;
};
struct BlockMetadata
{
uint32_t lastDocId;
uint32_t docSize; // compressed doc size
uint32_t freqSize; // compressed freq size
};
struct LexiconEntry
{
uint32_t startBlock; // which block term starts in
uint32_t startIndex; // which index within block term start (0-127)
uint32_t listLength; // total postings for the term
};
struct Block
{ // each of size 128 docIds, and 128 freqs
vector<int> docIds;
vector<int> freqs; // freq for corresponding docIds
void clear()
{
docIds.clear();
freqs.clear();
}
};
// VARBYTE ENCODING
void writeByte(vector<unsigned char> &buffer, uint8_t val) // 8 byte num
{
buffer.push_back(val);
}
void varbyteEncode(vector<unsigned char> &buffer, uint32_t num)
{
while (num >= 128)
{
writeByte(buffer, 128 + (num & 127)); // set the 1 and then the next 7 bits
num >>= 7; // right shift by 7 bits
}
writeByte(buffer, static_cast<uint8_t>(num)); // without the 1 bit at the front
}
// INVERTED INDEX + LEXICON
bool readNextRecord(ifstream &ifs, PostingEntry &p)
{
uint32_t termLen;
if (!ifs.read(reinterpret_cast<char *>(&termLen), sizeof(termLen)))
{
return false;
}
if (termLen == 0)
{
return false;
}
string term(termLen, '\0');
if (!ifs.read(&term[0], termLen))
{
return false;
}
int docId, freq;
if (!ifs.read(reinterpret_cast<char *>(&docId), sizeof(docId)))
{
return false;
}
if (!ifs.read(reinterpret_cast<char *>(&freq), sizeof(freq)))
{
return false;
}
p = PostingEntry{term, docId, freq};
return true;
}
void writeLexiconEntry(ofstream &ofs, const string &term, LexiconEntry &entry)
{
uint32_t termSize = term.size();
ofs.write(reinterpret_cast<char *>(&termSize), sizeof(termSize));
ofs.write(term.data(), termSize);
ofs.write(reinterpret_cast<char *>(&entry), sizeof(LexiconEntry));
}
// compress 1 block of docIDs and 1 block of freqs
// append metadata
// increment blockCount
void compressBlock(ofstream &ofs, const Block &block, vector<unsigned char> &buffer, vector<BlockMetadata> &metadata, uint32_t &blockCount)
{
buffer.clear();
// compress and write docIds
// use delta then varbyte !!
int prevDocId = 0;
for (int docId : block.docIds)
{
uint32_t delta = docId - prevDocId;
varbyteEncode(buffer, delta);
prevDocId = docId;
}
// write to output buffer
ofs.write(reinterpret_cast<char *>(buffer.data()), buffer.size());
uint32_t lastDocId = static_cast<uint32_t>(block.docIds.back());
uint32_t docByteCount = static_cast<uint32_t>(buffer.size());
buffer.clear();
// compress and write freqs
for (int freq : block.freqs)
{
varbyteEncode(buffer, static_cast<uint32_t>(freq));
}
ofs.write(reinterpret_cast<char *>(buffer.data()), buffer.size());
uint32_t freqByteCount = static_cast<uint32_t>(buffer.size());
buffer.clear();
// record metadata - one entry per block
BlockMetadata blockInfo{lastDocId, docByteCount, freqByteCount};
metadata.push_back(blockInfo);
++blockCount;
}
void generateInvertedIndex()
{
string inFilename = "final_merged.bin";
string outFilename = "compressed_inverted_index.bin";
string lexiconFilename = "lexicon.bin";
string metadataFilename = "metadata.bin";
ifstream ifs(inFilename, ios::binary);
if (!ifs)
{
cerr << "Failed to open " << inFilename << endl;
exit(1);
}
// inverted index
ofstream ofs(outFilename, ios::binary);
ofstream lexicon(lexiconFilename, ios::binary);
ofstream metadataOut(metadataFilename, ios::binary);
string currentTerm;
Block block;
vector<unsigned char> buffer; // temp buffer for the term docids/freqs
vector<BlockMetadata> metadata;
uint32_t blockCount = 0; // completed blocks
uint32_t termStartBlock = 0; // block index where current term started
uint32_t termStartIndex = 0; // byte offset within doc-area of block
uint32_t termPostingCount = 0; // total postings for current term
bool haveOnePosting = false;
PostingEntry p;
while (readNextRecord(ifs, p))
{
if (!haveOnePosting)
{
// first posting ever -> initialize term tracking
currentTerm = p.term;
termStartBlock = blockCount;
termStartIndex = static_cast<uint32_t>(block.docIds.size());
termPostingCount = 0;
haveOnePosting = true;
}
if (p.term != currentTerm) // does not include first ever posting
{
// new term! prev term finished -> write lexicon entry using termStartBlock/termStartIndex/termPostingCount
LexiconEntry entry{termStartBlock, termStartIndex, termPostingCount};
writeLexiconEntry(lexicon, currentTerm, entry);
// reset for new term
currentTerm = p.term;
termStartBlock = blockCount;
termStartIndex = static_cast<uint32_t>(block.docIds.size());
termPostingCount = 0;
}
block.docIds.push_back(p.docId);
block.freqs.push_back(p.freq);
++termPostingCount;
// flush block if full
if (block.docIds.size() == MAX_BUF_POSTINGS)
{
// write block
compressBlock(ofs, block, buffer, metadata, blockCount);
block.clear();
}
}
// final flush
if (haveOnePosting)
{
if (!block.docIds.empty())
{ // still have remaining but not full block
compressBlock(ofs, block, buffer, metadata, blockCount);
block.clear();
}
// write lexicon for last term
LexiconEntry entry{termStartBlock, termStartIndex, termPostingCount};
writeLexiconEntry(lexicon, currentTerm, entry);
}
// write metadata
if (!metadata.empty())
{
metadataOut.write(reinterpret_cast<char *>(metadata.data()), metadata.size() * sizeof(BlockMetadata));
}
// close files
ofs.close();
lexicon.close();
metadataOut.close();
}
int main()
{
using namespace std::chrono;
auto startTime = high_resolution_clock::now();
generateInvertedIndex();
auto endTime = high_resolution_clock::now();
auto duration = duration_cast<milliseconds>(endTime - startTime).count();
std::cout << "Elapsed time: " << duration << " ms" << std::endl;
}