Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/unreleased/query-feature-outcomes.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- `Project` and `WhereFeature` reading a feature an element row lacks now fail with a `missing-feature` error naming the element and the features it does have (`Target`, `Property`, `Candidates`), instead of leaving the cell empty, and carry the QVTS outcome record (`Feature(<name>)` step, the element and its features as frontier); a property no row carries stays `unknown-property`. The new `tolerate = true` argument on both operations keeps the empty cell for sources that mix element kinds (the cookbook's `MassTable` uses it). Metadata paths (`Tag::key`) are unaffected.
5 changes: 3 additions & 2 deletions docs/internals/design/query-outcomes.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ to `∅` is the ambiguity QVTS exists to remove.
| `Named` | the element | `unknown-element`: the longest prefix that resolves, the segment it lacks, the nearest spellings; an alias that denotes nothing | `ambiguous-element`: the elements, by qualified name |
| `WhereType`, `WhereMetadata` | the selected rows | `unknown-classification` | — |
| `RelatedElements` | the neighbours, possibly none | `unknown-relationship` | — |
| `Project`, `WhereFeature` on a feature the element lacks | *today an empty cell* `(none)` | *to do:* a path error per element, with the features reachable on it | — |
| `Project`, `WhereFeature` on a feature the element lacks | an empty cell, with `tolerate = true` | `missing-feature`: the element (`Target`), the feature (`Property`), the features it does have (`Candidates`); `unknown-property` when no row carries it | — |
| `Project`, `WhereFeature` on a metadata path (`Tag::key`) the element lacks | *today an empty cell* | *to do:* decide whether an absent annotation is a path error | — |
| `Project` on a `[1]` feature with two values | *today the first value* | — | *to do:* `multiplicity` |

`Error` carries the frontier in `Target` (the namespace that resolved), `Expected` (the segment it
Expand All @@ -38,7 +39,7 @@ QVTS record itself (`internal/doc/queryexec/outcome.go`), field for field with
`lean/QVTS/Outcome.lean`: a `PathError` is a list of `Failure{Step, Expected, Frontier}`, a
`ModelError` a list of `Finding{Step, Element, Constraint, Observed}` plus the path failures of the
same fan-out; `Frontier` carries `At`, `Members (name, metaclass)`, `Features (name, type)`, `Kinds`
and `Nearest`. `Named` fills it today. The surfaces that show it: `-run-query` prints it under the
and `Nearest`. `Named` and the `missing-feature` outcome of `Project`/`WhereFeature` fill it today. The surfaces that show it: `-run-query` prints it under the
error line and `-json` writes it as `checks[].outcome` in the shape `qvts/sparql/expected/*.json`
records (`outcome`, `step`, `failures` | `findings` + `pathFailures`); `%run-query` prints the same
lines; the gRPC service attaches it to the failed call as a `QueryOutcome` error detail. The
Expand Down
4 changes: 3 additions & 1 deletion docs/manual/examples/cookbook.sysml
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,8 @@ package Cookbook {
in root : Element;
Project(
source = PartsByMass(root = root),
properties = ("name", "mass", "qualifiedName")
properties = ("name", "mass", "qualifiedName"),
tolerate = true // opticalPath and dataPath declare no mass
)
}

Expand All @@ -173,6 +174,7 @@ package Cookbook {
Project(
source = PartsByMass(root = root),
properties = ("name", "mass"),
tolerate = true,
columns = (
Column(name = "massLbs", cell = { in row : Subsystem; (row.mass ?? 0.0) * 2.2 }),
Column(name = "label", expression = "part: " + Element::name)
Expand Down
25 changes: 21 additions & 4 deletions docs/manual/query-cookbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ calc def MassTable :> Query {
in root : Element; // entry parameters, bound by the caller
Project( // operations compose inside-out
source = PartsByMass(root = root), // ... and queries invoke queries
properties = ("name", "mass", "qualifiedName")
properties = ("name", "mass", "qualifiedName"),
tolerate = true // an element lacking a property gets an empty cell
)
}
```
Expand Down Expand Up @@ -550,7 +551,8 @@ calc def MassTable :> Query {
in root : Element;
Project(
source = PartsByMass(root = root),
properties = ("name", "mass", "qualifiedName")
properties = ("name", "mass", "qualifiedName"),
tolerate = true // opticalPath and dataPath declare no mass
)
}
```
Expand Down Expand Up @@ -581,8 +583,22 @@ $ sysml cookbook.sysml -run-query "Cookbook::MassTable root=Cookbook::telescope"
qualifiedName = "Cookbook::telescope::dataPath"
```

A cell for a property the element lacks is empty (`(none)` in the CLI's row
listing, an empty table cell in a document). A feature whose declared value
`opticalPath` and `dataPath` are connections: they declare no `mass`. Reading
a feature of an element that has none so named is by default an error that
names the element and the features it does have — a path error, telling a
query that left the model apart from a feature that is present and empty:

```console
$ sysml cookbook.sysml -run-query "Cookbook::MassTable root=Cookbook::telescope" # without tolerate = true
error: query Cookbook::MassTable reads feature mass of Cookbook::telescope::opticalPath, which has no such feature
```

`tolerate = true` on `Project` (and on `WhereFeature`) accepts the mixed rows
and leaves the cell empty (`(none)` in the CLI's row listing, an empty table
cell in a document). A property no row carries at all is an unknown property
whichever way — that is a misspelling, not a mixed source. A metadata path
(`Properties::key`) is always tolerated: an annotation is optional by nature.
A feature whose declared value
names an element of the model — an enumeration literal, a part, a unit — holds
that element, printed by name like `general`, whether the value is written on
the feature or bound by a redefinition (`attribute :>> beam = Beam::'650mm';`);
Expand Down Expand Up @@ -785,6 +801,7 @@ calc def MassBudget :> Query {
Project(
source = PartsByMass(root = root),
properties = ("name", "mass"),
tolerate = true,
columns = (
Column(name = "massLbs", cell = { in row : Subsystem; (row.mass ?? 0.0) * 2.2 }),
Column(name = "label", expression = "part: " + Element::name)
Expand Down
Loading
Loading