Skip to content

Commit 85d7175

Browse files
authored
Merge pull request #361 from rush-db/feat/better-date-type-inference
Date type inference improvement and minor UX fixes
2 parents 45f3716 + a45ab8d commit 85d7175

49 files changed

Lines changed: 860 additions & 258 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/eleven-crews-pull.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
'@rushdb/javascript-sdk': minor
3+
'rushdb-core': patch
4+
'rushdb-dashboard': patch
5+
'@rushdb/docs': patch
6+
---
7+
8+
**Embedding index cold-start** (`rushdb-core`): `POST /ai/indexes` no longer requires the indexed property to exist in Neo4j before creating the index policy. When no property node exists (no records with that property have been created yet), type validation is skipped — the property will be created naturally when the first record carrying it is written. Previously the server threw `NotFoundException`.
9+
10+
**`DBRecordInstance.score` getter** (`@rushdb/javascript-sdk`): Records returned by `records.vectorSearch()` now expose a typed `score` getter (`number | undefined`) that mirrors `data.__score`. Regular `find()`/`search()` results return `undefined`.
11+
12+
**`records.createMany` vectors overload** (`@rushdb/javascript-sdk`): The `vectors` parameter now accepts both `VectorEntry[]` and `VectorEntry[][]`. A flat `VectorEntry[]` list is auto-wrapped into per-record entries, so single-record batches can omit the outer array nesting.
13+
14+
**Import form label reset** (`rushdb-dashboard`): The label field in the import data page is now cleared whenever the user goes back to the method-selection step, uploads a new file, or switches to the CSV editor — preventing stale labels from persisting across import iterations.
15+
16+
**Docs updated** (`docs`): `indexing.md` revised to reflect that index creation no longer requires the property to already exist. `write-with-vectors.mdx` and `bring-your-own-vectors.mdx` document the flat `VectorEntry[]` overload. `semantic-search.mdx` mentions the `record.score` convenience getter. TS reference docs (`RushDB.md`, `DBRecordInstance.md`) describe the flat `vectors` form and the `score` getter. Python reference docs (`RushDB.md`) mention the flat `vectors` form. All tutorial code examples (15 `.mdx` files) now prefer the `.score` getter over raw `__score` access. `manage-indexes.mdx` error reference updated to remove the stale 404 row (property no longer needs to exist). All incorrect `where: { __id: ... }` filter patterns replaced with `where: { $id: ... }` across 12 tutorial files (TS, Python, and Shell code blocks).

.changeset/two-bears-hunt.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'rushdb-core': minor
3+
'rushdb-docs': minor
4+
'@rushdb/javascript-sdk': minor
5+
'@rushdb/mcp-server': minor
6+
'@rushdb/skills': minor
7+
'rushdb-dashboard': minor
8+
---
9+
10+
Loosen datetime detection on import: `YYYY-MM-DD` (date-only) strings are now automatically typed as `datetime`, not just full ISO 8601 timestamps. This means values like `2026-07-23` work the same as `2026-07-23T12:00:00Z` — they get datetime comparisons, time-based aggregations, and the correct `__proptypes` entry — without any extra configuration.

docs/docs/learn/records-and-queries/import-data.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ graph LR
3939
### The ingestion pipeline
4040

4141
1. **Parse** — BFS walk. Each nested object becomes a separate record.
42-
2. **Type inference** — Every value is classified as `string`, `number`, `boolean`, or `datetime`. A `null` value (or an all-`null` array) is treated as unset and is not stored.
42+
2. **Type inference** — Every value is classified as `string`, `number`, `boolean`, or `datetime`. Both ISO 8601 strings (`2026-07-23T12:00:00Z`) and date-only strings (`2026-07-23`) are inferred as `datetime`. A `null` value (or an all-`null` array) is treated as unset and is not stored.
4343
3. **Label assignment** — Top-level arrays and object records use the label you provide. Container objects can omit `label` when each top-level value is an object or an array of nested records; each top-level key becomes the label for its nested records. Nested objects derive their label from the parent key name (e.g., key `"engine"` → label `Engine`).
4444
4. **Relationship creation** — Parent → child records are linked with default relationships (`__RUSHDB__RELATION__DEFAULT__`).
4545

docs/docs/learn/reference/python/RushDB.md

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -41,24 +41,24 @@ All database operations are accessed through sub-namespaces on the client instan
4141

4242
CRUD and bulk operations on records.
4343

44-
| Method | Description |
45-
| ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
46-
| `create(label, data, *, options, vectors, transaction)` | Create a single record |
47-
| `create_many(label, data, *, options, vectors, transaction)` | Create multiple flat records |
48-
| `import_json(data, label, *, options, transaction)` | Import nested/complex JSON payloads; `label` may be omitted for container objects whose top-level values are objects or arrays of nested records |
49-
| `import_csv(label, data, *, options, parse_config, vectors, transaction)` | Import records from CSV text |
50-
| `upsert(data, label, *, options, vectors, transaction)` | Create or update a record |
51-
| `set(target, data, *, label, vectors, transaction)` | Replace all fields of a record |
52-
| `update(target, data, *, transaction)` | Partially update a record |
53-
| `find(search_query, *, record_id, transaction)` | Search records; returns `SearchResult` |
54-
| `find_one(search_query, *, transaction)` | Return the first match or `None` |
55-
| `find_uniq(search_query, *, transaction)` | Return the single match or `None`; raises if more than one |
56-
| `find_by_id(target, *, transaction)` | Fetch record(s) by ID |
57-
| `delete(search_query, *, transaction)` | Delete all records matching a query |
58-
| `delete_by_id(target, *, transaction)` | Delete record(s) by ID |
59-
| `attach(source, target, *, options, transaction)` | Create relationships between records |
60-
| `detach(source, target, *, options, transaction)` | Remove relationships between records |
61-
| `export(search_query, *, transaction)` | Export matching records as CSV text |
44+
| Method | Description |
45+
| ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
46+
| `create(label, data, *, options, vectors, transaction)` | Create a single record |
47+
| `create_many(label, data, *, options, vectors, transaction)` | Create multiple flat records. `vectors` accepts both a flat list of dicts (one per row, auto-wrapped) and a nested list of lists (explicit per-row entries) |
48+
| `import_json(data, label, *, options, transaction)` | Import nested/complex JSON payloads; `label` may be omitted for container objects whose top-level values are objects or arrays of nested records |
49+
| `import_csv(label, data, *, options, parse_config, vectors, transaction)` | Import records from CSV text |
50+
| `upsert(data, label, *, options, vectors, transaction)` | Create or update a record |
51+
| `set(target, data, *, label, vectors, transaction)` | Replace all fields of a record |
52+
| `update(target, data, *, transaction)` | Partially update a record |
53+
| `find(search_query, *, record_id, transaction)` | Search records; returns `SearchResult` |
54+
| `find_one(search_query, *, transaction)` | Return the first match or `None` |
55+
| `find_uniq(search_query, *, transaction)` | Return the single match or `None`; raises if more than one |
56+
| `find_by_id(target, *, transaction)` | Fetch record(s) by ID |
57+
| `delete(search_query, *, transaction)` | Delete all records matching a query |
58+
| `delete_by_id(target, *, transaction)` | Delete record(s) by ID |
59+
| `attach(source, target, *, options, transaction)` | Create relationships between records |
60+
| `detach(source, target, *, options, transaction)` | Remove relationships between records |
61+
| `export(search_query, *, transaction)` | Export matching records as CSV text |
6262

6363
### `db.tx`
6464

docs/docs/learn/reference/rest-api/ai-and-vectors/indexing.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ Returns all embedding index policies for the project.
6868

6969
`POST /api/v1/ai/indexes`
7070

71-
Creates a new managed embedding index policy scoped to a label. The property must exist in the graph and have type `string` (scalar or list).
71+
Creates a new embedding index policy scoped to a label. If the property already exists in the graph, its type must be `string` (scalar or list). If no records with this property exist yet, the index is created without type validation — the property node will be established when the first record carrying it is written.
7272

7373
### Request Body
7474

@@ -136,8 +136,7 @@ curl -X POST https://api.rushdb.com/api/v1/ai/indexes \
136136

137137
| Status | When |
138138
| ------ | ---------------------------------------------------------------------------------------------------------- |
139-
| `404` | The property does not exist in the project graph |
140-
| `422` | The property exists but is not `string` type |
139+
| `422` | The property exists in the graph but is not `string` type |
141140
| `422` | Embedding model is not configured on the server |
142141
| `409` | An index for this `(label, propertyName, sourceType, similarityFunction, dimensions)` tuple already exists |
143142

docs/docs/learn/reference/rest-api/records/import-data.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ curl -X POST https://api.rushdb.com/api/v1/records/import/json \
4848

4949
| Option | Default | Description |
5050
| ------------------------------- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
51-
| `suggestTypes` | `true` | Infer property types automatically |
51+
| `suggestTypes` | `true` | Infer property types automatically. Both ISO 8601 (`2026-07-23T12:00:00Z`) and date-only (`2026-07-23`) strings are detected as `datetime`. |
5252
| `convertNumericValuesToNumbers` | `false` | Convert string numbers to number type |
5353
| `capitalizeLabels` | `false` | Uppercase all inferred label names |
5454
| `skipEmptyValues` | `false` | Treat empty strings (`""`) and empty arrays (`[]`) as unset — no property is created. `0` and `false` are kept. |

docs/docs/learn/reference/typescript/DBRecordInstance.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,28 @@ data?: DBRecordInferred<S, Q>
4545

4646
The actual record data, which may include computed metric fields if the record was retrieved via a query with select/groupBy. The legacy aggregate clause is deprecated and only present for vector similarity until select supports it.
4747

48+
### score
49+
50+
```typescript
51+
get score(): number | undefined
52+
```
53+
54+
Relevance score from a vector/semantic search result. Returns `undefined` for records fetched outside of `records.vectorSearch()`.
55+
56+
**Example:**
57+
58+
```typescript
59+
const results = await db.records.vectorSearch({
60+
labels: ['DOC'],
61+
propertyName: 'content',
62+
query: 'machine learning',
63+
limit: 10
64+
})
65+
results.forEach((r) => {
66+
console.log(r.score) // e.g. 0.9214; undefined for non-vector results
67+
})
68+
```
69+
4870
## Methods
4971

5072
### exists()

0 commit comments

Comments
 (0)