Querying a tree with GOQL¶
GOQL, the Gramps Object Query Language, is the filter language of Gramps Web
API's structured search. It reads like one Python boolean expression and is
written against one object type at a time (people, families, events, places,
citations, sources, repositories, media, notes). It is a feature of
Gramps Web API and its
gramps-object-query-language
library, not something this project defines. The full reference is in that
library's docs/where_expr.md; this page covers what an agent needs most.
Requires Gramps Web API 3.21 or newer for the query tools, and 3.23 or newer
for the MatchesQuery filter rule.
Two ways to use it¶
1. The query_objects tool (with an object_type such as people) takes a
JSON body. This is the better choice: it can return only the fields you ask
for, sort, page and count.
{
"where_expr": "gender == Person.FEMALE and like(birth.place.title, '%TX%')",
"select": ["gramps_id", "primary_name.first_name", "birth.place.title as place"],
"order_by": [{"column": "birth.date.sortval", "direction": "asc"}],
"limit": 20,
"count": true
}
| Field | Meaning |
|---|---|
where_expr |
The GOQL expression, as a string. |
select |
Values to return. Defaults to everything, which is large: always select. |
order_by |
List of {"column": ..., "direction": "asc" or "desc"}. |
limit |
Rows per page, 1 to 1000, default 50. |
after |
The next_after cursor of the previous page, for the next one. |
count |
If true, the total number of matches is returned in the X-Total-Count header. |
The response has items and, when there are more rows, next_after.
2. The rules parameter of list_objects accepts the same expression through the MatchesQuery rule:
Writing expressions¶
A field is a dotted path, with [n] to pick a list element:
gender, primary_name.surname_list[0].surname, birth.date.sortval.
| To say | Write |
|---|---|
| equals, differs | ==, != |
| order | <, <=, >, >= |
| and, or, not | and, or, not (...) |
| one of a list | primary_name.first_name in ['John', 'Mary'] |
| contains text | 'Ann' in primary_name.first_name |
| pattern, case-insensitive | like(field, 'Gar%') (% is any text, _ one character) |
| regular expression | regex(primary_name.first_name, '^Ann') |
| a date | Date('Jan 1, 1968'), compared with .sortval |
Fixed values have names: Person.MALE, Person.FEMALE, EventType.BIRTH,
FamilyRelType.MARRIED, and so on. Types such as an event's type store
their number in .value, so write type.value == EventType.BIRTH.
Crossing relationships¶
A path can continue into a related object:
| From | Name | Goes to |
|---|---|---|
| Person | birth, death |
their birth or death event |
| Family | father, mother |
the parent |
| Event | place |
the place |
| Citation | source |
the source |
| Place | enclosed_by |
the enclosing place |
So birth.place.title is a person's birth place, and paths chain freely
(father.birth.date.sortval). Two paths can be compared to each other.
Families and their children¶
any(children) is true for a family with at least one child;
any(children, gender == Person.FEMALE) for one with a daughter;
len(children) >= 4 counts them.
Examples¶
These all ran against the Gramps example tree.
| Question | Type | where_expr |
|---|---|---|
| Men | people | gender == Person.MALE |
| Surnames starting with Gar | people | like(primary_name.surname_list[0].surname, 'Gar%') |
| Born in 1968 or later | people | birth.date.sortval >= Date('Jan 1, 1968') |
| Born in the 1900s | people | birth.date.sortval >= Date('Jan 1, 1900') and birth.date.sortval < Date('Jan 1, 1910') |
| Women born in Texas | people | like(birth.place.title, '%TX%') and gender == Person.FEMALE |
| Mother died before father | families | mother.death.date.sortval < father.death.date.sortval |
| Families with 4 children or more | families | len(children) >= 4 |
| Birth events | events | type.value == EventType.BIRTH |
Things to know¶
- Do not guess field names. An unknown field is rejected with a 422 whose message lists the fields that exist for that type. Read it and retry.
- Select what you need.
selectaccepts paths and renames withas:"birth.date.dateval as born". - Sorting by a path such as
birth.date.sortvalputs people without that value first when ascending. - Ask for the total with
count: truerather than paging through everything to count it. sortvalis a plain number that orders dates. It ignores qualifiers such as "about" or "before", so it cannot tell "about 1900" from "1900".