Latest news 2024-10-15: New blog post: Tales for Our Times Book Launch.

Gallery: Inner or Nested Glossaries

Image of hierarchical effect glossary with the top level entries in reverse alphabetical order.
The example Logical Glossary Divisions (type vs group vs parent) shows the different ways of dividing your entries into logical groups using the type, group or parent fields. As from glossaries-extra v1.44, you can now also use “inner” or nested glossaries. This is a more complicated method that can’t be used with tabular-like styles (such as long or super) and so should only be used if the methods described in Logical Glossary Divisions (type vs group vs parent) can’t produce the desired effect.

The example documents below use the same .bib files as in the earlier example: topics.bib, symboltopics.bib, pictographs.bib, latinsymbols.bib, mathgreek.bib, abbreviations.bib, animals.bib, minerals.bib, vegetables.bib.

To provide a better understanding of how inner glossaries work, it’s useful to understand the difference between \printglossary (used with makeindex and xindy) and \printunsrtglossary (used with bib2gls).

In the first case, makeindex or xindy is used to create a file that contains content in the form:

\glossarysection[\glossarytoctitle]{\glossarytitle}\glossarypreamble
\begin{theglossary}\glossaryheader
content
\end{theglossary}\glossarypostamble

where content contains lines such as:

\glsgroupheading{group label}\relax \glsresetentrylist
\glossentry{entry label}{location list}%
\subglossentry{level}{entry label}{location list}%

The group headings are typeset using \glsgroupheading. (That command is followed by \glsresetentrylist, which resets the location list encapsulation and allows the location list to be saved, if required.) Top-level entries are typeset with \glossentry and child entries are typeset with \subglossentry where level indicates the hierarchical level. Both makeindex and xindy order the items so that the child entries are placed immediately after the corresponding parent entry.

The \printglossary command essentially does:

Set default title and style.
\bgroup
  Initial setup.
  Input the file created by makeindex or xindy.
\egroup

The initial setup part sets the glossary style (which determines the definitions of theglossary, \glossaryheader, \glsgroupheading, \glossentry and \subglossentry), assigns the title and defines \currentglossary. (There is some other stuff done both before and after the file is input, but that’s not relevant here.)

In the second case, bib2gls is used to create a file that contains the entry definitions, which is input in the document preamble (via \GlsXtrLoadResources). The entries are defined in the required order and use internal fields to store the indexing information (such as the group label and location lists). Now \printunsrtglossary is used to display the glossary, which essentially does:

Set default title and style.
\bgroup
  Initial setup.
  \glossarysection[\glossarytoctitle]{\glossarytitle}\glossarypreamble
  Construct internal control sequence containing glossary content.
  Expand internal control sequence.
  \glossarypostamble
\egroup

The initial setup is the same as for \printglossary. The key difference here is that there’s no file containing the typeset glossary that can be simply input. Instead it’s necessary to iterate over all entries. Some of the glossary styles use a tabular-like environment (such as longtable, which is used by the long styles). It’s always problematic having a loop inside a tabular context so \printunsrtglossary by-passes the problem by moving the loop outside of the theglossary environment. The command iterates over all entries and constructs an internal control sequence, which ends up containing:

\begin{theglossary}\glossaryheader\glsresetentrylist
content
\end{theglossary}

The content in this case is different as it doesn’t explicitly contain \glossentry and \subglossentry but instead uses an internal handler that just takes the entry label as the argument. The \glsgroupheading{group label} command is inserted whenever a top-level entry has the group field set to a label that’s different to the previous top-level entry’s group field (unless the groups=false option, provided with glossaries-extra v1.44+, is used in the optional argument of \printunsrtglossary). So the content is in the form:

\glsgroupheading{group label}%
\internal cs handler{entry label}%
\internal cs handler{entry label}%
…
\glsgroupheading{group label}%
\internal cs handler{entry label}%
\internal cs handler{entry label}%
…

The loop that constructs this content has a hook that’s used at the start of each iteration. This hook can be modified to skip the current entry, as in the Summary of Frequently Used Terms example, so that the entry is omitted from the content.

The internal cs handler sets \glscurrententrylabel to the current entry label and uses \printunsrtglossaryhandler{label} to format the entry. This user-level command may be redefined to adjust the formatting. The default definition is simply:

\newcommand{\printunsrtglossaryhandler}[1]{\glsxtrunsrtdo{#1}}

where \glsxtrunsrtdo accesses the entry’s hierarchical level and, based on that, determines whether to use \glossentry or \subglossentry. The leveloffset option (glossaries-extra v1.44+) which can be passed to \printunsrtglossary) will make \glsxtrunsrtdo increment the hierarchical level by the offset before it determines whether \glossentry or \subglossentry is required.

\printunsrtinnerglossary

As from glossaries-extra v1.44, there is now a similar command:

\printunsrtinnerglossary[options]{pre-code}{post-code}

which behaves in a comparable way to the inner part of \printunsrtglossary[options]. This new command doesn’t do the first part:

Set default title and style.
\bgroup
  Initial setup.
  \glossarysection[\glossarytoctitle]{\glossarytitle}\glossarypreamble

or the last part:

  \glossarypostamble
\egroup

As with \printunsrtglossary, \printunsrtinnerglossary constructs an internal control sequence containing the content but it doesn’t add:

\begin{theglossary}%
\glossaryheader\glsresetentrylist

or \end{theglossary} to the content. To localise the effect of any options that may be supplied, internal scoping is inserted, so \printunsrtinnerglossary essentially does:

\begingroup
  Initial setup (process options).
  pre-code
  Construct internal control sequence containing glossary content.
  Expand internal control sequence.
  post-code
\endgroup

where the internal control sequence now only contains instances of \internal cs handler{entry label} and (possibly) \glsgroupheading{group label} (not the encapsulating theglossary environment or header, which have to be added separately).

Since this inner command doesn’t have a header and should always be placed inside theglossary (and so the style must have already been set before the start of that environment and can’t be altered mid-way through it), there are some options that can be passed to \printunsrtglossary that make no sense with \printunsrtinnerglossary and so are ignored. These include: title, toctitle, style, numberedsection and label.

The internal scoping and the need now for the loop to be inside theglossary makes this command unsuitable for tabular-like styles.

There are two ways of placing \printunsrtinnerglossary within theglossary (and ensuring that the style is correctly initialised):

  1. Place \printunsrtinnerglossary inside the printunsrtglossarywrap environment.
  2. Redefine the handler function \printunsrtglossaryhandler{label} to use \printunsrtinnerglossary. In this case, there must be some way of obtaining the inner glossary type from the entry identified by label.

These methods are demonstrated in the examples below.

printunsrtglossarywrap

The printunsrtglossarywrap environment takes one optional argument that uses the same keys as \printunsrtglossary. Note that in this case the type key simply provides a title (if one has been assigned to that glossary). It doesn’t indicate the content.

The start of the environment sets up the glossary style and does the header:

Set default title and style.
Initial setup.
\glossarysection[\glossarytoctitle]{\glossarytitle}\glossarypreamble
\glossarypreamble
\begin{theglossary}\glossaryheader\glsresetentrylist

The end of this wrapper environment ends theglossary and does the postamble:

\end{theglossary}\glossarypostamble

This first example uses the sample files pictographs.bib, latinsymbols.bib, mathgreek.bib, abbreviations.bib, animals.bib, minerals.bib, vegetables.bib. Each file corresponds to a separate glossary. The default main glossary isn’t required so the nomain option is used to prevent its automatic creation. The glossary labels correspond to the base name of each .bib file. Since the base name of the abbreviations.bib file matches the label of the glossary created with the abbreviations package option, that option is used to conveniently define it. The other glossaries are defined manually with \newglossary*:

\newglossary*{vegetables}{Vegetables}
\newglossary*{minerals}{Minerals}
\newglossary*{animals}{Animals}
\newglossary*{mathgreek}{Symbols (Greek)}
\newglossary*{latinsymbols}{Symbols (Latin)}
\newglossary*{pictographs}{Pictographs}

Remember that the abbreviation styles must be set before the resource file is loaded:

\setabbreviationstyle{short-long-desc}
\setabbreviationstyle[acronym]{short-long}

All entries can be processed in a single resource set. The type={same as base} option conveniently assigns the type field to the base name of the corresponding .bib file. All entries are selected. There are no references in the document so there are no location lists.

The list style is used. This can be problematic with \printunsrtinnerglossary as it can lead to a “missing \item” error on the first LaTeX run if all instances of \item are within the handler function. (No entries are defined on the first LaTeX run so the handler function isn’t called.) In this case, I’ve explicitly used \item to separate each glossary so that error won’t occur. On the first LaTeX run, only those items will be present.

example image

The initial comment lines below are arara directives. You can remove them if you don’t use arara.

% arara: pdflatex
% arara: bib2gls
% arara: pdflatex
\documentclass{article}

\usepackage{amssymb}
\usepackage[nomain,abbreviations,record]{glossaries-extra}[2020/03/23]% v1.44+

\setabbreviationstyle{short-long-desc}
\setabbreviationstyle[acronym]{short-long}

\newglossary*{vegetables}{Vegetables}
\newglossary*{minerals}{Minerals}
\newglossary*{animals}{Animals}
\newglossary*{mathgreek}{Symbols (Greek)}
\newglossary*{latinsymbols}{Symbols (Latin)}
\newglossary*{pictographs}{Pictographs}

\GlsXtrLoadResources[
  src={vegetables,minerals,animals,
       mathgreek,latinsymbols,pictographs,
       abbreviations},
  type={same as base},
  selection={all},
  sort={en}
]

\begin{document}
\begin{printunsrtglossarywrap}[style=list,title={List of All Terms}]
\item[pictographs (icons)]
\printunsrtinnerglossary[type=pictographs]{}{}

\item[mineral (inorganic substance)]
\printunsrtinnerglossary[type=minerals]{}{}

\item[animal (living organism)]
\printunsrtinnerglossary[type=animals]{}{}

\item[veggies (plants)]
\printunsrtinnerglossary[type=vegetables]{}{}

\item[mathematical symbols (Latin)]
\printunsrtinnerglossary[type=latinsymbols]{}{}

\item[mathematical symbols (Greek)]
\printunsrtinnerglossary[type=mathgreek]{}{}

\item[abbreviations (acronyms etc)]
\printunsrtinnerglossary[type=abbreviations]{}{}

\end{printunsrtglossarywrap}
\end{document}

If you don’t use arara, you need to run the following commands:

pdflatex innerglossaries1
bib2gls innerglossaries1
pdflatex innerglossaries1

(See Incorporating makeglossaries or makeglossaries-lite or bib2gls into the document build.)

Download: PDF (82.09K), source code (1.33K).

Notes


This example is provided to demonstrate the use of printunsrtglossarywrap and \printunsrtinnerglossary. In this case, a far simpler and neater solution can be obtained with just using \printunsrtglossary for each glossary (as in Multiple Glossaries) and adjusting the sectioning command. For example, the document can be modified slightly:

\setupglossaries{section=subsection}
\section*{List of All Terms}
\printunsrtglossary[type=pictographs,title={pictographs (icons)}]
\printunsrtglossary[type=minerals,title={mineral (inorganic substance)}]
\printunsrtglossary[type=animals,title={animal (living organism)}]
\printunsrtglossary[type=vegetables,title={veggies (plants)}]
\printunsrtglossary[type=latinsymbols,title={mathematical symbols (Latin)}]
\printunsrtglossary[type=mathgreek,title={mathematical symbols (Greek)}]
\printunsrtglossary[type=abbreviations,title={abbreviations (acronyms etc)}]

Since ignored glossaries work just as well for this example, it’s simpler to make bib2gls define them instead with the --provide-glossaries switch (v2.23+). The following example is a slight modification that demonstrates this. It also demonstrates the pre-code argument, which may be used to locally redefine commands (such as the \printunsrtglossaryentryprocesshook hook that may be used to filter entries). In this case, I’m using pre-code to sometimes locally redefine \glsnamefont, which alters the formatting of the entry names. It’s not used for all the glossaries, which helps demonstrate that the redefinition is localised.

example image

The initial comment lines below are arara directives. You can remove them if you don’t use arara.

% arara: pdflatex
% arara: bib2gls: { options: [ --provide-glossaries ] }
% arara: pdflatex
\documentclass{article}

\usepackage{xcolor}
\usepackage{amssymb}
\usepackage[nomain,abbreviations,record]{glossaries-extra}[2020/03/23]% v1.44+

\setabbreviationstyle{short-long-desc}
\setabbreviationstyle[acronym]{short-long}

\GlsXtrLoadResources[
  src={vegetables,minerals,animals,
       mathgreek,latinsymbols,pictographs,
       abbreviations},
  type={same as base},
  selection={all},
  sort={en}
]

\newcommand{\iconfont}[1]{\textcolor{teal}{#1}}
\newcommand{\substancefont}[1]{\textcolor{purple}{\mdseries #1}}

\begin{document}
\begin{printunsrtglossarywrap}[style=list,title={List of All Terms}]
\item[pictographs (icons)]
\printunsrtinnerglossary[type=pictographs]
{\let\glsnamefont\iconfont}% pre-code
{}% post-code

\item[mineral (inorganic substance)]
\printunsrtinnerglossary[type=minerals]{\let\glsnamefont\substancefont}{}

\item[animal (living organism)]
\printunsrtinnerglossary[type=animals]{\let\glsnamefont\substancefont}{}

\item[veggies (plants)]
\printunsrtinnerglossary[type=vegetables]{\let\glsnamefont\substancefont}{}

\item[mathematical symbols (Latin)]
\printunsrtinnerglossary[type=latinsymbols]{}{}

\item[mathematical symbols (Greek)]
\printunsrtinnerglossary[type=mathgreek]{}{}

\item[abbreviations (acronyms etc)]
\printunsrtinnerglossary[type=abbreviations]{}{}

\end{printunsrtglossarywrap}
\end{document}

If you don’t use arara, you need to run the following commands:

pdflatex innerglossaries2
bib2gls --provide-glossaries innerglossaries2
pdflatex innerglossaries2

(See Incorporating makeglossaries or makeglossaries-lite or bib2gls into the document build.)

Download: PDF (81.05K), source code (1.41K).

Nested Glossaries

This example uses \printunsrtinnerglossary inside the handler function \printunsrtglossaryhandler{label} to give the illusion of a single hierarchical glossary. This requires a style that supports hierarchy, such as the tree styles.

First consider a normal hierarchical glossary. The topics.bib contains:

% Encoding: UTF-8

@indexplural{animal,
 topicdescription={living organism that has specialised sense 
 organs and nervous system}
}

@indexplural{vegetable,
 topicdescription={plant or part of a plant used as food}
}

@indexplural{mineral,
 topicdescription={solid, inorganic, naturally-occurring substance}
}

@indexplural{abbreviation}
@indexplural{symbol}
@indexplural{markuplanguage,text={markup language}}
@indexplural{agency,plural={agencies}}

These are essentially topic titles. There are additional sub-topic titles in symboltopics.bib which contains:

% Encoding: UTF-8

@indexplural{latinsymbol,text={Latin symbol},topic={symbol}}
@indexplural{mathgreek,text={Greek symbol},topic={symbol}}
@indexplural{pictograph,topic={symbol}}

These have a custom field called topic.

The animals.bib file contains entries with another custom field called identifier. For example:

@entry{duck,
  name={duck},
  description={a waterbird with webbed feet},
  identifier={animal}
}

These custom fields will be ignored by bib2gls unless they’re defined or aliased to a known field. For example:

  field-aliases={identifier=parent,topic=parent},

This makes the “duck” entry in the animals.bib file a child of the “animal” entry that’s defined in the topics.bib file. It also makes all the entries in symboltopics.bib children of the “symbol” entry.

In this example, I’ve aliased these custom fields to type:

  field-aliases={identifier=type,topic=type},

which will assign the “duck” entry to the glossary with the label “animal” etc and the entries in symboltopics.bib will be assigned to the “symbol” glossary. Note that these glossaries have a label that’s identical to a topic or sub-topic entry label. The --provide-glossaries switch ensures that these glossaries are defined in the resource file that’s created by bib2gls.

The “main” glossary is now required for all the main topic entries (defined in topics.bib). Since these entries don’t have the type field set (they don’t have the custom identifier field) they will automatically be assigned to the default “main” glossary.

A custom handler function is defined:

\newcommand{\nestedhandler}[1]{%
  \glsxtrunsrtdo{#1}%
  \ifglossaryexists*{#1}%
  {%
    \printunsrtinnerglossary[type={#1},leveloffset=++1,groups=false]{}{}%
  }%
  {}%
}

This typesets the entry as usual with \glsxtrunsrtdo{label} but then tests if a glossary exists with the same label as the entry. This needs to use the starred form of \ifglossaryexists* which treats ignored glossaries as existing. If the glossary does exist, it’s then typeset using \printunsrtinnerglossary. The level offset is increment with leveloffset=++1, which makes the top-level entries of the nested glossary appear as though they were child entries. The automated group insertion must be cancelled with groups=false to avoid strange results.

All entries are then displayed with:

\printunsrtglossary*{\let\printunsrtglossaryhandler\nestedhandler}
example image

This locally sets the handler to the document’s custom handler. The complete document code is listed below. The initial comment lines are arara directives. You can remove them if you don’t use arara.

% arara: pdflatex
% arara: bib2gls: { group: on, options: [ --provide-glossaries ] }
% arara: pdflatex
\documentclass{article}

\usepackage{amssymb}
\usepackage[record,nostyles,stylemods=tree,style=treegroup]{glossaries-extra}[2020/03/23]% v1.44+

\setabbreviationstyle{short-long-desc}
\setabbreviationstyle[acronym]{short-long}

\GlsXtrLoadResources[
  src={topics,symboltopics,vegetables,minerals,animals,
       mathgreek,latinsymbols,pictographs,
       abbreviations},
  field-aliases={identifier=type,topic=type},
  selection={all},
  sort={en}
]

\newcommand{\nestedhandler}[1]{%
  \glsxtrunsrtdo{#1}%
  \ifglossaryexists*{#1}%
  {%
    \printunsrtinnerglossary[type={#1},leveloffset=++1,groups=false]{}{}%
  }%
  {}%
}

\begin{document}
\printunsrtglossary*{\let\printunsrtglossaryhandler\nestedhandler}
\end{document}

If you don’t use arara, you need to run the following commands:

pdflatex innerglossaries3
bib2gls --provide-glossaries --group innerglossaries3
pdflatex innerglossaries3

(See Incorporating makeglossaries or makeglossaries-lite or bib2gls into the document build.)

Download: PDF (81.00K), source code (828B).

Notes


As with the previous examples on this page, there’s a far simpler method to achieve the same result:

\documentclass{article}

\usepackage{amssymb}
\usepackage[record,nostyles,stylemods=tree,style=treegroup]{glossaries-extra}

\setabbreviationstyle{short-long-desc}
\setabbreviationstyle[acronym]{short-long}

\GlsXtrLoadResources[
  src={topics,symboltopics,vegetables,minerals,animals,
       mathgreek,latinsymbols,pictographs,
       abbreviations},
  field-aliases={identifier=parent,topic=parent},
  selection={all},
  sort={en}
]

\begin{document}
\printunsrtglossary
\end{document}

Different Ordering

The above examples could all be more easily reproduced with the methods used in Logical Glossary Divisions (type vs group vs parent) so you might be wondering what’s the point of \printunsrtinnerglossary. This example demonstrates a hierarchical effect with nested glossaries that can’t be reproduced with the parent field. In this case, the main topic entries are sorted in reverse alphabetical order, but the entries in the inner glossaries are sort according to the normal alphabetical order.

Only one sort method can be used for primary entries within a single resource set. It is possible to specify a different sort method for dual or secondary entries, but it’s not possible to use different sort methods for different hierarchical levels.

In order to use different sort methods for the different glossaries it’s necessary to split them into separate resource sets:

\GlsXtrLoadResources[
   src={topics},
   selection={all},
   sort={en-reverse}
]

\GlsXtrLoadResources[
  src={symboltopics,vegetables,minerals,animals,
       mathgreek,latinsymbols,pictographs,
       abbreviations},
  field-aliases={identifier=type,topic=type},
  selection={all},
  sort={en}
]

The rest of the code is the same as the previous example.

example image

The complete document code is listed below. The initial comment lines are arara directives. You can remove them if you don’t use arara.

% arara: pdflatex
% arara: bib2gls: { group: on, options: [ --provide-glossaries ] }
% arara: pdflatex
\documentclass{article}

\usepackage{amssymb}
\usepackage[record,nostyles,stylemods=tree,style=treegroup]{glossaries-extra}[2020/03/23]% v1.44+

\setabbreviationstyle{short-long-desc}
\setabbreviationstyle[acronym]{short-long}

\GlsXtrLoadResources[
   src={topics},
   selection={all},
   sort={en-reverse}
]

\GlsXtrLoadResources[
  src={topics,symboltopics,vegetables,minerals,animals,
       mathgreek,latinsymbols,pictographs,
       abbreviations},
  field-aliases={identifier=type,topic=type},
  selection={all},
  sort={en}
]

\newcommand{\nestedhandler}[1]{%
  \glsxtrunsrtdo{#1}%
  \ifglossaryexists*{#1}%
  {%
    \printunsrtinnerglossary[type={#1},leveloffset=++1,groups=false]{}{}%
  }%
  {}%
}

\begin{document}
\printunsrtglossary*{\let\printunsrtglossaryhandler\nestedhandler}
\end{document}

If you don’t use arara, you need to run the following commands:

pdflatex innerglossaries4
bib2gls --provide-glossaries --group innerglossaries4
pdflatex innerglossaries4

(See Incorporating makeglossaries or makeglossaries-lite or bib2gls into the document build.)

Download: PDF (80.99K), source code (911B).

Note that the symbol sub topics are in the normal alphabetical order. If these also need to be in reverse order then the symboltopics.bib file should be moved to the first resource set. Remember that the custom topic field alias will also need to be moved:

\GlsXtrLoadResources[
   src={topics,symboltopics},
   field-aliases={topic=type},
   selection={all},
   sort={en-reverse}
]

\GlsXtrLoadResources[
  src={vegetables,minerals,animals,
       mathgreek,latinsymbols,pictographs,
       abbreviations},
  field-aliases={identifier=type},
  selection={all},
  sort={en}
]