kermit_bench/benchmarks/
oxford.rs

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