@jarenjs/linq 0.34.2 → 0.43.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -42,6 +42,16 @@ played by the provider seam below.
42
42
  concurrency bound and the `parallel`/`concat`/`switch`/`exhaust`
43
43
  vocabulary; barrier operators buffer and run through the one engine
44
44
  so streaming answers equal in-memory answers by construction.
45
+ - **Geography is spellable.** The whole §8.14 family is on the
46
+ expression surface: `p.location.within(region)`,
47
+ `p.location.distance(here)`, `p.route.geoLength()`,
48
+ `p.location.geohash(6)`, and the conversion pair `geoParse`/`geoText`
49
+ that reads a WKT column and writes one back. A plain GeoJSON object
50
+ embeds as a literal; `.params({ region })` binds it at call time
51
+ instead, which is the shape a spatial index can be probed with. The
52
+ spatial measurements are `geoArea`/`geoLength` because `length` on
53
+ this surface is already `$string-length` — the mapping table says so
54
+ in its own row.
45
55
  - **The provider contract.** Any object with
46
56
  `execute(queryDocument, { externals })` is a provider.
47
57
  `@jarenjs/db` implements it — a chain over a SQLite-backed
@@ -115,7 +115,22 @@ is part of THIS design.
115
115
  | `Cast<S>` | `$assert` per item | native | `(schema)` → `Seq<S>`; needs `compileTypeTest` (`JL0003`) |
116
116
  | `Zip` | — no positional co-iteration in the grammar | unsupported (`JL0006`) | — |
117
117
  | expression methods | `eq ne lt le gt ge` → `$eq…$ge`; `and or not`; `add sub mul div idiv mod neg`; `startsWith endsWith contains matches upper lower length concat substring replace` → §8.7; `count sum avg min max` → §8.8 (aggregates as expressions, e.g. over a group); `year month day epoch` → §8.13; `exists isEmpty`; `at all get` | native | on `Expr<…>`, per the typed-surface order |
118
- | spatial family (§8.14) | surface deferred to the relational order; hand-write the document (`fromDocument`) today | unsupported (`JL0006`-adjacent: no methods exist yet) | |
118
+ | spatial family (§8.14) | `bbox geoArea geoLength centroid` `$bbox $area $length $centroid`; `distance within bboxIntersects` → `$distance $within $bbox-intersects`; `geohash(precision?)` `$geohash` (optional arity, like `substring`); `geoParse geoText geohashBounds geohashNeighbours` → the conversion family; `geoSimplify(tolerance)` `$geo-simplify`. A plain JSON polygon embeds as a literal (`p.at.within(poly)`); `.params({ region })` makes it an external instead | native | on `Expr<…>`, per the typed-surface order |
119
+
120
+ Two spatial names are deliberately not the obvious ones, and the reason
121
+ is the same one that made §8.14's `$length` and §8.7's `$string-length`
122
+ two operators: **`length` on this surface is already `$string-length`**,
123
+ and §8.14's `$length` is a geodesic line measurement. One method name
124
+ cannot carry both, and renaming the shipped string method for symmetry
125
+ would break a published surface for a cosmetic gain — so the spatial one
126
+ is **`geoLength`**, and **`geoArea`** joins it, because a bare `area()`
127
+ on an arbitrary expression reads as arithmetic to a C# eye. The prefix
128
+ names the family the way `geoParse`/`geoText` already do.
129
+
130
+ Every method name shadows a data member of the same name — that is what
131
+ the null prototype on the method table is for, and what `get(name)`
132
+ escapes. A position stored as `at` is the case that bites: `p.at` is the
133
+ index method, so it reads `p.get('at').within(region)`.
119
134
 
120
135
  ## 5. Deferred execution and re-enumeration
121
136
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jarenjs/linq",
3
3
  "private": false,
4
- "version": "0.34.2",
4
+ "version": "0.43.3",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
7
7
  "types": "./types/index.d.ts",
@@ -47,7 +47,7 @@
47
47
  "prepack": "npm run build:types"
48
48
  },
49
49
  "dependencies": {
50
- "@jarenjs/core": "^0.34.2",
51
- "@jarenjs/json": "^0.34.2"
50
+ "@jarenjs/core": "^0.43.3",
51
+ "@jarenjs/json": "^0.43.3"
52
52
  }
53
53
  }
package/src/expression.js CHANGED
@@ -216,6 +216,27 @@ const METHODS = {
216
216
  // arrives with the relational order)
217
217
  year: unary('$year'), month: unary('$month'), day: unary('$day'),
218
218
  epoch: unary('$epoch'),
219
+ // §8.14 spatial. `geoArea`/`geoLength` rather than `area`/`length`:
220
+ // `length` is already `$string-length` on this surface and renaming a
221
+ // shipped method for symmetry is a breaking change for a cosmetic
222
+ // gain, while a bare `area()` on an arbitrary expression reads as
223
+ // arithmetic. The prefix names the family the way `geoParse`/`geoText`
224
+ // do, and it is the same reason `$length` and `$string-length` are two
225
+ // operators in the first place.
226
+ bbox: unary('$bbox'), geoArea: unary('$area'),
227
+ geoLength: unary('$length'), centroid: unary('$centroid'),
228
+ distance: binary('$distance'), within: binary('$within'),
229
+ bboxIntersects: binary('$bbox-intersects'),
230
+ geoParse: unary('$geo-parse'), geoText: unary('$geo-text'),
231
+ geohashBounds: unary('$geohash-bounds'),
232
+ geohashNeighbours: unary('$geohash-neighbours'),
233
+ geoSimplify: binary('$geo-simplify'),
234
+ geohash(record, precision) {
235
+ const args = precision === undefined
236
+ ? record.doc
237
+ : [record.doc, toExpression(precision)];
238
+ return makeExpr({ $geohash: args }, record.epoch, false);
239
+ },
219
240
  // path navigation
220
241
  at(record, index) {
221
242
  if (record.pathable && Number.isInteger(index)) {