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
48 changes: 0 additions & 48 deletions .changeset/computed-expressions-and-updates.md

This file was deleted.

51 changes: 51 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,56 @@
# Changelog

## 2.1.0

### Minor Changes

- [#31](https://github.com/Semantu/linked/pull/31) [`eb88865`](https://github.com/Semantu/linked/commit/eb8886564f2c9663805c4308a834ca615f9a1dab) Thanks [@flyon](https://github.com/flyon)! - Properties in `select()` and `update()` now support expressions — you can compute values dynamically instead of just reading or writing raw fields.

### What's new

- **Computed fields in queries** — chain expression methods on properties to derive new values: string manipulation (`.strlen()`, `.ucase()`, `.concat()`), arithmetic (`.plus()`, `.times()`, `.abs()`), date extraction (`.year()`, `.month()`, `.hours()`), and comparisons (`.gt()`, `.eq()`, `.contains()`).

```typescript
await Person.select((p) => ({
name: p.name,
nameLen: p.name.strlen(),
ageInMonths: p.age.times(12),
}));
```

- **Expression-based WHERE filters** — filter using computed conditions, not just equality checks. Works on queries, updates, and deletes.

```typescript
await Person.select((p) => p.name).where((p) => p.name.strlen().gt(5));
await Person.update({ verified: true }).where((p) => p.age.gte(18));
```

- **Computed updates** — when updating data, calculate new values based on existing ones instead of providing static values. Pass a callback to `update()` to reference current field values.

```typescript
await Person.update((p) => ({ age: p.age.plus(1) })).for(entity);
await Person.update((p) => ({
label: p.firstName.concat(" ").concat(p.lastName),
})).for(entity);
```

- **`Expr` module** — for expressions that don't start from a property, like the current timestamp, conditional logic, or coalescing nulls.

```typescript
await Person.update({ lastSeen: Expr.now() }).for(entity);
await Person.select((p) => ({
displayName: Expr.firstDefined(p.nickname, p.name),
}));
```

Update expression callbacks are fully typed — `.plus()` only appears on number properties, `.strlen()` only on strings, etc.

### New exports

`ExpressionNode`, `Expr`, `ExpressionInput`, `PropertyRefMap`, `ExpressionUpdateProxy<S>`, `ExpressionUpdateResult<S>`, and per-type method interfaces (`NumericExpressionMethods`, `StringExpressionMethods`, `DateExpressionMethods`, `BooleanExpressionMethods`, `BaseExpressionMethods`).

See the [README](./README.md#computed-expressions) for the full method reference and more examples.

## 2.0.1

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@_linked/core",
"version": "2.0.1",
"version": "2.1.0",
"license": "MIT",
"description": "Linked.js core query and SHACL shape DSL (copy-then-prune baseline)",
"repository": {
Expand Down