stamtools/
query.rs

1use stam::*;
2
3pub(crate) fn textselection_from_queryresult<'a>(
4    resultitems: &QueryResultItems<'a>,
5    var: Option<&str>,
6) -> Result<(ResultTextSelection<'a>, bool, Option<&'a str>), &'a str> {
7    //convert query result to text selection
8    let resultitem = if let Some(var) = var {
9        resultitems.get_by_name(var).ok()
10    } else {
11        resultitems.iter().next()
12    };
13    let (resulttextselection, whole_resource, id) = match resultitem {
14        Some(QueryResultItem::TextSelection(textselection)) => (textselection.clone(), false, None),
15        Some(QueryResultItem::TextResource(resource)) => (
16            resource
17                .textselection(&Offset::whole())
18                .expect("textselection must succeed"),
19            true,
20            resource.id(),
21        ),
22        Some(QueryResultItem::Annotation(annotation)) => {
23            let mut iter = annotation.textselections();
24            if let Some(textselection) = iter.next() {
25                if iter.next().is_some() {
26                    return Err("Resulting annotation does not reference any text");
27                }
28                (textselection, false, annotation.id())
29            } else {
30                return Err("Resulting annotation does not reference any text");
31            }
32        }
33        Some(QueryResultItem::AnnotationData(_)) => {
34            return Err("Query produced result of type DATA, but this does not reference any text");
35        }
36        Some(QueryResultItem::DataKey(_)) => {
37            return Err("Query produced result of type KEY, but this does not reference any text");
38        }
39        Some(QueryResultItem::AnnotationDataSet(_)) => {
40            return Err("Query produced result of type SET, but this does not reference any text");
41        }
42        Some(QueryResultItem::AnnotationSubStore(_)) => {
43            return Err(
44                "Query produced result of type SUBSTORE, but this does not reference any text",
45            );
46        }
47        None | Some(QueryResultItem::None) => {
48            return Err("Query produced no results");
49        }
50    };
51    Ok((resulttextselection, whole_resource, id))
52}
53
54/// Run a query and outputs the results as STAM JSON to standard output
55pub fn to_json<'a, W: std::io::Write>(
56    store: &'a AnnotationStore,
57    writer: &mut W,
58    query: Query<'a>,
59) -> Result<(), StamError> {
60    let iter = store.query(query)?;
61    write!(writer, "[")?;
62    for (i, resultrow) in iter.enumerate() {
63        if i > 0 {
64            writeln!(writer, ",\n{{\n")?;
65        } else {
66            writeln!(writer, "{{\n")?;
67        }
68        for (j, (result, varname)) in resultrow.iter().zip(resultrow.names()).enumerate() {
69            let json = match result {
70                QueryResultItem::None => "null".to_string(),
71                QueryResultItem::Annotation(annotation) => {
72                    annotation.as_ref().to_json_string(store)?
73                }
74                QueryResultItem::AnnotationData(data) => {
75                    data.as_ref().to_json(data.set().as_ref())?
76                }
77                QueryResultItem::DataKey(key) => key.as_ref().to_json()?,
78                QueryResultItem::TextResource(resource) => resource.as_ref().to_json_string()?,
79                QueryResultItem::AnnotationDataSet(dataset) => dataset.as_ref().to_json_string()?,
80                QueryResultItem::TextSelection(tsel) => tsel.to_json()?,
81                QueryResultItem::AnnotationSubStore(substore) => {
82                    //MAYBE TODO: this may need to be thought out deeper but doesn't occur yet anyway
83                    substore.id().expect("must have ID").to_string()
84                }
85            };
86            let varnum = format!("{}", j + 1);
87            writeln!(
88                writer,
89                "\"{}\": {}{}",
90                if let Some(varname) = varname {
91                    varname
92                } else {
93                    &varnum
94                },
95                json,
96                if i < resultrow.len() - 1 { ",\n" } else { "\n" }
97            )?;
98        }
99        write!(writer, "}}")?;
100    }
101    writeln!(writer, "]")?;
102    Ok(())
103}
104
105/// Run a query and outputs the results as W3C Web Annotation to standard output.
106/// Each annotation will be formatted in JSON-LD on a single line, so the output is JSONL.
107pub fn to_w3anno<'a, W: std::io::Write>(
108    store: &'a AnnotationStore,
109    writer: &mut W,
110    query: Query<'a>,
111    use_var: &str,
112    config: WebAnnoConfig,
113) {
114    let iter = store.query(query).expect("query failed");
115    for resultrow in iter {
116        if let Ok(result) = resultrow.get_by_name(use_var) {
117            match result {
118                QueryResultItem::None => {}
119                QueryResultItem::Annotation(annotation) => {
120                    writeln!(writer, "{}", annotation.to_webannotation(&config))
121                        .expect("writer failed");
122                }
123                QueryResultItem::TextSelection(tsel) => {
124                    for annotation in tsel.annotations() {
125                        writeln!(writer, "{}", annotation.to_webannotation(&config))
126                            .expect("writer failed");
127                    }
128                }
129                _ => {
130                    eprintln!("Error: Obtained result type can not be serialised to Web Annotation, only ANNOTATION and TEXT work.");
131                }
132            }
133        } else {
134            eprintln!("Error: No result found for variable {}", use_var);
135            return;
136        }
137    }
138}