qvdjs 0.9.1 → 0.9.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 +180 -35
- package/dist/index.cjs +156 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +155 -34
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,10 +16,31 @@ The library works with real-world QVD files and maintains compatibility with Qli
|
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
19
|
-
The _qvdjs_ library provides a simple API for reading
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
The _qvdjs_ library provides a simple API for reading and writing Qlik View Data (QVD) files in JavaScript.
|
|
20
|
+
It parses the binary QVD format into a JavaScript object structure and back again, and is written for Node.js
|
|
21
|
+
(20.10 or newer) exclusively.
|
|
22
|
+
|
|
23
|
+
**What it does well:**
|
|
24
|
+
|
|
25
|
+
- **Large files without loading them.** Pass `maxRows` and the library reads only the header, the symbols those
|
|
26
|
+
rows actually reference, and that slice of the index table. On a file whose symbol table is large because its
|
|
27
|
+
fields hold mostly unique values, this is the difference between minutes and under a second — and it is the
|
|
28
|
+
only way to read a file above 2 GiB at all. See [Lazy Loading](#lazy-loading).
|
|
29
|
+
- **Writing at real row counts.** `toQvd()` handles hundreds of thousands of rows, with optional progress
|
|
30
|
+
callbacks for long writes. Files it produces open in Qlik Sense and QlikView.
|
|
31
|
+
- **Refusing rather than crashing.** A load too large for the process throws a catchable `QvdValidationError`
|
|
32
|
+
naming the limit it hit and a row count that would fit, instead of a `FATAL ERROR: Reached heap limit` that no
|
|
33
|
+
`try`/`catch` can intercept. The budget comes from the V8 heap ceiling and any container memory limit — the
|
|
34
|
+
two things that actually kill a process — so it neither refuses work the machine can do nor waves through work
|
|
35
|
+
it cannot. See [QVD File Size Limitations](#qvd-file-size-limitations).
|
|
36
|
+
- **Corrupt files are detected, not silently misread.** Truncated index tables, missing header delimiters and
|
|
37
|
+
out-of-range offsets raise typed errors rather than returning short or fabricated data.
|
|
38
|
+
- **Path traversal protection by default.** File access is confined to the working directory unless you widen it,
|
|
39
|
+
and containment is decided by the filesystem — symlinks are resolved, so a link inside the allowed directory
|
|
40
|
+
pointing outside it is refused. See [Security Considerations](#security-considerations).
|
|
41
|
+
|
|
42
|
+
**What it does not do yet** is worth knowing before you start: see
|
|
43
|
+
[Known limitations](#known-limitations).
|
|
23
44
|
|
|
24
45
|
---
|
|
25
46
|
|
|
@@ -31,10 +52,13 @@ structure and vice versa. The library is written to be used in a Node.js environ
|
|
|
31
52
|
- [Important: Symbol Table and High-Cardinality Fields](#important-symbol-table-and-high-cardinality-fields)
|
|
32
53
|
- [Performance Optimizations](#performance-optimizations)
|
|
33
54
|
- [QVD File Size Limitations](#qvd-file-size-limitations)
|
|
55
|
+
- [The 2 GiB boundary](#the-2-gib-boundary)
|
|
34
56
|
- [Why Safety Limits Exist](#why-safety-limits-exist)
|
|
35
|
-
- [
|
|
57
|
+
- [Writing QVD files](#writing-qvd-files)
|
|
58
|
+
- [Progress tracking](#progress-tracking)
|
|
36
59
|
- [Working with Metadata](#working-with-metadata)
|
|
37
60
|
- [Security Considerations](#security-considerations)
|
|
61
|
+
- [Known limitations](#known-limitations)
|
|
38
62
|
- [QVD File Format](#qvd-file-format)
|
|
39
63
|
- [XML Header](#xml-header)
|
|
40
64
|
- [Symbol Table](#symbol-table)
|
|
@@ -131,8 +155,11 @@ console.log(df.shape); // [1000, numberOfColumns]
|
|
|
131
155
|
|
|
132
156
|
The QVD format stores data in two parts:
|
|
133
157
|
|
|
134
|
-
1. **Symbol table**: Contains
|
|
135
|
-
|
|
158
|
+
1. **Symbol table**: Contains every unique value for every field. It has to be _scanned_ in full, because a
|
|
159
|
+
symbol's length is only known once the previous one has been read — but with `maxRows` the library parses
|
|
160
|
+
only the symbols the requested rows actually reference and steps over the rest. Scanning is cheap; parsing
|
|
161
|
+
is what costs memory.
|
|
162
|
+
2. **Index table**: Contains row-by-row indices into the symbol table (only the requested rows are read)
|
|
136
163
|
|
|
137
164
|
⚠️ **Performance Impact of High-Cardinality Fields:**
|
|
138
165
|
|
|
@@ -142,12 +169,23 @@ If your QVD file contains fields with many unique values (high cardinality), suc
|
|
|
142
169
|
- Timestamps with millisecond precision
|
|
143
170
|
- Unique text fields
|
|
144
171
|
|
|
145
|
-
The symbol table
|
|
172
|
+
The symbol table then becomes very large, and has to be scanned end to end even when using `maxRows`. Parsing
|
|
173
|
+
is skipped for symbols the requested rows do not use, so the cost is I/O rather than memory:
|
|
146
174
|
|
|
147
175
|
- **Small symbol table** (fields with reusable values): Fast loading regardless of file size
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
176
|
+
- **Large symbol table** (fields with unique values per row): Slower, because the scan is proportional to the
|
|
177
|
+
symbol table's size — but memory stays proportional to the rows you asked for, not to the file
|
|
178
|
+
|
|
179
|
+
You can see what happened on any load through `loadStats` (see [QvdDataFrame](#qvddataframe)):
|
|
180
|
+
|
|
181
|
+
```javascript
|
|
182
|
+
const df = await QvdDataFrame.fromQvd('large.qvd', {maxRows: 1000});
|
|
183
|
+
console.log(df.loadStats);
|
|
184
|
+
// { symbolTableBytes: 451704, totalRows: 1705805, rowsLoaded: 1000,
|
|
185
|
+
// symbolFiltering: true, symbolsKept: 3898 }
|
|
186
|
+
//
|
|
187
|
+
// 3,898 symbols parsed for 1,000 rows, out of a file holding 1.7 million.
|
|
188
|
+
```
|
|
151
189
|
|
|
152
190
|
To check your QVD's symbol table size, look at the `NoOfSymbols` in field metadata - values close to the total row count indicate high cardinality.
|
|
153
191
|
|
|
@@ -170,24 +208,28 @@ When you specify `maxRows`, the library:
|
|
|
170
208
|
2. Parses **only** those symbols from the symbol table
|
|
171
209
|
3. Skips parsing unused symbols entirely (not just filtering after parsing)
|
|
172
210
|
|
|
173
|
-
|
|
211
|
+
This matters most on files whose symbol table is large because most values are unique — exactly the files where
|
|
212
|
+
a naive `maxRows` would still pay for the whole table. Compared to parsing every symbol regardless of `maxRows`,
|
|
213
|
+
loading a few thousand rows from a multi-million-row file has been measured at roughly an order of magnitude
|
|
214
|
+
faster and an order of magnitude smaller in peak memory.
|
|
174
215
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
- **~10x faster load times** - Loading 5,000 rows from a 20M row file: 8.3 seconds → 0.9 seconds
|
|
178
|
-
- **~10x less memory usage** - Same operation: 1,616 MB → 180 MB
|
|
179
|
-
- **Native or better efficiency** - Memory overhead reduced from 6.0x to 0.7x of raw data size
|
|
180
|
-
|
|
181
|
-
**Real-World Example:**
|
|
216
|
+
The saving is visible on any file through `loadStats`. On a bundled 1.7-million-row fixture, asking for 1,000
|
|
217
|
+
rows parses 3,898 symbols:
|
|
182
218
|
|
|
183
219
|
```javascript
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
//
|
|
187
|
-
|
|
188
|
-
|
|
220
|
+
const df = await QvdDataFrame.fromQvd('chicago_taxi_rides_2016_01.qvd', {
|
|
221
|
+
maxRows: 1000,
|
|
222
|
+
symbolFilteringThreshold: 0, // this fixture is well under the 50 MB default
|
|
223
|
+
});
|
|
224
|
+
console.log(df.loadStats);
|
|
225
|
+
// { symbolTableBytes: 451704, totalRows: 1705805, rowsLoaded: 1000,
|
|
226
|
+
// symbolFiltering: true, symbolsKept: 3898 }
|
|
189
227
|
```
|
|
190
228
|
|
|
229
|
+
The optimisation engages automatically once the symbol table passes `symbolFilteringThreshold`, which defaults
|
|
230
|
+
to 50 MB — the point where the extra analysis pass pays for itself. The example lowers it so the path can be
|
|
231
|
+
demonstrated on a small bundled fixture; on a real high-cardinality file it engages on its own.
|
|
232
|
+
|
|
191
233
|
**Key Benefits:**
|
|
192
234
|
|
|
193
235
|
- Much faster previews of large QVD files
|
|
@@ -199,6 +241,28 @@ This optimization is particularly effective for files with many unique values (h
|
|
|
199
241
|
|
|
200
242
|
### QVD File Size Limitations
|
|
201
243
|
|
|
244
|
+
#### The 2 GiB boundary
|
|
245
|
+
|
|
246
|
+
There is one hard limit worth knowing before anything else: **a full load cannot read a file larger than
|
|
247
|
+
2 GiB**, because Node caps `fs.readFile` there. It fails with a raw Node error rather than a `QvdError`:
|
|
248
|
+
|
|
249
|
+
```
|
|
250
|
+
RangeError: File size (2362232013) is greater than 2 GiB
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
**`maxRows` reads past that boundary**, because the lazy path reads the file in chunks instead:
|
|
254
|
+
|
|
255
|
+
```javascript
|
|
256
|
+
// A 2.2 GiB file
|
|
257
|
+
await QvdDataFrame.fromQvd(huge, {}); // ❌ RangeError, above
|
|
258
|
+
await QvdDataFrame.fromQvd(huge, {maxRows: 1000}); // ✅ works
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
So above 2 GiB, `maxRows` is not an optimisation — it is the only way in. Removing the full-load ceiling is
|
|
262
|
+
tracked in [#122](https://github.com/ptarmiganlabs/qvdjs/issues/122).
|
|
263
|
+
|
|
264
|
+
Below that boundary, what limits you is memory rather than file size, which is the rest of this section.
|
|
265
|
+
|
|
202
266
|
**Simple Explanation:**
|
|
203
267
|
|
|
204
268
|
Node.js has memory limits that affect how large QVD files you can work with. By default, Node.js can use up to about 4GB of memory. This means if you try to load a very large QVD file, you might run out of memory and get an error. Think of it like trying to open a very large document on a computer with limited RAM - if the document is too big, it won't open.
|
|
@@ -231,6 +295,26 @@ The maximum QVD file size you can handle with qvdjs depends on several factors a
|
|
|
231
295
|
});
|
|
232
296
|
```
|
|
233
297
|
|
|
298
|
+
**What the budget is measured against.** The V8 heap ceiling, and any container memory limit reported by
|
|
299
|
+
`process.constrainedMemory()`. Those are the two limits that actually terminate a process — exceeding the heap
|
|
300
|
+
is a fatal, uncatchable V8 error, and exceeding a cgroup limit is a SIGKILL that arrives as exit 137 with no
|
|
301
|
+
JavaScript error at all.
|
|
302
|
+
|
|
303
|
+
What the operating system reports as _free_ memory is deliberately **not** part of it. It is recorded in the
|
|
304
|
+
error context for diagnostics and ignored for the decision, because it does not describe a wall the process
|
|
305
|
+
can hit — a machine with virtual memory gets slower, not fatal — and because the figure is not dependable:
|
|
306
|
+
on macOS it counts only free and speculative pages, excluding the file-cache pages the OS reclaims on demand,
|
|
307
|
+
and readings on one idle machine varied nine-fold within minutes. Budgeting from it meant the same file loaded
|
|
308
|
+
or was refused depending on when you asked.
|
|
309
|
+
|
|
310
|
+
**Turning the check off.** `memorySafetyFactor: 0` disables it entirely, for callers who would rather manage
|
|
311
|
+
memory themselves, or run on a runtime whose limits cannot be measured — Bun reports its current heap as its
|
|
312
|
+
heap limit, so the ceiling it advertises is meaningless.
|
|
313
|
+
|
|
314
|
+
```javascript
|
|
315
|
+
const df = await QvdDataFrame.fromQvd('large-file.qvd', {memorySafetyFactor: 0});
|
|
316
|
+
```
|
|
317
|
+
|
|
234
318
|
To phrase it differently: There is no magic here - viewing a 40 GB QVD on a laptop with 24 GB RAM will not work. That laptop may in fact struggle with QVD files larger than 4-6 GB depending on data characteristics - or happily work with 10+ GB files if the data is very friendly.
|
|
235
319
|
|
|
236
320
|
- **Data Characteristics**: The actual memory consumption depends _heavily_ on what's inside your QVD:
|
|
@@ -284,7 +368,24 @@ try {
|
|
|
284
368
|
|
|
285
369
|
For more technical details about the memory safety system, including specific thresholds and the four-tier protection model, see [docs/DYNAMIC_SAFETY_LIMITS.md](docs/DYNAMIC_SAFETY_LIMITS.md). For practical examples, see [docs/examples/heap-scaling-example.md](docs/examples/heap-scaling-example.md).
|
|
286
370
|
|
|
287
|
-
###
|
|
371
|
+
### Writing QVD files
|
|
372
|
+
|
|
373
|
+
`toQvd()` builds the symbol and index tables in memory and writes the file in one pass. Two things are worth
|
|
374
|
+
knowing about it.
|
|
375
|
+
|
|
376
|
+
**Row count is no longer a barrier.** Writing used to fail outright above roughly 122,000 rows with a
|
|
377
|
+
`RangeError: Maximum call stack size exceeded`, because the bit width for each column was derived with
|
|
378
|
+
`Math.max(...oneArgumentPerRow)`. It is now derived from the symbol count in constant time, and writes of
|
|
379
|
+
hundreds of thousands of rows are routine. The regression suite round-trips a 200,000-row data frame and a
|
|
380
|
+
150,000-row slice of real Qlik output on every CI run, across Linux, Windows and macOS.
|
|
381
|
+
|
|
382
|
+
**Memory scales with the data, not with the file you are writing.** A write holds the whole data frame plus its
|
|
383
|
+
symbol and index tables, so it needs more memory than reading the equivalent file. There is no lazy equivalent
|
|
384
|
+
of `maxRows` for writing — if the data does not fit in the heap, split it across several QVDs.
|
|
385
|
+
|
|
386
|
+
Writes are not atomic; see [Known limitations](#known-limitations).
|
|
387
|
+
|
|
388
|
+
#### Progress tracking
|
|
288
389
|
|
|
289
390
|
When writing large QVD files, the `toQvd()` operation can take significant time. The library provides optional progress callbacks to track the write operation in real-time:
|
|
290
391
|
|
|
@@ -411,7 +512,25 @@ await df.toQvd('processed/sales-filtered.qvd', {
|
|
|
411
512
|
- **Null Byte Protection**: Detects and blocks null byte injection attempts
|
|
412
513
|
- **Default CWD Restriction**: By default, file operations are restricted to the current working directory (CWD) and its subdirectories to prevent path traversal attacks
|
|
413
514
|
- **Custom Directory Restriction**: Optional `allowedDir` parameter allows you to specify a different base directory
|
|
414
|
-
- **
|
|
515
|
+
- **Symlinks are resolved**: Containment is decided by the filesystem, not by comparing strings. Both paths are
|
|
516
|
+
resolved through symlinks and compared by device and inode, so a link _inside_ `allowedDir` that points
|
|
517
|
+
outside it is refused — for writes as well as reads. A string comparison sees only the link's own name, still
|
|
518
|
+
under `allowedDir`, and lets it through; that meant a link planted in an upload directory could be used to
|
|
519
|
+
read any file the process could read, and to overwrite and truncate any file it could write.
|
|
520
|
+
- **Case is handled as the filesystem handles it**: comparing by inode means `Qvd` and `qvd` are the same
|
|
521
|
+
directory on a case-insensitive volume and different directories on a case-sensitive one, without guessing
|
|
522
|
+
from `process.platform`. macOS supports both.
|
|
523
|
+
- **Security Errors**: Throws `QvdSecurityError` with detailed context when security violations are detected.
|
|
524
|
+
The context includes a `check` field saying whether the filesystem or the fallback string comparison refused,
|
|
525
|
+
so a rejection of a path that looks contained is traceable to a symlink or a case difference.
|
|
526
|
+
|
|
527
|
+
**A note on `allowedDir` values:** `null`, `undefined` and `''` all fall back to the current working directory
|
|
528
|
+
rather than meaning "no restriction" — callers routinely produce those from optional config or a JSON round
|
|
529
|
+
trip, and silently dropping the sandbox there would be a security hole. To permit an entire volume, pass its
|
|
530
|
+
root explicitly (`'/'` on POSIX, `'C:\\'` on Windows).
|
|
531
|
+
|
|
532
|
+
**Not covered:** this is a check on a path, so it remains open to a symlink swapped in between the check and the
|
|
533
|
+
open. Closing that would require opening with `O_NOFOLLOW` and verifying the descriptor.
|
|
415
534
|
|
|
416
535
|
**Best Practices:**
|
|
417
536
|
|
|
@@ -454,6 +573,22 @@ try {
|
|
|
454
573
|
}
|
|
455
574
|
```
|
|
456
575
|
|
|
576
|
+
### Known limitations
|
|
577
|
+
|
|
578
|
+
Honest boundaries rather than an issue list — these are the ones that change what you can do.
|
|
579
|
+
|
|
580
|
+
| Limitation | What it means in practice | Tracked as |
|
|
581
|
+
| ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------- |
|
|
582
|
+
| **Full loads stop at 2 GiB** | Use `maxRows` above that; the lazy path reads in chunks and has no such ceiling. Failure is a raw Node `RangeError`, not a `QvdError`. | [#122](https://github.com/ptarmiganlabs/qvdjs/issues/122) |
|
|
583
|
+
| **The memory estimate is based on symbol table size, not on rows × columns** | A file with a modest symbol table but very many rows can pass every check and then exhaust the heap while materialising rows. Loading tens of millions of rows, size the heap for the result rather than for the file. | [#121](https://github.com/ptarmiganlabs/qvdjs/issues/121) |
|
|
584
|
+
| **Dual values are not round-tripped as pairs** | Qlik stores dates and timestamps as a number plus a display string. A data frame exposes one of the two, so a read-modify-write does not reproduce the original dual — a `BirthDate` field comes back as `24205`, not as a formatted date. | [#138](https://github.com/ptarmiganlabs/qvdjs/issues/138) |
|
|
585
|
+
| **Writes are not atomic** | `toQvd()` writes in place. A failure part-way through, or two writers targeting one path, leaves the previous file damaged rather than intact. Write to a temporary path and rename if that matters. | [#129](https://github.com/ptarmiganlabs/qvdjs/issues/129) |
|
|
586
|
+
| **Writer input is not validated** | Unsupported value types can produce a raw `TypeError`, or a file that is written but does not read back as intended. Feed `fromDict()` numbers, strings and `null`. | [#130](https://github.com/ptarmiganlabs/qvdjs/issues/130) |
|
|
587
|
+
|
|
588
|
+
Numeric-looking strings are currently coerced to numbers on read, which is being reconsidered as a breaking
|
|
589
|
+
change in [#120](https://github.com/ptarmiganlabs/qvdjs/issues/120). Empty and whitespace-only strings are _not_
|
|
590
|
+
coerced — that was a defect, and is fixed.
|
|
591
|
+
|
|
457
592
|
## QVD File Format
|
|
458
593
|
|
|
459
594
|
The QVD file format is a binary file format that is used by QlikView to store data. The format is proprietary. However,
|
|
@@ -567,13 +702,14 @@ Empty QVDs are fully supported for both reading and writing, maintaining compati
|
|
|
567
702
|
The `QvdDataFrame` class represents the data frame stored inside of a finally parsed QVD file. It provides a high-level
|
|
568
703
|
abstraction access to the QVD file content. This includes meta information as well as access to the actual data records.
|
|
569
704
|
|
|
570
|
-
| Property | Type | Description
|
|
571
|
-
| -------------- | ---------- |
|
|
572
|
-
| `shape` | `number[]` | The shape of the data table. The first element is the number of rows, the second element is the number of columns.
|
|
573
|
-
| `data` | `any[][]` | The actual data records of the QVD file. The first dimension represents the single rows.
|
|
574
|
-
| `columns` | `string[]` | The names of the fields that are contained in the QVD file.
|
|
575
|
-
| `metadata` | `object` | The complete metadata object from the QVD file header, or null if not loaded from a QVD file.
|
|
576
|
-
| `fileMetadata` | `object` | File-level metadata from the QVD header (qvBuildNo, tableName, createUtcTime, etc.).
|
|
705
|
+
| Property | Type | Description |
|
|
706
|
+
| -------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
707
|
+
| `shape` | `number[]` | The shape of the data table. The first element is the number of rows, the second element is the number of columns. |
|
|
708
|
+
| `data` | `any[][]` | The actual data records of the QVD file. The first dimension represents the single rows. |
|
|
709
|
+
| `columns` | `string[]` | The names of the fields that are contained in the QVD file. |
|
|
710
|
+
| `metadata` | `object` | The complete metadata object from the QVD file header, or null if not loaded from a QVD file. |
|
|
711
|
+
| `fileMetadata` | `object` | File-level metadata from the QVD header (qvBuildNo, tableName, createUtcTime, etc.). |
|
|
712
|
+
| `loadStats` | `object` | What the read did: `symbolTableBytes`, `totalRows`, `rowsLoaded`, `symbolFiltering`, `symbolsKept`. `null` unless the frame came from `fromQvd()`. |
|
|
577
713
|
|
|
578
714
|
#### `static fromQvd(path: string, options?: object): Promise<QvdDataFrame>`
|
|
579
715
|
|
|
@@ -586,7 +722,8 @@ to a `QvdDataFrame` instance.
|
|
|
586
722
|
- `options` (object, optional): Loading options
|
|
587
723
|
- `maxRows` (number, optional): Maximum number of rows to load. If not specified, all rows are loaded. This is useful for loading only a subset of data from large QVD files to improve performance and reduce memory usage.
|
|
588
724
|
- `allowedDir` (string, optional): Base directory for file access validation. Defaults to current working directory (CWD). The file path must resolve to a location within this directory to prevent path traversal attacks. Set to a specific directory in production environments with user-provided paths.
|
|
589
|
-
- `memorySafetyFactor` (number, optional):
|
|
725
|
+
- `memorySafetyFactor` (number, optional): Fraction (0.0-1.0) of the memory budget a load may use. Default is 0.3 (30%). The budget is the smaller of the V8 heap limit and any container memory limit; see [QVD File Size Limitations](#qvd-file-size-limitations). Increase this (e.g., to 0.5 or 0.7) when running with a larger heap via `--max-old-space-size`. **`0` disables the memory check entirely.**
|
|
726
|
+
- `symbolFilteringThreshold` (number, optional): Symbol table size, in bytes, above which a lazy load switches to the two-pass filtering path. Defaults to 50 MB, the point where the extra analysis pass pays for itself. Lower it to use filtering on smaller files, raise it to keep the simpler single-pass read for longer.
|
|
590
727
|
|
|
591
728
|
**Example:**
|
|
592
729
|
|
|
@@ -604,8 +741,16 @@ const dfSecure = await QvdDataFrame.fromQvd('reports/sales.qvd', {
|
|
|
604
741
|
|
|
605
742
|
// Load with increased memory usage for large heap configurations
|
|
606
743
|
const dfLarge = await QvdDataFrame.fromQvd('large-file.qvd', {
|
|
607
|
-
memorySafetyFactor: 0.5, // Use 50% of
|
|
744
|
+
memorySafetyFactor: 0.5, // Use 50% of the budget instead of the default 30%
|
|
608
745
|
});
|
|
746
|
+
|
|
747
|
+
// Manage memory yourself: skip the check entirely
|
|
748
|
+
const dfUnchecked = await QvdDataFrame.fromQvd('large-file.qvd', {memorySafetyFactor: 0});
|
|
749
|
+
|
|
750
|
+
// Inspect what the read actually did
|
|
751
|
+
const preview = await QvdDataFrame.fromQvd('large-file.qvd', {maxRows: 500});
|
|
752
|
+
console.log(preview.loadStats.symbolFiltering); // true when the two-pass path ran
|
|
753
|
+
console.log(preview.loadStats.symbolsKept); // how many symbols it kept
|
|
609
754
|
```
|
|
610
755
|
|
|
611
756
|
#### `static fromDict(dict: object): Promise<QvdDataFrame>`
|