---
title: "The Emu query language"
author: "Jonathan Harrington, Raphael Winkelmann"
date: "WiSe 2122"
output: 
  bookdown::html_document2:
    number_sections: TRUE
    toc: true
    theme: flatly
    highlight: pygments
---

<style>
div.gray {background-color: #e8e8e8; border-radius: 5px; padding: 20px;}
body {font-size: 16pt;}
h1 {font-size: 24pt;}
h2 {font-size: 22pt;}
h3 {font-size: 20pt;}
h4 {font-size: 18pt;}
h5 {font-size: 16pt;}
p.caption {font-size: 12pt; text-align: justify;}
code.sourceCode {font-size: 16pt;}
</style>

# Preliminaries

Follow the setup instructions given [here](https://www.phonetik.uni-muenchen.de/~jmh/lehre/basic_r/_book/index.html), i.e. download R and RStudio, create a directory on your computer where you will store files on this course, make a note of the directory path, create an R project that accesses this directory, and install all indicated packages.

For this and subsequent tutorials, access the `tidyverse`,`magrittr`, `emuR`, and `wrassp` libraries:

```{r}
library(tidyverse)
library(magrittr)
library(emuR)
library(wrassp)
```

The following makes use of the demonstration database `emuDB` that was also used [here](https://www.phonetik.uni-muenchen.de/~jmh/lehre/sem/ws2122/Emuintro/00_emu_R.html#accessing-an-existing-emu-speech-database).

Store and access the demo database as also described [here](https://www.phonetik.uni-muenchen.de/~jmh/lehre/sem/ws2122/Emuintro/00_emu_R.html) and thus:

```{r}
create_emuRdemoData(dir = tempdir())
path.ae = file.path(tempdir(), "emuR_demoData", "ae_emuDB")
ae = load_emuDB(path.ae)
summary(ae)
```

The level definitions show an `EVENT` tier (`Tone` in which annotations are defined by single points in time), one `SEGMENT` tier (`Phonetic`, with start and end times), and several `ITEM` tiers, e.g. `Syllable` or `Word` that inherit times from the `Phonetic` tier. The link definitions summary shows a rich annotation structure that produces the following tree-like structure for the first utterance (note that only a single path through the hierarchy is shown):

```{r, eval=FALSE}
serve(ae)
```

![Figure 1: Hierarchy of the first utterance of the database *ae*.](img/annot_struct.png)

# The query language

## Simple queries

The function for computing queries is called `query()`; this function needs at least two arguments: the name of the database and the query itself, e.g.

```{r}
V = query(ae,  "[Phonetic == V]")
V = query(ae,  "Phonetic == V")
V = query(ae,  "Phonetic = V")
```

The expression `["Phonetic == V"]` is a legal expression in the `EMU Query Language (EQL)` (details see below) and means **"which annotations in the *Phonetic* tier are equal to the label 'V'"** (and "V" is the [*SAMPA for English*](https://www.phon.ucl.ac.uk/home/sampa/english.htm) equivalent to *IPA* /ʌ/, i.e. the vowel in words like *cut*).

### Results of `query()`: segment lists

`query()` has found three tokens of "V" segments:

```{r}
V
```

As of emuR version 2.0.0 this object of the type tibble with one row per segment descriptor:

**Data frame columns**

+ *labels*: annotations or sequenced annotations of segments concatenated by '->'

+ *start*: onset time in milliseconds

+ *end*: offset time in milliseconds

+ *db_uuid*: UUID of emuDB (= a unique identifier)

+ *session*: session name

+ *bundle*: bundle name (= utterance name)

+ *start_item_id*: item ID of first element of sequence

+ *end_item_id*: item ID of last element of sequence

+ *level*: name of the tier that has been searched

+ *attribute*: name of attribute that has been searched

+ *start_item_seq_idx*: sequence index of start item

+ *end_item_seq_idx*: sequence index of end item

+ *type*: type of "segment" row: `ITEM`: symbolic item, `EVENT`: event item, `SEGMENT`: segment

+ *sample_start*: start sample position

+ *sample_end*: end sample position

+ *sample_rate*: sample rate

This makes it easy to access certain informations, e.g.:

```{r}
# get labels:
V$labels

# get start times:
V$start
# or
V %>% pull(start)
# or
with(V, start)

# get end times:
V$end

# calculate durations of the [V]s
V$end - V$start
```

`V` in the above example is a segment list with start and end times because `Phonetic` is a `SEGMENT` tier. Event tiers can be queried as well in which case the structure of the tibble data-frame that is returned is exactly the same, except that the end times are all zero (because the annotations mark events in time, rather than segments). For example, here is an event list of all tones in which the end times are all zero.

```{r}
tones = query(ae, "Tone =~ .*")
tones[1:3,]
```

### Inherited times

Annotations at `ITEM` tiers that either have no times or that inherit times can be can queried in the same way:

```{r}
# Phonetic of type SEGMENT, Phoneme of type ITEM
list_levelDefinitions(ae)
V_phoneme = query(ae,  "[Phoneme == V]")
V_phoneme
V
```

Both `V` and `V_phoneme` are associated with times, even though `Phoneme` is a timeless `ITEM` tier. The reason for this was explained in an earlier module [here](https://www.phonetik.uni-muenchen.de/~jmh/lehre/sem/ws2122/Emuintro/00_emu_R.html#fig:seen).

The calculation of inherited times can be time-consuming and may be switched off with: `calcTimes = FALSE`:

```{r}
# Phonetic of type SEGEMNT, Phoneme of type ITEM
list_levelDefinitions(ae)
V_phoneme2 = query(ae,
                   "[Phoneme == V]",
                   calcTimes = FALSE)
V_phoneme2
```

In this case, all entries in `start` and `end` are `NA` (== `N`ot `A`vailable).

### `requery_hier()` and `requery_seq()`

`requery_hier()` allows segment or event lists to be created for a tier linked to any existing segment list. In the above case, the annotation tier of  `V_phoneme2` was `Phoneme` which is linked to many other tiers, as the following shows:

```{r}
list_linkDefinitions(ae)
```

Therefore, to make a segment list of the words corresponding to these segments:

```{r}
t.s = requery_hier(ae, V_phoneme2, "Text")
t.s
```

The above is a requery looking upstream to a tier than dominates `Phoneme`. A downstream query makes a segment list of all annotations that are found delimited by `->`. Thus for the segment list `t.s` at the `Text` tier that has just been created, the corresponding phonemes are:
```{r}
requery_hier(ae, t.s, "Phoneme")
```

As another example, the following makes a segment list of all words and then requeries these to find out which of them are associate with tones. This is possible because the `Word` and therefore `Text` tiers are linked to the `Tone` tier via the `Syllable` tier, as `list_linkDefinitions(ae)` had shown:

```{r}
all.s = query(ae, "Text =~ .*")
requery_hier(ae, all.s, "Tone")
```
Notice how many rows are marked `NA` because they are unassociated with tones (i.e. not pitch-accented).
```{r}
# find all "V"-labels in `ae`
V = query(ae,
          "[Phonetic == V]")
```

The function `requery_seq()` is used for finding annotations and/or making segment lists that precede or follow an existing segment/event list in sequence. In contrast to the  `requery_hier()` function, the segment or event lists returned from `requery_seq()` are always from the same tier as the segment or event list being requeried. The argument `offset = n` for any negative or positive integer finds all  following anotations if the integer is positive, and all preceding annotations if the integer is negative. Thus to find the annotations that follow those of the segment list `V` created earlier:

```{r}
requery_seq(emuDBhandle = ae,
            seglist = V,
            offset = 1)
```

To find e.g., those 2 positions to the left, the command is the same but with `offset=-2`. However, this will fail in this case because there are no annotations 2 positions to the left for some of the annotations in `V`. To get round this problem, the additional argument `ignoreOutOfBounds=TRUE` must be included:

```{r}
requery_seq(emuDBhandle = ae,
            seglist = V,
            offset = -2, ignoreOutOfBounds=TRUE)
```
which gives `NA` for the first row above, because for this segment, there are no annotations two positions to its left.  

A further variation on the function `requery_seq()` is to include `length=n` where `n` is a positive integer: this finds a sequence of `n` annotations at a given `offset` position. For example, the following makes a segment list that extends from 1 to 3 annotations to the right relative to the semgent list `V`:

```{r}
requery_seq(emuDBhandle = ae,
            seglist = V,
            offset = 1, length=3)
```

## More on the **E**mu **Q**uery **L**anguage `EQL`

To learn more about the functionality of the `EQL`, see also the manual chapter [here](https://ips-lmu.github.io/The-EMU-SDMS-Manual/chap-querysys.html).

As discussed in the preceding section, any query must be placed within `" "`, and any query *can* be placed within `[ ]`. The query must include minimally the name of an annotation tier combined with a representation for an annotation (which can also be a [*regular expression*](https://medium.com/factory-mind/regex-tutorial-a-simple-cheatsheet-by-examples-649dc1c3f285). Further details as follows.

### Single argument queries

#### Equality

In the examples above, the equality of the "V" annotations at the `Phonetic` tier (in the database `ae`) were tested:

```{r}
query(emuDBhandle = ae, 
      "Phonetic == V")
```

The equality operator is `==`. For backward compatibility with earlier versions of `emuR` a single `=` is also allowed. Thus the preceding command is equivalent to:

```{r}
query(emuDBhandle = ae, 
      "Phonetic = V")
```

#### Inequality

Searches can be made for everything *except* `V` by the use of `!=`.

```{r}
query(ae, 
      "Phonetic != V")
```

One way to get 'everything' is to query something that is probably not in the database like `xyz`. Alternatively, a regular expression can be used and is preceded by `=~`. The regular expression for finding all annotations is `.*` (meaning: any character (`.`) zero or more times (`*`)). 

```{r}
everything1 = query(ae, 
                    "Phonetic != xyz")
everything2 = query(ae, 
                    "Phonetic =~ .*")
# should be T if both are equal everywhere
all(everything1 == everything2) 
```

The operator `!~` is for negation. An example would be:

```{r}
# Find all segments at the `Text` tier that don't begin with "a"
query(ae,  "Text !~ a.*")
```

There are therefore four operators, two for equality matching, and two for inequality:

| Symbol | Meaning |
| :------------------ | :--------------------------------------------------------------------------------------|
|`==`|	equality|
|`=~`|	regular expression matching|
|`!=`|	inequality|
|`!~`|	regular expression non-matching|

#### The `OR` operator

The operator `|` can be used to search for several annotations:

```{r}
# find all `m` or `n` annotations at the `Phonetic` tier
query(ae, 
      "Phonetic == m | n")
# find all `m` or `n` or `N` annotations at the `Phonetic` tier
query(ae, "Phonetic == m | n | N")
```
#### Use of features
It is possible in the the Emu system to define *features* and then to query them. The definition of features is accomplished with the function `add/list/remove_attrDefLabelGroups()`. For the existing `ae` database some features have already been defined. To see these for the `Phonetic` tier:

```{r}
list_attrDefLabelGroups(ae,
                        levelName = "Phonetic",
                        attributeDefinitionName = "Phonetic")
```
shows for example that there is a feature `nasal` that includes the annotations `m` and `n`. Consequently, the following give the same output:

```{r}
# Segment list of nasals
nas.s1 = query(ae, "Phonetic = nasal")
nas.s2 = query(ae, "Phonetic = m|n")
all(nas.s1 == nas.s2)
```

Use `add_attrDefLabelGroup()` to add new features. In this example, a feature `grave` is added to the `Phoneme` tier by which `grave` includes the labial and velar consonants:

```{r}
add_attrDefLabelGroup(
  ae,
  levelName = "Phoneme",
  attributeDefinitionName = "Phoneme",
  labelGroupName = "grave",
  labelGroupValues = c("p", "b", "k", "g")
)
```

A segment list of the `grave` annotations is then:

```{r}
grave.s = query(ae, "Phoneme = grave")
count(grave.s, labels)
```

### Sequence queries

Anything except simple queries requires the use of `[ ]` brackets. Thus whereas in  simple queries brackets are optional.

```{r}
mnN = query( ae, "[Phonetic == m | n | N]")
# or
mnN = query( ae, "Phonetic == m | n | N")
```

sequence (and hierarchical) queries require `[ ]` brackets. 
The `->` operator is for finding *sequences* of annotations:

```{r}
query(ae, "[Phonetic == V -> Phonetic == m]")
```

Note: all row entries in the resulting segment list have the start time of `V`, the end time of `m` and their annotations are `V->m`. This can be changed with the `result modifier` hash tag `#` as follows:

```{r}
# finds V, if V is followed by m
query(ae, "[#Phonetic == V -> Phonetic == m]") 
# finds m, if m is preceded by V
query(ae, "[Phonetic == V -> #Phonetic == m]") 
```

Only one hash tag per query is allowed.

Embedded bracketing is needed to search for multiple sequences. This finds all sequences of `@ n s` at the `Phonetic` tier:

```{r}
query(ae, "[[Phonetic == @ -> Phonetic == n] -> Phonetic == s]")
```

The following finds `offer` followed by any two annotations followed by `resistance`
at the `Text` tier.
```{r}
query(ae, "[[[Text == offer -> Text =~ .*] 
      -> Text =~ .* ] -> Text == resistance]")
```

### Domination or linked queries

The operator `^` is for queries spanning two linked tiers. The following find all `p` annotations at the `Phoneme` tier in strong syllables (i.e. `p` annotations at the `Phoneme` tier  dominated by / linked to `S` annotations at the  `Syllable` tier):

```{r}
query(ae, "[Phoneme == p ^ Syllable == S]")
```

The `^` operator is *not* directional. Thus, although `Syllable` dominates `Phoneme`, the same output is also given by:

```{r}
query(ae, "[Syllable == S ^ #Phoneme == p]")
```

Thus the semantics of `^` is not really *"is dominated by"*, but rather *"is linked to"*. However, the preceding requires `#` which can be omitted by placing the annotations that are required in first position i.e. as `query(ae, "[Phoneme == p ^ Syllable == S]")`.

Brackets are needed for queries spanning several linked tiers:

```{r}
# Find all Phonetic annotations in strong syllables
# in either `amongst` or `beautiful`
query(ae, 
      "[[Phonetic =~ .* ^ Syllable == S] 
      ^ Text == amongst | beautiful]")
```

### Conjunction queries
The `&` operator is used to for annotations of a tier than is an `attribute` of another tier.For example, `Text` and `Accent` are evidently attributes of the `Word` tier, as shown by:

```{r}
list_attributeDefinitions(ae, level = "Word")
```

Thus to find all accented (`S`) words:

```{r}
query(ae, "[Text =~.* & Accent == S]")
```

To find all unaccented function words:

```{r}
query(ae, "[Text =~.* & Accent == W & Word == F]")
```

To find  unaccented function words but only if they follow a content word:

```{r}
query(ae, "[Word = C -> #Text =~.* & Accent == W & Word == F]")
```

### Position queries

There are three position functions, `Start(X,Y)`, `Medial(X,Y)`, and `End(X,Y)`.  In all position queries, the annotations are returned from `Y` in `F(X, Y)` where `X `and `Y` are the two tiers that form part of a position query. Thus the following finds all annotations at the `Phoneme` tier that are initial relative to annotations at the `Word` tier (i.e., it finds all word-initial phonemes).

```{r}
query(ae, "[Start(Word, Phoneme) == T]")
```

Word-initial and word-medial phonemes could then be obtained by finding  all phonemes that are *not* word-final:

```{r}
# Word-initial and word-medial phonemes
query(ae, "[End(Word, Phoneme) == F]")
```

To find all `f` and `S` phonemes that are word-initial:

```{r}
query(ae, "[Phoneme = f | S  & Start(Word, Phoneme) == T]")
```

To find all phonemes in intonationally phrase-final syllables is a domination/linked query. This is because  (i) `Y` is `Syllable` in the second part of the query and (ii) `Syllable` dominates `Phoneme`. Because `Syllable` dominates `Phoneme` the two pieces of the query must be linked by `^`:

```{r}
query(ae, "[Phoneme =~ .* 
      ^ End(Intonational, Syllable) = T]")
```

To find all phonemes in intonationally phrase-final *weak* syllables is:

```{r}
query(ae, "[Phoneme =~ .* 
      ^ Syllable = W 
      & End(Intonational, Syllable) = T]")
```


### Count queries
 These involve a comparison with a number (by using one of `==`, `!=`, `>`, `>=`, `<`, `<=`: see below). The count function  `Num(X,Y)` which counts the number of annotations at tier `Y` relative to tier `X`. Notice that whereas position queries return annotations at tier `Y`, **count queries return annotations at tier `X`**. Thus the following finds all bisyllabic words and then all words made of four or more syllables:

```{r}
# Find all words that contain two syllables
query(ae, "[Num(Text, Syllable) == 2]")
# Find all syllables that contain more than four phonemes
query(ae, "[Num(Syllable, Phoneme) > 4]")
```

To get all syllables in bisyllabic words requires:

```{r}
query(ae, "[Syllable =~ .* ^ Num(Text, Syllable) == 2]")
```

Notice that the above query has to be linked by `^`. This is because as stated earlier `Num(X, Y)` returns annotations from tier `X` which is in this case `Text` which dominates (is linked to) `Syllable`. Similarly, to find all `Intermediate` phrases that contain words of 4 or more syllables:

```{r}
query(ae, "[Intermediate =~ .* ^ Num(Text, Syllable) >= 4]")
```
Again the two bits of the query must be joined with `^` because `Intermediate` dominates `Text`. To get all words of 4 or more syllables in `L- Intermediate` phrases requires putting the `Num(X,Y)` function as the first part of the query argument:

```{r}
query(ae, "[Num(Text, Syllable) >= 4 ^ Intermediate = L-]")
```

As a general guide, it's helpful to build up more complex queries that involve e.g. both number, position and multiple tiers in stages. For example:

- find the final syllable of trisyllabic words that have a word-final /s/.

The above would match `circumstance` (if it's in the database) because the word is trisyllabic and ends in /s/.

```{r}
# Find all three syllable words
query(ae, "Num(Text, Syllable)=3")
```

```{r}
# Find all three syllable words that end in /s/.
query(ae, 
      "[Num(Text, Syllable)=3 ^ 
      Phoneme=s & End(Text, Phoneme)=T]")
```
Note again the need for `^` in the above. This is because the first part of the query returns `Text` annotations and the second part returns `Phoneme` annotations and because the `Text` tier dominates (is linked to) the `Phoneme` tier. To get at the `Phoneme` annotations in the above, modify the above with `#`

```{r}
# Find all word-final /s/ in trisyllabic words.
query(ae, 
      "[Num(Text, Syllable)=3 ^ 
      #Phoneme=s & End(Text, Phoneme)=T]")
```

To get the syllable that dominates an /s/ that is word-final:

```{r}
# Syllables dominating  word-final /s/
query(ae,  
      "[Phoneme=s & End(Text, Phoneme)=T ^ 
      #Syllable =~ .*]")
```

or equivalently without `#`:
```{r}
# Syllables dominating  word-final /s/
query(ae,  "[Syllable =~ .* ^ 
      Phoneme=s & End(Text, Phoneme)=T]")
```

Now just modify the command for obtaining all word-final /s/ in trisyllabic words with the above command, thus:

```{r}
# the final syllable of trisyllabic words 
# that end in /s/.
query(ae, 
      "[Num(Text, Syllable)=3 ^ 
      [Phoneme=s & End(Text, Phoneme)=T ^ 
      #Syllable =~ .*]]")
```

or equivalently:

```{r}
# the final syllable of trisyllabic words 
# that end in /s/.
query(ae,  "[[Syllable =~ .* ^ 
      Phoneme=s & End(Text, Phoneme)=T] ^ 
      Num(Text, Syllable)=3]")
```
