Data expressions are dot-notation paths for reading and writing genealogy data in Pedigree Forge. They provide a uniform way to navigate the record structure — the same path syntax works everywhere:
A data expression starts with a root type, followed by one or more field segments separated by dots:
person.birth.date
This reads as: starting from a person record, navigate to their birth fact, then return its date.
Fields come in three kinds:
| Kind | What it does | Example |
|---|---|---|
| Terminal | Returns a string value | surname, date, name |
| Navigation | Links to another record type | father (person), place (place) |
| Collection | An indexed list of records | children, facts |
Terminal fields are the leaves — they produce a value you can display or compare. Navigation fields move you to a different record, where you can continue with that record’s fields. Collection fields give you access to a list of records, which you can index, filter, or count.
Read a value with pf.get:
pf.get("I1", "person.birth.date") – "1 Jan 1900"
pf.get("I1", "person.surname") – "Darwin"
Write a value with pf.set:
pf.set("I1", "person.birth.date", "1 Jan 1900")
pf.set("I1", "person.surname", "Darwin")
Run a query with pf.query:
pf.query("SELECT name FROM persons WHERE surname = ’Darwin’")
This returns a table of rows matching the query.
| Field | Writable | Description |
|---|---|---|
| id | No | Record ID |
| name | No | Full formatted name |
| surname | Yes | Surname |
| given_names | Yes | Given names |
| sex | Yes | Sex (M/F/U) |
| trade | Yes | Occupation |
| life_dates | No | Formatted life dates |
| nickname | Yes | Nickname |
| name_prefix | Yes | Name prefix |
| name_suffix | Yes | Name suffix |
| title | Yes | Formal title |
| first_given_name | No | First given name only |
| gender | No | Gender description |
| pronoun | No | Pronoun (he/she/they) |
| possessive_pronoun | No | Possessive pronoun (his/her/their) |
| religion | Yes | Religion |
| nationality | Yes | Nationality |
| son_or_daughter | No | "son" or "daughter" |
| boy_or_girl | No | "boy" or "girl" |
| brother_or_sister | No | "brother" or "sister" |
| husband_or_wife | No | "husband" or "wife" |
| father_or_mother | No | "father" or "mother" |
| bachelor_or_spinster | No | "bachelor" or "spinster" |
| living | Yes | Whether flagged as living |
| user_reference | Yes | User reference number |
| fullname | No | Full name with dates |
| Field | Target type |
|---|---|
| father | person |
| mother | person |
| spouse | person |
| birth | fact |
| death | fact |
| baptism | fact |
| burial | fact |
| cremation | fact |
| family_as_child | family |
| Field | Element type |
|---|---|
| children | person |
| families_as_spouse | family |
| facts | fact |
| Field | Writable | Sorts as | Description |
|---|---|---|---|
| date | Yes | Chronological | Date of the event. Sorts by internal serial number; non-Gregorian calendars are normalised to Gregorian. |
| kind | No | Text | Fact type code (e.g. "BIRT", "CENS") — use in WHERE filters and bracket expressions |
| label | No | Text | Human-readable event name (e.g. "Birth", "Census") — use in SELECT for display |
| address | Yes | Text | Address |
| note | Yes | Text | Note text |
| cause | Yes | Text | Cause (e.g. cause of death) |
| age | Yes | Duration | Age at time of event. Sorts by total duration; vague ages are assigned approximate values for sorting (stillborn = 0, infant ~ 1 year, child ~ 8 years, minor ~ 16 years, full age ~ 21 years) and sort before numeric ages. |
| time | Yes | Text | Time of event |
| type | Yes | Text | Subtype |
| agency | Yes | Text | Agency |
| fact_trade | Yes | Text | Occupation at time of event |
| status | Yes | Text | Status |
| role | Yes | Text | Role |
| default_value | Yes | Text | Default value |
| with | Yes | Text | With whom |
| whom | Yes | Text | Whom |
| likelihood | Yes | Text | Likelihood |
| uncertain | Yes | Text | Uncertainty flag |
| other_address | Yes | Text | Other address |
These fields are available on marriage facts, providing details about each partner:
| Field | Writable | Sorts as |
|---|---|---|
| husband_age | Yes | Duration |
| wife_age | Yes | Duration |
| husband_address | Yes | Text |
| wife_address | Yes | Text |
| husband_status | Yes | Text |
| wife_status | Yes | Text |
| husband_trade | Yes | Text |
| wife_trade | Yes | Text |
| Field | Target type |
|---|---|
| place | place |
| other_place | place |
| husband_place | place |
| wife_place | place |
| Field | Element type |
|---|---|
| citations | citation |
| Field | Writable | Description |
|---|---|---|
| id | No | Family ID |
| names | No | Combined couple names |
| status | Yes | Couple status |
| never_married | Yes | Never married flag |
| divorced | Yes | Divorced flag |
| separated | Yes | Separated flag |
| stated_child_count | Yes | Stated number of children |
| user_reference | Yes | User reference |
| main_notes | Yes | Main notes |
| Field | Target type |
|---|---|
| husband | person |
| wife | person |
| marriage | fact |
| divorce | fact |
| Field | Element type |
|---|---|
| children | person |
| facts | fact |
| Field | Writable | Description |
|---|---|---|
| id | No | Source ID |
| title | Yes | Title |
| short_title | Yes | Short title |
| author | Yes | Author |
| publisher | Yes | Publisher |
| call_number | Yes | Call number |
| call_number_note | Yes | Call number note |
| source_type | Yes | Source type |
| group | Yes | Group |
| fact_type | Yes | Associated fact type |
| url | Yes | URL |
| Field | Target type |
|---|---|
| archive | archive |
| Field | Writable | Description |
|---|---|---|
| id | No | Archive ID |
| name | Yes | Name |
| phone | Yes | Phone |
| Yes | ||
| www | Yes | Website |
| fax | Yes | Fax |
| address_0 | Yes | Address line 1 |
| address_1 | Yes | Address line 2 |
| address_2 | Yes | Address line 3 |
| address_3 | Yes | Address line 4 |
| town | Yes | Town |
| county | Yes | County |
| postcode | Yes | Postcode |
| country | Yes | Country |
| note | Yes | Notes |
All place fields are read-only.
| Field | Description |
|---|---|
| name | Place name |
| town | Town |
| county | County |
| country | Country |
| preposition | Preposition (in, at, etc.) |
All citation terminal fields are read-only.
| Field | Description |
|---|---|
| description | Citation description |
| page | Page reference |
| note | Note |
| is_local | Whether citation is local |
| Field | Target type |
|---|---|
| source | source |
Date fields support sub-fields for accessing individual components. Append them after any date field:
| Sub-field | Sorts as | Returns |
|---|---|---|
| .year | Integer | Year number (normalised to Gregorian for non-Gregorian calendars) |
| .month | Integer | Month number (1-12) |
| .day | Integer | Day number (1-31) |
| .weekday | Text | Day of week name |
| .full | Text | Full format (e.g. "1 January 1900") |
| .compact | Text | Compact format (e.g. "1 Jan 1900") |
| .minimal | Text | Minimal format (e.g. "1900") |
| .decorated | Text | Decorated format with qualifiers |
For example, person.birth.date.year returns "1809" for Charles Darwin.
Navigation fields link to another record type, so you can keep adding fields to drill deeper:
person.father.birth.date – father’s birth date
person.mother.surname – mother’s maiden name
person.spouse.death.place.name – spouse’s death place
Each navigation step changes the current record type. After person.father you are on a person record, so all person fields are available again. After .birth you are on a fact, so fact fields (like date and place) apply.
Collection fields return a list of records. Access individual elements with square brackets:
person.children[0] – first child
person.children[-1] – last child
person.families_as_spouse[0].marriage.date – first marriage date
Use .count to get the number of elements:
person.children.count – number of children
Note — Children are aggregated across all marriages.
Narrow a collection to matching elements using [property=value] syntax:
person.facts[kind=census] – all census facts
person.facts[kind=census & year=1881] – 1881 census specifically
person.children[sex=M] – male children
person.children[sex=M].count – count of male children
| Operator | Meaning | |
|---|---|---|
| = | Equals | |
| != | Not equals | |
| < | Less than | |
| > | Greater than | |
| <= | Less than or equal | |
| >= | Greater than or equal | |
| *= | Contains | |
| ~= | Not equals (alternative) | |
| ^= | Starts with | |
| $= | Ends with | |
| \ | = | Sounds like |
Combine conditions within a filter using logical operators:
| Operator | Meaning | |
|---|---|---|
| & | AND | |
| \ | OR | |
| ! | NOT |
Use parentheses for grouping:
person.facts[kind=census & (year=1881 | year=1891)]
The properties available inside filters depend on the collection’s element type:
Fact filters: kind, year, place, date, note
Person filters: sex, year, name, surname
Family filters: year
Expressions can include $parameter placeholders that are replaced at runtime:
These are used in gizmo bound widgets (where parameter values come from the binding context) and in parameterised queries (where the user is prompted for values).
pf.get("I1", "person.father.name") – "Robert Darwin"
pf.get("I1", "person.mother.name") – "Susannah Wedgwood"
pf.get("I1", "person.birth.date") – "12 Feb 1809"
pf.get("I1", "person.birth.place.name") – "The Mount, Shrewsbury"
pf.get("I1", "person.families_as_spouse[0].children.count") – "10"
pf.get("I1", "person.families_as_spouse[0].children[0].name")
pf.get("I1", "person.facts[kind=census & year=1881].date")
pf.get("I1", "person.facts[kind=census].count") – "5"
pf.get("S1", "source.title")
pf.get("S1", "source.archive.name")