1use crate::encoding::rdf_type_id;
10use crate::error::Result;
11use crate::sql::{col, expect_len, Capabilities, Request, Response, Statement};
12use std::collections::{BTreeSet, HashMap};
13
14#[derive(Debug, Clone, Copy, PartialEq)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17pub struct PredicateStats {
18 pub triples: f64,
19 pub distinct_subjects: f64,
20 pub distinct_objects: f64,
21}
22
23#[derive(Debug, Clone, Default)]
25#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26pub struct Stats {
27 pub available: bool,
29 pub total: f64,
30 pub predicates: HashMap<i64, PredicateStats>,
31 pub classes: HashMap<i64, f64>,
33 pub pairs: HashMap<(i64, i64), f64>,
36 pub graph_index: bool,
37 pub text_index: bool,
39 pub transitive: BTreeSet<i64>,
41}
42
43fn id_col(caps: &Capabilities, c: &str) -> String {
44 if caps.int64_as_text {
45 format!("CAST({c} AS TEXT)")
46 } else {
47 c.into()
48 }
49}
50
51impl Stats {
52 pub fn load_request(caps: &Capabilities) -> Request {
54 Request::read(vec![
55 Statement::new("SELECT key, value FROM oxilite_meta"),
56 Statement::new(format!(
57 "SELECT {}, triples, distinct_s, distinct_o FROM stats_pred",
58 id_col(caps, "p")
59 )),
60 Statement::new(format!(
61 "SELECT {}, instances FROM stats_class",
62 id_col(caps, "o")
63 )),
64 crate::reason::transitive_statement(|c| id_col(caps, c)),
65 Statement::new(format!(
66 "SELECT {}, {}, n FROM stats_po",
67 id_col(caps, "p"),
68 id_col(caps, "o")
69 )),
70 ])
71 }
72
73 pub fn from_response(response: &Response) -> Result<Self> {
74 expect_len(response, 5)?;
75 let mut stats = Self::default();
76 for row in &response[0].rows {
77 let key = col(row, 0)?.as_str().unwrap_or_default();
78 let value = col(row, 1)?.clone().into_string().unwrap_or_default();
79 match key {
80 "graph_index" => stats.graph_index = value == "1",
81 "text_index" => stats.text_index = value == "1",
82 "total" => {
83 stats.total = value.parse().unwrap_or(0.0);
84 stats.available = true;
85 }
86 _ => {}
87 }
88 }
89 for row in &response[1].rows {
90 let (Some(p), Some(t), Some(ds), Some(d_o)) = (
91 col(row, 0)?.as_i64(),
92 col(row, 1)?.as_f64(),
93 col(row, 2)?.as_f64(),
94 col(row, 3)?.as_f64(),
95 ) else {
96 continue;
97 };
98 stats.predicates.insert(
99 p,
100 PredicateStats {
101 triples: t,
102 distinct_subjects: ds.max(1.0),
103 distinct_objects: d_o.max(1.0),
104 },
105 );
106 }
107 for row in &response[2].rows {
108 if let (Some(o), Some(n)) = (col(row, 0)?.as_i64(), col(row, 1)?.as_f64()) {
109 stats.classes.insert(o, n);
110 }
111 }
112 for row in &response[3].rows {
113 if let Some(p) = col(row, 0)?.as_i64() {
114 stats.transitive.insert(p);
115 }
116 }
117 for row in &response[4].rows {
118 if let (Some(p), Some(o), Some(n)) = (
119 col(row, 0)?.as_i64(),
120 col(row, 1)?.as_i64(),
121 col(row, 2)?.as_f64(),
122 ) {
123 stats.pairs.insert((p, o), n);
124 }
125 }
126 Ok(stats)
127 }
128
129 pub fn refresh_request() -> Request {
132 let mut r = Self::refresh_statements();
133 r.extend(crate::reason::closure_statements());
134 r.extend(crate::shapes::refresh_statements());
135 Request::atomic(r)
136 }
137
138 fn refresh_statements() -> Vec<Statement> {
139 vec![
140 "DELETE FROM stats_pred".into(),
141 "INSERT INTO stats_pred(p, triples, distinct_s, distinct_o) \
142 SELECT p, COUNT(*), COUNT(DISTINCT s), COUNT(DISTINCT o) FROM quads GROUP BY p"
143 .into(),
144 "DELETE FROM stats_class".into(),
145 Statement::new(format!(
146 "INSERT INTO stats_class(o, instances) SELECT o, COUNT(*) FROM quads WHERE p = {} GROUP BY o",
147 rdf_type_id()
148 )),
149 "INSERT OR REPLACE INTO oxilite_meta(key, value) SELECT 'total', CAST(COUNT(*) AS TEXT) FROM quads"
150 .into(),
151 "DELETE FROM stats_po".into(),
152 Statement::new(format!(
153 "INSERT INTO stats_po(p, o, n) SELECT q.p, q.o, COUNT(*) FROM quads q JOIN stats_pred sp ON sp.p = q.p \
154 WHERE sp.distinct_o <= 1024 AND q.p <> {} GROUP BY q.p, q.o, sp.triples, sp.distinct_o \
155 HAVING COUNT(*) >= 4.0 * sp.triples / sp.distinct_o ORDER BY COUNT(*) DESC LIMIT 2000",
156 rdf_type_id()
157 )),
158 ]
159 }
160}