frankjc2022/semantic-history-search
Semantic History (Synthetic) Semantic History is a synthetic dataset for semantic history search research.It ships as three normalized Parquet tables: Split Description docs Search history records with url, title, description, frecency, last_visit_date, and tags. queries One row per query, tagged by profile and temporal/multi-label flags. qrels Relevance pairs linking queries ↔ docs with rank and relevance. All content is synthetic (no real browsing logs).… See the full description on the dataset page: https://huggingface.co/datasets/frankjc2022/semantic-history-search.
Semantic History (Synthetic)
Semantic History is a synthetic dataset for semantic history search research. It ships as three normalized Parquet tables:
All content is synthetic (no real browsing logs).
Temporal Variant
The dataset includes a temporal slice designed to test retrieval with time-aware queries (e.g., “yesterday”, “last week”, “on Black Friday”). Temporal queries in queries are tagged with variant="temporal" and carry a per-query reference time ref_datetime_iso, which is the anchor used to resolve relative phrases (e.g., “yesterday” → specific date range).
For reproducibility, we also publish the raw temporal profiles (one folder per profile) that were used to construct the Parquet release. Each temporal profile contains:
history.csv— history rows withlast_visit_date(µs epoch) andfrecencyquery.csv— temporal queries and expected matchestemporal_context.json— reference time and locale rules (e.g., timezone, weekend, date format)
See the detailed temporal documentation and per-locale notes here: https://huggingface.co/datasets/frankjc2022/semantic-history-search/blob/main/raw/profiles/temporal/README.md
Motivation
- User-oriented IR (per-profile history retrieval)
- Temporal-aware retrieval (e.g., profile histories with a reference time)
- Embedding & ranking evaluation on synthetic history traces
Data Layout
data/v1/
├── docs.parquet
├── queries.parquet
└── qrels.parquetColumns
- docs:
doc_id, url, title, description, frecency, last_visit_date, profile, profile_id, variant
- queries:
query_id, search_query, profile, profile_id, is_temporal, is_multi, variant, ref_datetime_iso
- qrels:
query_id, doc_id, profile_id, relevance, rank, variant
profile_idis a stable, hashed identifier per profile folder;variantcan betemporalor other configured variants;is_multiindicates multi-label queries.
Firefox Places Background (schema context)
The dataset docs mimics Firefox’s Places DB:
moz_placestable schema: https://searchfox.org/firefox-main/source/toolkit/components/places/nsPlacesTables.h- Length limits referenced in Places utils:
title≤ 4096 chars,description≤ 256 https://searchfox.org/firefox-main/source/toolkit/components/places/PlacesUtils.sys.mjs#162 titlefrom DOM<title>: https://searchfox.org/firefox-main/source/dom/svg/SVGTitleElement.cppdescriptionfrom prioritized page metadata: https://searchfox.org/firefox-main/source/toolkit/actors/ContentMetaChild.sys.mjs#12
Embedding text: we use `title + description`.
How to Load (HF Datasets)
from datasets import load_dataset
dataset_id = "frankjc2022/semantic-history-search"
docs_pd = load_dataset(dataset_id, name="docs")["train"]
queries_pd = load_dataset(dataset_id, name="queries")["train"]
qrels_pd = load_dataset(dataset_id, name="qrels")["train"]Common Operations
HF Datasets Version
List available profiles
profiles = (docs.to_pandas()[["profile_id","profile","variant"]]
.drop_duplicates()
.sort_values(["profile","variant"]))Filter by profile
pid = "262e49ec20c32c41"
p_docs = docs.filter(lambda x: x["profile_id"] == pid)
p_queries = queries.filter(lambda x: x["profile_id"] == pid)
p_qrels = qrels.filter(lambda x: x["profile_id"] == pid)Temporal / multi-label slices
q_temporal = queries.filter(lambda x: x["variant"] == "temporal")
q_multi = queries.filter(lambda x: x["is_multi"])Pandas Version
import pandas as pd
from datasets import load_dataset
dataset_id = "frankjc2022/semantic-history-search"
docs_pd = load_dataset(dataset_id, name="docs")["train"].to_pandas()
queries_pd = load_dataset(dataset_id, name="queries")["train"].to_pandas()
qrels_pd = load_dataset(dataset_id, name="qrels")["train"].to_pandas()
q = queries_pd[["query_id","search_query","profile_id","variant","is_multi"]].set_index("query_id")
r = qrels_pd[["query_id","doc_id","rank","relevance"]].set_index("query_id")
d = docs_pd[["doc_id","url","title"]].set_index("doc_id")
# Reconstruct (query <-> doc/url) pairs
qr = r.join(q, how="inner").reset_index()
query_pairs = (qr.join(d, on="doc_id", how="left")
.sort_values(["query_id","rank"])
.reset_index(drop=True))Evaluation
This dataset is intended for retrieval evaluation. Please see the repository for the evaluation scripts/notebooks and metric implementations:
- Precision@k, Recall@k, nDCG@k
- Reciprocal Rank (RR), Average Precision (AP)
- On-Topic Rate@k
Synthetic Data Generation
All profiles, queries, and qrels are synthetic. The pipeline creates per-profile histories and LLM-judged relevance pairs from public English documents.
Overview
- Source: MS MARCO documents (msmarco-docs.tsv); keep top 500k rows with
docid,url,title,body(English).
- Normalize: build a unified table with
url,title,description,topic,lang,domain,combined_text - description = first 300 chars of
body - filter titles to length 5-200
combined_text = title + " " + description
- Sample: draw ~50k examples (English-only for this HF release).
- Profiles: create 25 synthetic profiles; for each, sample 1k-5k items aligned with profile themes; set random
frecency(100-5000) and incrementallast_visit_date.
- Queries & qrels: generate profile-specific queries with an LLM; judge relevance over the profile history; export
qrelswithrankandrelevance=1and concisequery.csvper profile.
Code (full scripts & notebooks):
- https://github.com/mozilla/smartsearch/tree/temporalawareness/preprocessing/generate_profiles
- https://github.com/mozilla/smartsearch/blob/temporalawareness/notebooks/generate_history.ipynb
Additional Dataset
We also include a second dataset built from publicly available synthetic histories:
- Source repo: https://github.com/komosny/synthetic-browsing-history
- Details/paper: https://pmc.ncbi.nlm.nih.gov/articles/PMC11754914/
Countries (English-focused): Australia, Canada, United Kingdom, United States.
Preprocessing
- Deduplicate by URL.
- Fetch title and description with priorities:
- title:
<title>→meta[property=og:title]→meta[name=twitter:title]→<h1> - description:
meta[name=description]→meta[property=og:description]→meta[name=twitter:description]→summary - Enforce Firefox-style limits: title ≤ 4096, description ≤ 256.
- Drop records with no title and no description.
Query Construction
- For each profile, randomly sample 50 URLs.
- Use an LLM (gpt-5-mini) to generate 50 semantic search queries that should retrieve the given URL, conditioning on its title and description.
Raw Layer
We maintain a reference raw/ tree for repro, but the canonical interface is the Parquet layer:
raw/profiles/<variant>/<single|multi-label>/<profile>/
├── history.csv
├── query.csv
└── temporal_context.json # only for temporalUse Parquet for all experiments. raw/ is reference-only; no remote execution loaders.