jinaga
Version:
Data management for web and mobile applications.
113 lines (80 loc) • 3.81 kB
Markdown
# Querying for `predecessor`
The `predecessor()` method allows you to navigate from a fact to its direct predecessor in a specification. This provides a convenient way to traverse relationships in the reverse direction.
## Example of Using the `predecessor` Method
In the context of a company model, you can use the `predecessor()` method to find the company that an office belongs to:
```typescript
const specification = model.given(Office).match(office =>
office.company.predecessor()
);
const result = await j.query(specification, office);
```
In this example, the `predecessor()` method is used to navigate from an office to its company.
## Compared to the `join` Method
The alternative to the `predecessor()` syntax in Jinaga is to use the `join` method with `facts.ofType()`:
```typescript
const specification = model.given(Office).match((office, facts) =>
facts.ofType(Company)
.join(company => company, office.company)
);
const result = await j.query(specification, office);
```
The `predecessor()` method provides a more concise and readable way to express the same query.
## Using the `predecessor` Method with Projections
You can use the `predecessor()` method with projections to select specific fields or create composite results:
```typescript
const specification = model.given(Office).match(office =>
office.company.predecessor()
.select(company => company.identifier)
);
const result = await j.query(specification, office);
```
For composite projections that include predecessor relationships, you need to properly label the predecessor facts:
```typescript
const specification = model.given(Office).match((office, facts) =>
office.company.predecessor()
.select(company => ({
identifier: company.identifier,
creator: facts.ofType(User)
.join(user => user, company.creator)
}))
);
const result = await j.query(specification, office);
```
## Chaining Predecessor Calls
You can chain multiple `predecessor()` calls to navigate through multiple levels of relationships:
```typescript
const specification = model.given(President).match(president =>
president.office.company.predecessor()
);
const result = await j.query(specification, president);
```
This example navigates from a president to their office, and then to the company of that office.
## Combining with Existential Conditions
The `predecessor()` method can be used with existential conditions:
```typescript
const specification = model.given(OfficeClosed).match(officeClosed =>
officeClosed.office.predecessor()
.exists(office => office.company.predecessor())
);
const result = await j.query(specification, officeClosed);
```
## Combining with Successors
You can combine `predecessor()` and `successors()` methods in the same query:
```typescript
const specification = model.given(Company).match(company =>
company.successors(Office, office => office.company)
.select(office => ({
identifier: office.identifier,
presidents: office.successors(President, president => president.office)
.selectMany(president => president.user.predecessor()
.select(user => ({
user: user,
names: user.successors(UserName, userName => userName.user)
.select(userName => userName.value)
}))
)
}))
);
const result = await j.query(specification, company);
```
This example shows how to navigate from a company to its offices (using `successors`), then to the presidents of those offices (using `successors`), then to the users who are those presidents (using `predecessor`), and finally to the names of those users (using `successors`).