dataset

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 2, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package dataset converts a meguri live store to and from Apache Parquet, so a frontier snapshot can be published as a Hugging Face dataset and read back into a .meguri file. The .meguri format is meguri's own columnar store, tuned for the crawl loop (a resident seen-set filter, a due-scan cursor, host clustering); Parquet is the interchange format the data ecosystem reads (the Hugging Face dataset viewer, datasets, pyarrow, duckdb, polars). This package is the bridge: export streams the URL table out to Parquet in key order, import streams Parquet back into a fresh .meguri, and the pair round-trips every field.

The unit of publication is the URL table: one row per frontier entry, the crawl state plus the resolved URL and host strings. The host table (DNS, robots, politeness) is operational state that does not belong in a public dataset, so it is not exported; a re-import rebuilds a minimal host table from the URL rows' host strings, which is all a fresh frontier needs.

Index

Constants

View Source
const CardName = "README.md"

CardName is the fixed filename of the dataset card at the repo root. Hugging Face renders it as the dataset landing page and reads its YAML frontmatter to wire the dataset viewer to the parquet files.

View Source
const ManifestName = "manifest.json"

ManifestName is the fixed filename of the dataset manifest at the repo root.

View Source
const SchemaVersion = 1

SchemaVersion is stamped into the manifest and the dataset card. It bumps when the Row layout changes so a reader can tell an old dump from a new one.

Variables

This section is empty.

Functions

func FromRow

func FromRow(row *Row) (rec m.URLRecord, url, host, etag string)

FromRow reconstructs a record from a Parquet row, and returns the url, host, and etag strings the caller must re-intern into the new file's arena (the URLRef and ETagRef offsets are assigned by the build, not carried over). The numeric status and source codes are the source of truth; the name columns are for readers and are ignored here, so a row with an unknown name still decodes to the right code.

func Import

func Import(in, out string, opts ImportOptions) (live.BuildResult, error)

Import builds one .meguri file from a Parquet dataset. in is a dataset repo folder (with a manifest.json listing the data files) or a single .parquet file; out is the .meguri to write. The rows are read back in URLKey order across all files, with a later file winning a key tie, so an incremental dataset (where a changed URL was re-exported in a newer file) imports to the latest state of each URL with no duplicates.

Types

type ExportOptions

type ExportOptions struct {
	// RowGroupRows is the Parquet row-group size in rows. A row group is the unit a
	// reader seeks to and the unit column statistics summarize, so it trades scan
	// granularity against footer size. Zero uses a sane default.
	RowGroupRows int
	// FileRows caps the rows per output file in repo mode, so a 100M export becomes a
	// folder of evenly sized shards a git-LFS push and the Hugging Face viewer both
	// handle better than one multi-gigabyte blob. Zero uses a default; single-file
	// export ignores it.
	FileRows int
	// Codec is the column compression: "zstd" (default, best ratio), "snappy" (faster
	// decode), or "none".
	Codec string
	// SinceHours filters to rows with activity (first_seen, last_crawled, or
	// last_changed) at or after this epoch-hour, the incremental cursor. Zero exports
	// every row (a full dump). A caller sets it to a prior dump's watermark to export
	// only what is new or changed since.
	SinceHours uint32
}

ExportOptions configures a meguri-to-Parquet export.

type ExportStats

type ExportStats struct {
	Rows       int64  `json:"rows"`
	Skipped    int64  `json:"skipped"` // rows filtered out by SinceHours
	Hosts      int    `json:"hosts"`
	Files      int    `json:"files"`
	Bytes      int64  `json:"bytes"`
	LossyETags int64  `json:"lossy_etags"`
	Watermark  uint32 `json:"watermark_hours"` // max activity epoch-hour seen, the next incremental cursor
}

ExportStats reports what an export produced.

func ExportRepo

func ExportRepo(src, outDir string, opts ExportOptions) (ExportStats, error)

ExportRepo writes a Hugging Face dataset repo folder: data/urls-NNNNNN.parquet plus a manifest.json and a README.md dataset card. src is a .meguri file or a store dir. It is the publish-ready shape; each incremental dump adds files under data/ and is a commit on top.

func ExportSingle

func ExportSingle(src, outFile string, opts ExportOptions) (ExportStats, error)

ExportSingle writes the whole source to one .parquet file. src is a .meguri file or a sharded store directory; a store's shards stream into the one file in shard order.

type FileMeta

type FileMeta struct {
	Name  string `json:"name"`  // path relative to the dataset root
	Rows  int64  `json:"rows"`  // row count
	Bytes int64  `json:"bytes"` // file size on disk
}

FileMeta describes one written .parquet file in the manifest.

type ImportOptions

type ImportOptions struct {
	// TmpDir is the scratch directory for the build's arena and record temps. Empty
	// uses the system default.
	TmpDir string
	// Codec is the output .meguri body codec: format.CodecZstd (default) or
	// format.CodecNone. It is the meguri codec, not the Parquet one.
	Codec uint8
	// PageRows caps the output column page size. Zero uses a scale-friendly default so
	// a dedup confirm decodes one page, not the whole column.
	PageRows int
	// FPRate is the resident seen-set filter false-positive budget. Zero uses the
	// build default.
	FPRate float64
	// ExpectedKeys sizes the filter. Zero lets the build guess; a caller that knows the
	// row count (the manifest does) should set it so the filter is not oversized.
	ExpectedKeys uint64
	// PartitionID stamps the output partition.
	PartitionID uint32
}

ImportOptions configures a Parquet-to-meguri import.

type Manifest

type Manifest struct {
	SchemaVersion int    `json:"schema_version"`
	Codec         string `json:"codec"`
	RowGroupRows  int    `json:"row_group_rows"`
	FileRows      int    `json:"file_rows"`

	Rows       int64 `json:"rows"`
	Hosts      int   `json:"hosts"`
	LossyETags int64 `json:"lossy_etags,omitempty"`

	// Watermark is the max last_changed epoch-hour across all rows, the cursor the next
	// incremental dump exports past. Zero means nothing has been observed to change yet.
	Watermark uint32 `json:"watermark_hours"`

	Files []FileMeta `json:"files"`
}

Manifest describes a published dataset: the schema version, the codec, the row and host totals, the incremental watermark, and the list of data files. It is the machine-readable index a re-import reads to find every .parquet file, and the record an incremental dump advances. It lives beside the data so the folder is self-describing without the .meguri it came from.

func ReadManifest

func ReadManifest(dir string) (Manifest, error)

ReadManifest reads the manifest from a dataset root.

func (Manifest) DataFiles

func (man Manifest) DataFiles(root string) []string

DataFiles returns the absolute paths of every .parquet file the manifest lists, in name order, the read set a full import consumes.

type Row

type Row struct {
	// Identity: the 128-bit URLKey split into its two halves, and the strings.
	HostKey uint64 `parquet:"host_key"`
	PathKey uint64 `parquet:"path_key"`
	URL     string `parquet:"url,zstd"`
	Host    string `parquet:"host,zstd,dict"`

	// State machine and discovery.
	Status     string  `parquet:"status,dict"`
	StatusCode uint8   `parquet:"status_code"`
	Priority   float32 `parquet:"priority"`
	Depth      uint16  `parquet:"depth"`
	Source     string  `parquet:"source,dict"`
	SourceCode uint8   `parquet:"source_code"`

	// Lifecycle timestamps, all nullable. A freshly seeded frontier carries none of
	// them (a URL is discovered but not yet dated or scheduled), so every one is nil
	// until its event happens: the zero hour is a null column, not a boundary date.
	// This also keeps the columns inside the representable timestamp range; a non-null
	// value is a real epoch-hour a scheduler set.
	FirstSeen    *time.Time `parquet:"first_seen,timestamp(millisecond),optional"`
	NextDue      *time.Time `parquet:"next_due,timestamp(millisecond),optional"`
	LastCrawled  *time.Time `parquet:"last_crawled,timestamp(millisecond),optional"`
	LastChanged  *time.Time `parquet:"last_changed,timestamp(millisecond),optional"`
	LastModified *time.Time `parquet:"last_modified,timestamp(millisecond),optional"`

	// Change model and counters.
	Lambda         float32 `parquet:"lambda"`
	CrawlCount     uint32  `parquet:"crawl_count"`
	ChangeCount    uint32  `parquet:"change_count"`
	NoChangeStreak uint16  `parquet:"no_change_streak"`

	// Validators and content fingerprints.
	ETag      string `parquet:"etag,zstd,optional"`
	ContentFP uint64 `parquet:"content_fp"`
	Simhash   uint64 `parquet:"simhash"`

	// Last-crawl outcome.
	HTTPStatus  uint16 `parquet:"http_status"`
	RedirectRef uint64 `parquet:"redirect_ref"`
	RetryCount  uint8  `parquet:"retry_count"`
	ErrorCount  uint16 `parquet:"error_count"`
}

Row is the Parquet shape of one URL-table entry. Every URLRecord field is carried so the round-trip is lossless, with three readability additions the raw record leaves implicit: the resolved url, host, and etag strings (the record holds only arena offsets), and human-readable status and source names alongside the numeric codes. The epoch-hour timestamps become real Parquet TIMESTAMP columns so the Hugging Face viewer renders dates, not integers; never-set times are nullable and come back nil.

The field order is the column order in the file. Keys and strings lead (what a human scans first), then the crawl-state numerics, then the fingerprints and error counters. The `zstd` tag on the big string columns compresses them per column; the low-cardinality name and code columns dictionary-encode to near nothing on their own.

func ToRow

func ToRow(r *m.URLRecord, url, host, etag string) Row

ToRow builds the Parquet row for a record and its resolved strings. The record carries arena offsets (URLRef, ETagRef) that mean nothing outside the file it came from, so the caller resolves them to strings and passes them in; RedirectRef is an internal record reference kept raw for fidelity but not portable across a rebuild.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL