Skip to content

MongoDB Schema

EchoStats uses MongoDB as its primary data store. This page documents every collection, its fields, indexes, and relationships.

Collection Overview

CollectionPurpose
usersUser accounts linked to Spotify
spotify_tokensEncrypted OAuth tokens per user
listening_historyIndividual track play events
tracksSpotify track metadata and audio features
artistsSpotify artist metadata
albumsSpotify album metadata
playlistsSynced Spotify playlists with embedded tracks
daily_rollupsPre-aggregated daily listening statistics
analytics_snapshotsPre-computed analytics for time periods
sync_jobsBackground job tracking with step-by-step progress
api_logsExternal API call logs for monitoring
User
├── 1:1 → SpotifyTokens
├── 1:N → ListeningHistory → Track → Album
│ → Artist
├── 1:N → Playlist (tracks embedded)
├── 1:N → DailyRollup
├── 1:N → AnalyticsSnapshot
├── 1:N → SyncJob
└── 1:N → ApiLog

users

Stores EchoStats user accounts, each linked to a Spotify profile.

FieldTypeDefaultDescription
_idObjectIdautoPrimary key
spotify_idstringrequiredSpotify user ID
display_namestring""User display name
image_urlstring""Profile image URL
countrystring""ISO country code
productstring""Spotify subscription (premium, free)
created_atdatetimeutcnow()Account creation timestamp
last_synced_atdatetime | nullnullLast successful data sync

Indexes:

FieldsTypeNotes
spotify_iduniqueUser lookup by Spotify ID

Example document:

{
"_id": "682a3b1f2e4a5c6d7e8f9012",
"spotify_id": "atul123",
"display_name": "Atul",
"image_url": "https://i.scdn.co/image/ab67...",
"country": "US",
"product": "premium",
"created_at": "2025-01-01T00:00:00Z",
"last_synced_at": "2025-01-15T14:00:00Z"
}

spotify_tokens

Stores encrypted Spotify OAuth refresh tokens. One document per user.

FieldTypeDefaultDescription
_idObjectIdautoPrimary key
user_idstringrequiredReference to users._id
refresh_token_encryptedstringrequiredAES-256 encrypted refresh token
expires_atdatetimerequiredToken expiration time
scopestring""OAuth scopes granted

Indexes:

FieldsTypeNotes
user_idsimpleToken lookup by user

Example document:

{
"_id": "682a3b1f2e4a5c6d7e8f9013",
"user_id": "682a3b1f2e4a5c6d7e8f9012",
"refresh_token_encrypted": "gAAAAABl...",
"expires_at": "2025-01-15T15:00:00Z",
"scope": "user-read-recently-played user-read-playback-state user-library-read"
}

listening_history

Records individual track play events. Each document represents one play of one track by one user.

FieldTypeDefaultDescription
_idObjectIdautoPrimary key
user_idstringrequiredReference to users._id
trackembedded objectrequiredTrack snapshot (see below)
played_atdatetimerequiredWhen the track was played
ms_playedint | nullnullMilliseconds listened (from imports)
sourcestring"api"Data source: api or import
context_typestring""Playback context: playlist, album, artist
context_uristring""Spotify context URI

Embedded track object:

FieldTypeDescription
spotify_idstringSpotify track ID
namestringTrack name
artist_namestringPrimary artist name
album_namestringAlbum name
album_image_urlstringAlbum cover art URL
duration_msintTrack duration in ms

Indexes:

FieldsTypeNotes
user_idsimpleFilter by user
played_atsimpleTime-range queries
track.spotify_idsimpleFilter by track
(user_id, played_at DESC)compoundPaginated history queries
(user_id, track.spotify_id, played_at)compound uniquePrevents duplicate play events

Example document:

{
"_id": "682a3b1f2e4a5c6d7e8f9014",
"user_id": "682a3b1f2e4a5c6d7e8f9012",
"track": {
"spotify_id": "2kRFrWaLWifQkBFasAWgMo",
"name": "Everything In Its Right Place",
"artist_name": "Radiohead",
"album_name": "Kid A",
"album_image_url": "https://i.scdn.co/image/ab67...",
"duration_ms": 250000
},
"played_at": "2025-01-15T14:32:00Z",
"ms_played": 245000,
"source": "api",
"context_type": "album",
"context_uri": "spotify:album:6GjwtEZcfenmof6l18N7T7"
}

tracks

Stores Spotify track metadata including audio features. Tracks are shared across users — one document per Spotify track ID.

FieldTypeDefaultDescription
_idObjectIdautoPrimary key
spotify_idstringrequiredSpotify track ID
namestringrequiredTrack name
artistsarray of embedded obj[]Artist references (see below)
albumembedded obj | nullnullAlbum reference (see below)
duration_msint0Duration in milliseconds
popularityint0Spotify popularity score (0–100)
explicitboolfalseExplicit content flag
preview_urlstring | nullnull30-second preview audio URL
external_urlstring""Spotify web URL
audio_featuresembedded obj | nullnullAudio analysis (see below)
genresarray of string[]Genre tags
fetched_atdatetimeutcnow()When the track was fetched
updated_atdatetimeutcnow()Last metadata update

Embedded artists[] object:

FieldTypeDescription
spotify_idstringSpotify artist ID
namestringArtist name

Embedded album object:

FieldTypeDescription
spotify_idstringSpotify album ID
namestringAlbum name
image_urlstringAlbum cover art URL
release_datestringRelease date (YYYY-MM-DD)

Embedded audio_features object:

FieldTypeRangeDescription
danceabilityfloat0.0–1.0How suitable for dancing
energyfloat0.0–1.0Perceptual intensity
keyint0–11Pitch class (0=C, 1=C#, …, 11=B)
loudnessfloatdBOverall loudness
modeint0 or 1Modality (0=minor, 1=major)
speechinessfloat0.0–1.0Presence of spoken words
acousticnessfloat0.0–1.0Acoustic confidence
instrumentalnessfloat0.0–1.0No vocals prediction
livenessfloat0.0–1.0Live audience presence
valencefloat0.0–1.0Musical positiveness
tempofloatBPMEstimated tempo
duration_msintmsDuration from audio analysis
time_signatureint3–7Estimated time signature

Indexes:

FieldsTypeNotes
spotify_iduniqueTrack deduplication
artists.spotify_idsimpleFind tracks by artist
namesimpleSearch tracks by name

Example document:

{
"_id": "682a3b1f2e4a5c6d7e8f9015",
"spotify_id": "2kRFrWaLWifQkBFasAWgMo",
"name": "Everything In Its Right Place",
"artists": [
{ "spotify_id": "4Z8W4fKeB5YxbusRsdQVPb", "name": "Radiohead" }
],
"album": {
"spotify_id": "6GjwtEZcfenmof6l18N7T7",
"name": "Kid A",
"image_url": "https://i.scdn.co/image/ab67...",
"release_date": "2000-10-02"
},
"duration_ms": 250000,
"popularity": 72,
"explicit": false,
"preview_url": "https://p.scdn.co/mp3-preview/...",
"external_url": "https://open.spotify.com/track/2kRFrWaLWifQkBFasAWgMo",
"audio_features": {
"danceability": 0.45,
"energy": 0.63,
"key": 4,
"loudness": -9.2,
"mode": 1,
"speechiness": 0.04,
"acousticness": 0.04,
"instrumentalness": 0.73,
"liveness": 0.11,
"valence": 0.22,
"tempo": 156.1,
"duration_ms": 250000,
"time_signature": 4
},
"genres": ["art rock", "alternative rock"],
"fetched_at": "2025-01-10T12:00:00Z",
"updated_at": "2025-01-15T14:00:00Z"
}

artists

Stores Spotify artist metadata. One document per artist, shared across users.

FieldTypeDefaultDescription
_idObjectIdautoPrimary key
spotify_idstringrequiredSpotify artist ID
namestringrequiredArtist name
genresarray of string[]Genre tags
popularityint0Spotify popularity (0–100)
followersint0Follower count
imagesarray of embedded obj[]Artist images
external_urlstring""Spotify profile URL
fetched_atdatetimeutcnow()When fetched
updated_atdatetimeutcnow()Last update

Embedded images[] object:

FieldTypeDescription
urlstringImage URL
heightint | nullHeight in pixels
widthint | nullWidth in pixels

Computed properties:

  • image_url — returns the URL of the first image, or empty string if none.

Indexes:

FieldsTypeNotes
spotify_iduniqueArtist deduplication
genressimpleFilter artists by genre
namesimpleSearch artists by name

Example document:

{
"_id": "682a3b1f2e4a5c6d7e8f9016",
"spotify_id": "4Z8W4fKeB5YxbusRsdQVPb",
"name": "Radiohead",
"genres": ["art rock", "alternative rock", "experimental"],
"popularity": 78,
"followers": 8500000,
"images": [
{ "url": "https://i.scdn.co/image/ab67...", "height": 640, "width": 640 },
{ "url": "https://i.scdn.co/image/ab67...", "height": 320, "width": 320 }
],
"external_url": "https://open.spotify.com/artist/4Z8W4fKeB5YxbusRsdQVPb",
"fetched_at": "2025-01-10T12:00:00Z",
"updated_at": "2025-01-15T14:00:00Z"
}

albums

Stores Spotify album metadata. One document per album, shared across users.

FieldTypeDefaultDescription
_idObjectIdautoPrimary key
spotify_idstringrequiredSpotify album ID
namestringrequiredAlbum name
album_typestring""Type: album, single, compilation
artistsarray of embedded obj[]Album artists
imagesarray of embedded obj[]Album cover art
release_datestring""Release date (YYYY-MM-DD)
total_tracksint0Number of tracks
genresarray of string[]Genre tags
popularityint0Spotify popularity (0–100)
external_urlstring""Spotify web URL
fetched_atdatetimeutcnow()When fetched

Embedded artists[] object:

FieldTypeDescription
spotify_idstringSpotify artist ID
namestringArtist name

Embedded images[] object:

FieldTypeDescription
urlstringImage URL
heightint | nullHeight in pixels
widthint | nullWidth in pixels

Computed properties:

  • image_url — returns the URL of the first image, or empty string if none.

Indexes:

FieldsTypeNotes
spotify_iduniqueAlbum deduplication

Example document:

{
"_id": "682a3b1f2e4a5c6d7e8f9017",
"spotify_id": "6GjwtEZcfenmof6l18N7T7",
"name": "Kid A",
"album_type": "album",
"artists": [
{ "spotify_id": "4Z8W4fKeB5YxbusRsdQVPb", "name": "Radiohead" }
],
"images": [
{ "url": "https://i.scdn.co/image/ab67...", "height": 640, "width": 640 }
],
"release_date": "2000-10-02",
"total_tracks": 10,
"genres": ["art rock"],
"popularity": 75,
"external_url": "https://open.spotify.com/album/6GjwtEZcfenmof6l18N7T7",
"fetched_at": "2025-01-10T12:00:00Z"
}

playlists

Stores Spotify playlists with their full track list embedded. Scoped per user.

FieldTypeDefaultDescription
_idObjectIdautoPrimary key
spotify_idstringrequiredSpotify playlist ID
user_idstringrequiredReference to users._id
namestringrequiredPlaylist name
descriptionstring""Playlist description
publicbooltruePublic visibility
collaborativeboolfalseCollaborative editing
imagesarray of embedded obj[]Cover images
owner_idstring""Spotify owner ID
owner_namestring""Spotify owner display name
total_tracksint0Track count
tracksarray of embedded obj[]Embedded track list
snapshot_idstring""Spotify snapshot version ID
external_urlstring""Spotify web URL
fetched_atdatetimeutcnow()When fetched

Embedded tracks[] object:

FieldTypeDescription
spotify_idstringSpotify track ID
namestringTrack name
artist_namestringPrimary artist name
album_namestringAlbum name
added_atdatetime | nullWhen added to playlist
duration_msintTrack duration

Indexes:

FieldsTypeNotes
spotify_idsimplePlaylist lookup
user_idsimpleUser’s playlists
(user_id, spotify_id)compoundUser-scoped playlist lookup

Example document:

{
"_id": "682a3b1f2e4a5c6d7e8f9018",
"spotify_id": "37i9dQZF1DXcBWIGoYBM5M",
"user_id": "682a3b1f2e4a5c6d7e8f9012",
"name": "Today's Top Hits",
"description": "The hottest tracks right now.",
"public": true,
"collaborative": false,
"images": [
{ "url": "https://i.scdn.co/image/ab67...", "height": 640, "width": 640 }
],
"owner_id": "spotify",
"owner_name": "Spotify",
"total_tracks": 50,
"tracks": [
{
"spotify_id": "2kRFrWaLWifQkBFasAWgMo",
"name": "Everything In Its Right Place",
"artist_name": "Radiohead",
"album_name": "Kid A",
"added_at": "2025-01-10T00:00:00Z",
"duration_ms": 250000
}
],
"snapshot_id": "MTY3...",
"external_url": "https://open.spotify.com/playlist/37i9dQZF1DXcBWIGoYBM5M",
"fetched_at": "2025-01-15T14:00:00Z"
}

daily_rollups

Pre-aggregated listening statistics for one user per day. Built from listening_history records to enable fast analytics queries.

FieldTypeDefaultDescription
_idObjectIdautoPrimary key
user_idstringrequiredReference to users._id
datestringrequiredDate in YYYY-MM-DD format
day_of_weekint0Day of week (0=Monday, 6=Sunday)
total_playsint0Total plays for the day
total_msint0Total milliseconds listened
hourlyarray of embedded obj[]Plays broken down by hour
artist_playsdict (str → int){}Artist name → play count
album_playsdict (str → int){}Album name → play count
track_playsarray of embedded obj[]Track play breakdown

Embedded hourly[] object:

FieldTypeDescription
hourintHour of day (0–23)
countintNumber of plays
msintMilliseconds listened

Embedded track_plays[] object:

FieldTypeDescription
spotify_idstringSpotify track ID
namestringTrack name
artist_namestringArtist name
countintNumber of plays

Indexes:

FieldsTypeNotes
user_idsimpleFilter by user
datesimpleFilter by date
(user_id, date ASC)compoundAscending time-range queries
(user_id, date DESC)compoundDescending (latest first)

Example document:

{
"_id": "682a3b1f2e4a5c6d7e8f9019",
"user_id": "682a3b1f2e4a5c6d7e8f9012",
"date": "2025-01-15",
"day_of_week": 2,
"total_plays": 42,
"total_ms": 8400000,
"hourly": [
{ "hour": 9, "count": 5, "ms": 1000000 },
{ "hour": 14, "count": 12, "ms": 2400000 },
{ "hour": 20, "count": 8, "ms": 1600000 }
],
"artist_plays": {
"Radiohead": 15,
"Aphex Twin": 8,
"Boards of Canada": 5
},
"album_plays": {
"Kid A": 10,
"Selected Ambient Works 85-92": 6
},
"track_plays": [
{ "spotify_id": "2kRFrWaLWifQkBFasAWgMo", "name": "Everything In Its Right Place", "artist_name": "Radiohead", "count": 3 }
]
}

analytics_snapshots

Pre-computed user analytics for named time periods. These power the /api/v1/analytics/overview endpoint.

FieldTypeDefaultDescription
_idObjectIdautoPrimary key
user_idstringrequiredReference to users._id
periodstringrequiredPeriod: week, month, quarter, year, all_time
period_startdatetime | nullnullPeriod start boundary
period_enddatetime | nullnullPeriod end boundary
total_tracks_playedint0Total tracks played
total_ms_playedint0Total milliseconds
unique_tracksint0Unique track count
unique_artistsint0Unique artist count
unique_albumsint0Unique album count
unique_genresint0Unique genre count
listening_streak_daysint0Consecutive listening days
top_artistsarray of embedded obj[]Top artists (see TopItem below)
top_tracksarray of embedded obj[]Top tracks
top_genresarray of embedded obj[]Top genres
top_albumsarray of embedded obj[]Top albums
hourly_distributionarray of embedded obj[]Plays per hour
daily_distributionarray of embedded obj[]Plays per day of week
avg_audio_featuresembedded obj | nullnullAverage audio features
computed_atdatetimeutcnow()When snapshot was computed

Embedded TopItem object (used by top_artists, top_tracks, top_genres, top_albums):

FieldTypeDescription
spotify_idstringSpotify ID (empty for genres)
namestringDisplay name
play_countintNumber of plays
total_msintTotal listening time
image_urlstringImage URL
rankintPosition in ranking

Embedded hourly_distribution[] object:

FieldTypeDescription
hourintHour of day (0–23)
countintNumber of plays
total_msintMilliseconds listened

Embedded daily_distribution[] object:

FieldTypeDescription
dayintDay of week (0=Mon, 6=Sun)
countintNumber of plays
total_msintMilliseconds listened

Embedded avg_audio_features object:

FieldTypeDescription
danceabilityfloatAverage danceability (0–1)
energyfloatAverage energy (0–1)
valencefloatAverage valence (0–1)
acousticnessfloatAverage acousticness (0–1)
instrumentalnessfloatAverage instrumentalness (0–1)
livenessfloatAverage liveness (0–1)
speechinessfloatAverage speechiness (0–1)
tempofloatAverage tempo (BPM)

Indexes:

FieldsTypeNotes
user_idsimpleFilter by user
periodsimpleFilter by period
(user_id, period)compoundUnique analytics per user+period

Example document:

{
"_id": "682a3b1f2e4a5c6d7e8f9020",
"user_id": "682a3b1f2e4a5c6d7e8f9012",
"period": "week",
"period_start": "2025-01-08T00:00:00Z",
"period_end": "2025-01-15T00:00:00Z",
"total_tracks_played": 319,
"total_ms_played": 64140000,
"unique_tracks": 279,
"unique_artists": 81,
"unique_albums": 45,
"unique_genres": 11,
"listening_streak_days": 5,
"top_artists": [
{ "spotify_id": "4Z8W4fKeB5YxbusRsdQVPb", "name": "Radiohead", "play_count": 45, "total_ms": 9000000, "image_url": "https://...", "rank": 1 }
],
"top_tracks": [
{ "spotify_id": "2kRFrWaLWifQkBFasAWgMo", "name": "Everything In Its Right Place", "play_count": 5, "total_ms": 1250000, "image_url": "", "rank": 1 }
],
"top_genres": [
{ "spotify_id": "", "name": "art rock", "play_count": 120, "total_ms": 24000000, "image_url": "", "rank": 1 }
],
"top_albums": [
{ "spotify_id": "6GjwtEZcfenmof6l18N7T7", "name": "Kid A", "play_count": 30, "total_ms": 6000000, "image_url": "https://...", "rank": 1 }
],
"hourly_distribution": [
{ "hour": 9, "count": 25, "total_ms": 5000000 },
{ "hour": 14, "count": 42, "total_ms": 8400000 }
],
"daily_distribution": [
{ "day": 0, "count": 48, "total_ms": 9600000 },
{ "day": 2, "count": 52, "total_ms": 10400000 }
],
"avg_audio_features": {
"danceability": 0.52,
"energy": 0.61,
"valence": 0.38,
"acousticness": 0.24,
"instrumentalness": 0.15,
"liveness": 0.12,
"speechiness": 0.05,
"tempo": 122.5
},
"computed_at": "2025-01-15T14:05:00Z"
}

sync_jobs

Tracks background data sync jobs with step-by-step progress. Used by the worker service.

FieldTypeDefaultDescription
_idObjectIdautoPrimary key
user_idstringrequiredReference to users._id
job_typestring"periodic"Type: periodic, rollup_build, import, initial
statusstring"pending"Status: pending, running, completed, failed
started_atdatetime | nullnullJob start time
completed_atdatetime | nullnullJob completion time
items_processedint0Items completed so far
items_totalint0Total items to process
error_messagestring""Error details if failed
stepsarray of embedded obj[]Execution steps (see below)
created_atdatetimeutcnow()Job creation timestamp

Embedded steps[] object:

FieldTypeDescription
actionstringStep name (e.g. recently_played, audio_features, playlists)
statusstringpending, running, completed, failed
detailstringHuman-readable status message
itemsintItems processed in this step
started_atdatetimeStep start time
completed_atdatetime | nullStep completion time
errorstringError message if failed

Indexes:

FieldsTypeNotes
user_idsimpleFilter by user
statussimpleFilter by status
(user_id, job_type, created_at DESC)compoundLatest job by type per user

Example document:

{
"_id": "682a3b1f2e4a5c6d7e8f9021",
"user_id": "682a3b1f2e4a5c6d7e8f9012",
"job_type": "periodic",
"status": "completed",
"started_at": "2025-01-15T14:00:00Z",
"completed_at": "2025-01-15T14:00:12Z",
"items_processed": 50,
"items_total": 50,
"error_message": "",
"steps": [
{
"action": "recently_played",
"status": "completed",
"detail": "Fetched 50 recent tracks",
"items": 50,
"started_at": "2025-01-15T14:00:00Z",
"completed_at": "2025-01-15T14:00:03Z",
"error": ""
},
{
"action": "audio_features",
"status": "completed",
"detail": "Updated audio features for 12 tracks",
"items": 12,
"started_at": "2025-01-15T14:00:03Z",
"completed_at": "2025-01-15T14:00:05Z",
"error": ""
},
{
"action": "playlists",
"status": "completed",
"detail": "Synced 24 playlists",
"items": 24,
"started_at": "2025-01-15T14:00:05Z",
"completed_at": "2025-01-15T14:00:10Z",
"error": ""
}
],
"created_at": "2025-01-15T14:00:00Z"
}

api_logs

Logs outbound API calls (primarily to Spotify) for monitoring and debugging.

FieldTypeDefaultDescription
_idObjectIdautoPrimary key
user_idstring""Associated user (optional)
servicestring"spotify"API service: spotify, internal
methodstring""HTTP method (GET, POST, etc.)
endpointstring""API endpoint path
status_codeint0HTTP response status code
latency_msfloat0.0Request latency in milliseconds
errorstring""Error message if request failed
request_idstring""Unique request correlation ID
timestampdatetimeutcnow()When the call was made

Indexes:

FieldsTypeNotes
user_idsimpleFilter by user
timestampsimpleTime-range queries
(user_id, timestamp DESC)compoundUser’s recent API calls
(service, status_code)compoundService health monitoring

Example document:

{
"_id": "682a3b1f2e4a5c6d7e8f9022",
"user_id": "682a3b1f2e4a5c6d7e8f9012",
"service": "spotify",
"method": "GET",
"endpoint": "/v1/me/player/recently-played",
"status_code": 200,
"latency_ms": 142.5,
"error": "",
"request_id": "req-abc123",
"timestamp": "2025-01-15T14:00:01Z"
}