1 : #include <ept/debtags/maint/vocabularyindexer.h>
2 : #include <ept/debtags/vocabulary.h>
3 : #include <ept/debtags/maint/vocabularymerger.h>
4 : #include <ept/debtags/maint/path.h>
5 :
6 : namespace ept {
7 : namespace debtags {
8 :
9 31 : VocabularyIndexer::VocabularyIndexer()
10 31 : : mainSource(Path::debtagsSourceDir()), userSource(Path::debtagsUserSourceDir())
11 : {
12 31 : rescan();
13 31 : }
14 :
15 31 : void VocabularyIndexer::rescan()
16 : {
17 31 : ts_main_src = mainSource.vocTimestamp();
18 31 : ts_user_src = userSource.vocTimestamp();
19 31 : ts_main_voc = Path::timestamp(Path::vocabulary());
20 62 : ts_main_idx = Path::timestamp(Path::vocabularyIndex());
21 62 : ts_user_voc = Path::timestamp(Path::userVocabulary());
22 62 : ts_user_idx = Path::timestamp(Path::userVocabularyIndex());
23 31 : }
24 :
25 31 : bool VocabularyIndexer::needsRebuild() const
26 : {
27 : // If there are no indexes of any kind, then we need rebuilding
28 31 : if (ts_user_voc == 0 && ts_user_idx == 0 && ts_main_voc == 0 && ts_main_idx == 0)
29 3 : return true;
30 :
31 : // If the user index is ok, then we are fine
32 28 : if (ts_user_voc >= sourceTimestamp() && ts_user_idx >= sourceTimestamp())
33 28 : return false;
34 :
35 : // If there are user sources, then we cannot use the system index
36 0 : if (ts_user_src > 0)
37 0 : return true;
38 :
39 : // If there are no user sources, then we can fallback on the system
40 : // indexes in case the user indexes are not up to date
41 0 : if (ts_main_voc >= sourceTimestamp() && ts_main_idx >= sourceTimestamp())
42 0 : return false;
43 :
44 0 : return true;
45 : }
46 :
47 31 : bool VocabularyIndexer::userIndexIsRedundant() const
48 : {
49 : // If there is no user index, then it is not redundant
50 31 : if (ts_user_voc == 0 && ts_user_idx == 0)
51 2 : return false;
52 :
53 : // If we have user sources, then the user index is never redundant
54 29 : if (ts_user_src > 0)
55 29 : return false;
56 :
57 : // If the system index is not up to date, then the user index is not
58 : // redundant
59 0 : if (ts_main_voc < sourceTimestamp() || ts_main_idx < sourceTimestamp())
60 0 : return false;
61 :
62 0 : return true;
63 : }
64 :
65 3 : bool VocabularyIndexer::rebuild(const std::string& vocfname, const std::string& idxfname)
66 : {
67 : using namespace tagcoll;
68 :
69 : // Create the master MMap index
70 3 : diskindex::MasterMMapIndexer master(idxfname);
71 :
72 : // Read and merge vocabulary data
73 3 : VocabularyMerger voc;
74 3 : mainSource.readVocabularies(voc);
75 3 : userSource.readVocabularies(voc);
76 :
77 3 : if (voc.empty())
78 2 : return false;
79 : //throw wibble::exception::Consistency("Reading debtags sources from " + mainSource.path() + " and " + userSource.path(), "Unable to find any vocabulary data");
80 :
81 : // Write the merged vocabulary, and generate tag and facet IDs as a side
82 : // effect
83 1 : std::string tmpvocfname = vocfname + ".tmp";
84 1 : voc.write(tmpvocfname);
85 :
86 : // Add the indexed vocabulary data to the master index
87 : // 0: facets
88 1 : master.append(voc.facetIndexer());
89 : // 1: tags
90 1 : master.append(voc.tagIndexer());
91 :
92 1 : if (rename(tmpvocfname.c_str(), vocfname.c_str()) == -1)
93 0 : throw wibble::exception::System("renaming " + tmpvocfname + " to " + vocfname);
94 :
95 1 : master.commit();
96 1 : return true;
97 : }
98 :
99 31 : bool VocabularyIndexer::rebuildIfNeeded()
100 : {
101 31 : if (needsRebuild())
102 : {
103 : // Decide if we rebuild the user index or the system index
104 :
105 3 : if (ts_user_src == 0 && Path::access(Path::debtagsIndexDir(), W_OK) == 0)
106 : {
107 : // There are no user sources and we can write to the system index
108 : // directory: rebuild the system index
109 1 : if (!rebuild(Path::vocabulary(), Path::vocabularyIndex()))
110 1 : return false;
111 0 : ts_main_voc = Path::timestamp(Path::vocabulary());
112 0 : ts_main_idx = Path::timestamp(Path::vocabularyIndex());
113 0 : if (Path::vocabulary() == Path::userVocabulary())
114 0 : ts_user_voc = ts_main_voc;
115 0 : if (Path::vocabularyIndex() == Path::userVocabularyIndex())
116 0 : ts_user_idx = ts_main_idx;
117 : } else {
118 2 : wibble::sys::fs::mkFilePath(Path::userVocabulary());
119 4 : wibble::sys::fs::mkFilePath(Path::userVocabularyIndex());
120 4 : if (!rebuild(Path::userVocabulary(), Path::userVocabularyIndex()))
121 1 : return false;
122 1 : ts_user_voc = Path::timestamp(Path::userVocabulary());
123 2 : ts_user_idx = Path::timestamp(Path::userVocabularyIndex());
124 : }
125 1 : return true;
126 : }
127 28 : return false;
128 : }
129 :
130 31 : bool VocabularyIndexer::deleteRedundantUserIndex()
131 : {
132 31 : if (userIndexIsRedundant())
133 : {
134 : // Delete the user indexes if they exist
135 0 : if (Path::vocabulary() != Path::userVocabulary())
136 : {
137 0 : unlink(Path::userVocabulary().c_str());
138 0 : ts_user_voc = 0;
139 : }
140 0 : if (Path::vocabularyIndex() != Path::userVocabularyIndex())
141 : {
142 0 : unlink(Path::userVocabularyIndex().c_str());
143 0 : ts_user_idx = 0;
144 : }
145 0 : return true;
146 : }
147 31 : return false;
148 : }
149 :
150 31 : bool VocabularyIndexer::getUpToDateVocabulary(std::string& vocfname, std::string& idxfname)
151 : {
152 : // If there are no indexes of any kind, then we have nothing to return
153 31 : if (ts_user_voc == 0 && ts_user_idx == 0 && ts_main_voc == 0 && ts_main_idx == 0)
154 2 : return false;
155 :
156 : // If the user index is up to date, use it
157 29 : if (ts_user_voc >= sourceTimestamp() &&
158 : ts_user_idx >= sourceTimestamp())
159 : {
160 29 : vocfname = Path::userVocabulary();
161 58 : idxfname = Path::userVocabularyIndex();
162 29 : return true;
163 : }
164 :
165 : // If the user index is not up to date and we have user sources, we cannot
166 : // fall back to the system index
167 0 : if (ts_user_src != 0)
168 0 : return false;
169 :
170 : // Fallback to the system index
171 0 : if (ts_main_voc >= sourceTimestamp() &&
172 : ts_main_idx >= sourceTimestamp())
173 : {
174 0 : vocfname = Path::vocabulary();
175 0 : idxfname = Path::vocabularyIndex();
176 0 : return true;
177 : }
178 :
179 0 : return false;
180 : }
181 :
182 :
183 31 : bool VocabularyIndexer::obtainWorkingVocabulary(std::string& vocfname, std::string& idxfname)
184 : {
185 31 : VocabularyIndexer v;
186 :
187 31 : v.rebuildIfNeeded();
188 31 : v.deleteRedundantUserIndex();
189 31 : return v.getUpToDateVocabulary(vocfname, idxfname);
190 : }
191 :
192 : }
193 6 : }
194 :
195 : // vim:set ts=4 sw=4:
|