1use {
2 crate::{
3 benchmark::{BenchmarkConfig, BenchmarkMetadata, SubTask, Task},
4 downloader::{DownloadMethod, DownloadSpec},
5 utils,
6 },
7 std::path::Path,
8};
9
10pub struct OxfordBenchmark;
16
17static METADATA: BenchmarkMetadata = BenchmarkMetadata {
18 name: "Oxford Dataset",
19 description: "Oxford Database Systems and Implementation final course exam",
20 download_spec: DownloadSpec {
21 name: "oxford_dataset",
22 method: DownloadMethod::CLONE,
23 url: "https://github.com/schroederdewitt/leapfrog-triejoin",
24 },
25 tasks: &[
26 Task {
27 name: "Uniform",
28 description: "Uniformly distributed data",
29 subtasks: &[
30 SubTask {
31 name: "Scale 1",
32 data_paths: &["data/dataset1-uniform/scale1"],
33 query_paths: &[
34 "queries/query1.txt",
35 "queries/query2.txt",
36 "queries/query3.txt",
37 ],
38 description: "Scale 1 Dataset",
39 },
40 SubTask {
41 name: "Scale 2",
42 data_paths: &["data/dataset1-uniform/scale2"],
43 query_paths: &[
44 "queries/query1.txt",
45 "queries/query2.txt",
46 "queries/query3.txt",
47 ],
48 description: "Scale 2 Dataset",
49 },
50 SubTask {
51 name: "Scale 3",
52 data_paths: &["data/dataset1-uniform/scale3"],
53 query_paths: &[
54 "queries/query1.txt",
55 "queries/query2.txt",
56 "queries/query3.txt",
57 ],
58 description: "Scale 3 Dataset",
59 },
60 SubTask {
61 name: "Scale 4",
62 data_paths: &["data/dataset1-uniform/scale4"],
63 query_paths: &[
64 "queries/query1.txt",
65 "queries/query2.txt",
66 "queries/query3.txt",
67 ],
68 description: "Scale 4 Dataset",
69 },
70 SubTask {
71 name: "Scale 5",
72 data_paths: &["data/dataset1-uniform/scale5"],
73 query_paths: &[
74 "queries/query1.txt",
75 "queries/query2.txt",
76 "queries/query3.txt",
77 ],
78 description: "Scale 5 Dataset",
79 },
80 SubTask {
81 name: "Scale 6",
82 data_paths: &["data/dataset1-uniform/scale6"],
83 query_paths: &[
84 "queries/query1.txt",
85 "queries/query2.txt",
86 "queries/query3.txt",
87 ],
88 description: "Scale 6 Dataset",
89 },
90 ],
91 },
92 Task {
93 name: "Zipf",
94 description: "Zipf distributed data",
95 subtasks: &[
96 SubTask {
97 name: "Scale 1",
98 data_paths: &["data/dataset2-zipf/scale1"],
99 query_paths: &[
100 "queries/query1.txt",
101 "queries/query2.txt",
102 "queries/query3.txt",
103 ],
104 description: "Scale 1 Dataset",
105 },
106 SubTask {
107 name: "Scale 2",
108 data_paths: &["data/dataset2-zipf/scale2"],
109 query_paths: &[
110 "queries/query1.txt",
111 "queries/query2.txt",
112 "queries/query3.txt",
113 ],
114 description: "Scale 2 Dataset",
115 },
116 SubTask {
117 name: "Scale 3",
118 data_paths: &["data/dataset2-zipf/scale3"],
119 query_paths: &[
120 "queries/query1.txt",
121 "queries/query2.txt",
122 "queries/query3.txt",
123 ],
124 description: "Scale 3 Dataset",
125 },
126 SubTask {
127 name: "Scale 4",
128 data_paths: &["data/dataset2-zipf/scale4"],
129 query_paths: &[
130 "queries/query1.txt",
131 "queries/query2.txt",
132 "queries/query3.txt",
133 ],
134 description: "Scale 4 Dataset",
135 },
136 SubTask {
137 name: "Scale 5",
138 data_paths: &["data/dataset2-zipf/scale5"],
139 query_paths: &[
140 "queries/query1.txt",
141 "queries/query2.txt",
142 "queries/query3.txt",
143 ],
144 description: "Scale 5 Dataset",
145 },
146 SubTask {
147 name: "Scale 6",
148 data_paths: &["data/dataset2-zipf/scale6"],
149 query_paths: &[
150 "queries/query1.txt",
151 "queries/query2.txt",
152 "queries/query3.txt",
153 ],
154 description: "Scale 6 Dataset",
155 },
156 ],
157 },
158 ],
159};
160
161fn translate_dataset(source: &Path, dest: &Path) -> Result<(), Box<dyn std::error::Error>> {
162 let header_path = source.join("databasefile");
163 let headers: Vec<String> = std::fs::read_to_string(&header_path)
164 .map_err(|e| format!("Failed to read header file {}: {e}", header_path.display()))?
165 .lines()
166 .map(|s| s.to_string())
167 .collect();
168
169 for header in headers {
170 let header_content: Vec<String> = header.split(",").map(|s| s.trim().to_string()).collect();
171
172 let rel_file_name = &header_content[0];
173 let rel_name = &header_content[1];
174 let rel_attrs: Vec<String> = header_content[2..]
175 .iter()
176 .map(|s| s.trim().to_string())
177 .filter(|s| !s.is_empty())
178 .collect();
179
180 let data_path = source.join(rel_file_name);
181
182 let raw = std::fs::read_to_string(&data_path)
183 .map_err(|e| format!("Failed to read data file {}: {e}", data_path.display()))?;
184 let data_content: Vec<Vec<usize>> = raw
185 .trim()
186 .lines()
187 .map(|line| {
188 line.split(",")
189 .take(rel_attrs.len())
190 .map(|s| {
191 s.trim().parse::<usize>().map_err(|e| {
192 format!(
193 "Failed to parse value {:?} in {}: {e}",
194 s.trim(),
195 data_path.display()
196 )
197 })
198 })
199 .collect::<Result<Vec<_>, _>>()
200 })
201 .collect::<Result<Vec<_>, _>>()?;
202
203 let parquet_path = dest.join(format!("{}.parquet", rel_name));
204 utils::write_relation_to_parquet(&parquet_path, &rel_attrs, &data_content)
205 .map_err(|e| format!("Failed to write {}: {e}", parquet_path.display()))?;
206 }
207 Ok(())
208}
209
210fn translate_query(source: &Path, dest: &Path) -> Result<(), Box<dyn std::error::Error>> {
211 let query_content: Vec<_> = std::fs::read_to_string(source)
212 .map_err(|e| format!("Failed to read query file {}: {e}", source.display()))?
213 .lines()
214 .map(|s| s.to_string())
215 .collect();
216 let rels = query_content[0]
217 .split(",")
218 .filter(|s| !s.is_empty())
219 .map(|s| s.trim())
220 .collect::<Vec<_>>();
221 let attrs = query_content[1]
222 .split(",")
223 .filter(|s| !s.is_empty())
224 .map(|s| s.trim())
225 .collect::<Vec<_>>();
226 let mut file = std::fs::File::create(dest)
227 .map_err(|e| format!("Failed to create query output {}: {e}", dest.display()))?;
228 use std::io::Write;
229 writeln!(file, "{}", rels.join(","))?;
230 writeln!(file, "{}", attrs.join(","))?;
231 Ok(())
232}
233
234impl BenchmarkConfig for OxfordBenchmark {
235 fn metadata(&self) -> &BenchmarkMetadata { &METADATA }
236
237 fn load(&self, source: &Path, path: &Path) -> Result<(), Box<dyn std::error::Error>> {
238 let dl_spec = &self.metadata().download_spec;
239
240 let data_dir = path.join(dl_spec.name).join("data");
246
247 let ds_tmp_path = source.join("datasets");
249
250 for dataset_parent in ["dataset1-uniform", "dataset2-zipf"] {
251 for dataset_sub in ["scale1", "scale2", "scale3", "scale4", "scale5", "scale6"] {
252 let source_path = ds_tmp_path.join(dataset_parent).join(dataset_sub);
254
255 let dest_path = data_dir.join(dataset_parent).join(dataset_sub);
257
258 if !dest_path.exists() {
260 std::fs::create_dir_all(&dest_path)?;
261 }
262
263 translate_dataset(&source_path, &dest_path)?;
264 }
265 }
266
267 let queries_dest = path.join(dl_spec.name).join("queries");
269
270 if !queries_dest.exists() {
271 std::fs::create_dir_all(&queries_dest)?;
272 }
273 for query_file in ["query1", "query2", "query3"] {
274 let source_path = ds_tmp_path.join(query_file);
276 let dest_path = queries_dest.join(format!("{}.txt", query_file));
278 translate_query(&source_path, &dest_path)?;
279 }
280
281 Ok(())
282 }
283}