herringbone 0.7.0 → 0.9.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e9b95c84373ccf0215789628229613c8899227be711dc204c31be6778352f2d6
4
- data.tar.gz: 9314fc6d6d55660e210db0fe62369d9732e33dc3910e306f500b3ebc5c7090e9
3
+ metadata.gz: c59941b3e4ed776a03d3596bf8b9288dd852b89f49934dfb1110f6b29d527ab8
4
+ data.tar.gz: 13ef32e91a12a11d249be06834f0c33dfb70c9ebb7746d29d28844f9af180fa5
5
5
  SHA512:
6
- metadata.gz: 63292b6fde84386d41068903db5267fe7fa94b15c8c191d12c229f888cd94bfd1e6c78e3d01ed5f562643565e8126e9ee2f372e80ab96e08609293afeff1c3bf
7
- data.tar.gz: e9946d5f2ac156222b3bca852f5fc26194ec304fa2b150fd9520311b7e3a323af3ba28e1662bc3b47e593508b22b216e148cf6af67d55318830272f1d87578e5
6
+ metadata.gz: '083624f293ce5d1825c9f6998e46b6e58767e9bbec15d6adbbd1cb556ef3771d2a0426a41ba8f097a43b2b937bef55ce018a01925a7df2d9f9d8d36a1b4f8da9'
7
+ data.tar.gz: 38b917be5c728a3837d3eafae3e9f060708574f5c6249792219d17a410a4fe14683df46dfd4b922d3bf4503a327a2384105c950e06ca1523a7482c042629f367
data/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.9.0
6
+
7
+ - ZSTD files are read without the `zstd-ruby` gem, by a pure-Ruby decompressor; `zstd-ruby` is
8
+ still used when loaded, and still needed to write ZSTD.
9
+
10
+ ## 0.8.0
11
+
12
+ - `Herringbone::MissingCodecError` explains over several lines which gem to install and that it has
13
+ to be required before the read or write, and names the column it could not decompress up front.
14
+ - `Herringbone::Schema#only(:id, :name)` and `#except(:ssn)` make a schema with some of the
15
+ top-level fields.
16
+ - `time` columns convert a `Time` to UTC when writing, as `timestamp` columns already did, instead
17
+ of storing its time of day in its own zone.
18
+ - **Breaking:** `local_timestamp` and `local_time` (`s.local_timestamp :opens_at`) write the wall
19
+ clock time without converting it to UTC, replacing the `utc: false` option, which now raises.
20
+
5
21
  ## 0.7.0
6
22
 
7
23
  - `Herringbone.redact` and `Herringbone::Redaction#apply` take a `Herringbone::Reader` as well as an
data/MANUAL.md CHANGED
@@ -19,6 +19,7 @@ The full reference for Herringbone. For a quick start, see the [README](README.m
19
19
  - [SimpleWriter: coming from CSV](#simplewriter-coming-from-csv)
20
20
  - [Statistics, page indexes and bloom filters](#statistics-page-indexes-and-bloom-filters)
21
21
  - [Writing to S3](#writing-to-s3)
22
+ - [Writing to Google Cloud Storage](#writing-to-google-cloud-storage)
22
23
  - [Encryption](#encryption)
23
24
  - [Writing encrypted files](#writing-encrypted-files)
24
25
  - [Which settings other tools read](#which-settings-other-tools-read)
@@ -54,7 +55,7 @@ Add these libraries to speed up and enable certain functionality:
54
55
 
55
56
  ```ruby
56
57
  gem "snappy" # native Snappy, 2-3x faster reads and writes of typical files
57
- gem "zstd-ruby" # ZSTD
58
+ gem "zstd-ruby" # writing ZSTD; reading falls back to pure Ruby, 30-100x slower
58
59
  gem "brotli" # Brotli
59
60
  gem "xxhash" # faster bloom filters
60
61
  gem "numo-narray-alt" # read(as: :numo)
@@ -212,6 +213,17 @@ The block receives the schema builder, and the blocks of `struct`, `list` and `m
212
213
  builder of their own, so the block can still call your methods and read your instance variables.
213
214
  A block that takes no parameter raises `ArgumentError`.
214
215
 
216
+ `only` and `except` make a schema with some of the top-level fields, keeping their order, for
217
+ instance to write part of a file you are reading:
218
+
219
+ ```ruby
220
+ schema.only(:id, :name) # id and name
221
+ schema.except(:ssn) # every field but ssn
222
+ ```
223
+
224
+ A name the schema has no top-level field for raises `ArgumentError`, and so does leaving out
225
+ every field.
226
+
215
227
  ### Writing with an inferred schema
216
228
 
217
229
  `Herringbone.write(io, rows)` writes an Enumerable of rows in one go, inferring the schema from the
@@ -232,11 +244,17 @@ file and rename it.
232
244
  ### Column types
233
245
 
234
246
  `boolean int8 int16 int32 int64 uint8 uint16 uint32 uint64 float double float16 string binary
235
- json bson enum uuid date int96 time timestamp decimal fixed`, plus `struct`, `list` and `map`;
247
+ json bson enum uuid date int96 time local_time timestamp local_timestamp decimal fixed`, plus
248
+ `struct`, `list` and `map`;
236
249
  `s.column :name, :int32` declares one by name. Fields are nullable unless `null: false` is given;
237
250
  list elements unless `element_null: false`, map values unless `value_null: false`. Nested lists:
238
- `s.list :matrix do |matrix| matrix.list :element, :double end`. `time` and `timestamp` take `unit:`
239
- (`:millis`, `:micros`, `:nanos`) and `utc:`.
251
+ `s.list :matrix do |matrix| matrix.list :element, :double end`. The time and timestamp types take `unit:`
252
+ (`:millis`, `:micros`, `:nanos`).
253
+
254
+ `timestamp` stores instants: a `Time` in any zone is converted to UTC, so the file means the same
255
+ thing wherever it was written. `local_timestamp` stores the wall clock date and time as given, with
256
+ no zone, for values like "the shop opens at 09:00" that are not instants. `time` and `local_time`
257
+ do the same for a time of day.
240
258
 
241
259
  `enum` is a string column. `values:` restricts what may be written, and also takes a Rails-style
242
260
  Hash (`values: Order.statuses`), in which case both labels and stored integers are accepted and
@@ -250,8 +268,8 @@ Columns accept the values Ruby and Rails code usually has at hand:
250
268
  | column | accepts |
251
269
  |---|---|
252
270
  | `date` | `Date`, `Time`/`DateTime` (their date), `"2024-05-01"` |
253
- | `timestamp` | `Time`, `DateTime`, `ActiveSupport::TimeWithZone`, `Date` (midnight UTC), ISO-8601 strings, Integers in the column's unit |
254
- | `time` | `Time` (its time of day, as Rails returns for `time` columns), `"13:45:30.25"`, Integers |
271
+ | `timestamp`, `local_timestamp` | `Time`, `DateTime`, `ActiveSupport::TimeWithZone`, `Date` (midnight UTC), ISO-8601 strings, Integers in the column's unit |
272
+ | `time`, `local_time` | `Time` (its time of day, in UTC for `time`, as Rails returns for `time` columns), `"13:45:30.25"`, Integers |
255
273
  | `json` | Strings as-is, anything else through `JSON.generate` |
256
274
  | `string`, `enum` | Strings, Symbols, anything with `to_s` |
257
275
  | `boolean` | `true`/`false`, `1`/`0`, `"t"`/`"f"`, `"true"`/`"false"`, `"yes"`/`"no"` |
@@ -342,6 +360,18 @@ If the block raises, the SDK aborts the multipart upload and raises `Aws::S3::Mu
342
360
  so no partial object is left. S3 allows at most 10,000 parts, which with the default 5MB parts caps
343
361
  the file at about 48GB; raise `part_size:` for bigger files.
344
362
 
363
+ ### Writing to Google Cloud Storage
364
+
365
+ `google-cloud-storage` needs the size of an upload up front, so it can't take a stream. Use
366
+ [gcs_put](https://github.com/julik/gcs_put), which streams into a resumable upload of unknown size:
367
+
368
+ ```ruby
369
+ gcs_file = Google::Cloud::Storage.new.bucket("exports").file("orders.parquet", skip_lookup: true)
370
+ GCSPut.open_gcs_file(gcs_file) { |io| Herringbone.write(io, Order.all) }
371
+ ```
372
+
373
+ If the block raises, the upload is never finalized and no object appears.
374
+
345
375
  ## Encryption
346
376
 
347
377
  Herringbone reads and writes files encrypted with
data/README.md CHANGED
@@ -4,7 +4,7 @@ A pure-Ruby reader and writer for [Apache Parquet](https://parquet.apache.org/)
4
4
 
5
5
  - No Thrift gem and no native extensions of its own: the Thrift compact protocol, all encodings
6
6
  and the Snappy and LZ4 codecs are implemented in Ruby (ZSTD and Brotli use optional gems).
7
- LZO, from Hadoop-era files, can be read but not written
7
+ ZSTD and LZO (from Hadoop-era files) are also read in pure Ruby; writing ZSTD needs `zstd-ruby`
8
8
  - Full nesting support (structs, lists, maps, any depth) via Dremel record shredding/assembly
9
9
  - Reads files from parquet-mr, Arrow, Spark, Impala, DuckDB, Rust writers etc.
10
10
  - Optimized reads with batches and pages
@@ -21,9 +21,9 @@ rules, combining files, type mapping and the inspector.
21
21
  gem "herringbone"
22
22
  ```
23
23
 
24
- Optionally add `snappy` (faster), `zstd-ruby` and `brotli` (more codecs), `xxhash` (faster bloom
25
- filters) or `numo-narray-alt` (`read(as: :numo)`). Herringbone uses whichever of them are loaded,
26
- see [Optional gems](MANUAL.md#optional-gems).
24
+ Optionally add `snappy` (faster), `zstd-ruby` (writing ZSTD, much faster reading of it), `brotli`,
25
+ `xxhash` (faster bloom filters) or `numo-narray-alt` (`read(as: :numo)`). Herringbone uses
26
+ whichever of them are loaded, see [Optional gems](MANUAL.md#optional-gems).
27
27
 
28
28
  ## Exporting from Rails
29
29
 
@@ -46,7 +46,9 @@ end
46
46
  ```
47
47
 
48
48
  Any Enumerable of Hashes, Structs or Arrays works too; the schema is inferred from the first 1000
49
- rows. See [ActiveRecord](MANUAL.md#activerecord) and [Writing to S3](MANUAL.md#writing-to-s3).
49
+ rows. See [ActiveRecord](MANUAL.md#activerecord) and [Writing to S3](MANUAL.md#writing-to-s3). For
50
+ Google Cloud Storage, use [gcs_put](https://github.com/julik/gcs_put), see
51
+ [Writing to Google Cloud Storage](MANUAL.md#writing-to-google-cloud-storage).
50
52
 
51
53
  ## Reading
52
54
 
@@ -128,7 +128,7 @@ module Herringbone
128
128
  when :boolean then [:boolean, {}]
129
129
  when :binary then [:binary, {}]
130
130
  when :date then [:date, {}]
131
- when :datetime, :timestamp, :timestamptz then [:timestamp, {unit: :micros, utc: true}]
131
+ when :datetime, :timestamp, :timestamptz then [:timestamp, {unit: :micros}]
132
132
  when :time then [:time, {unit: :micros}]
133
133
  when :json, :jsonb then [:json, {}]
134
134
  when :uuid then [:uuid, {}]