asopi tech
asopi techIndie Developer

Asopitech With · Flashcards

Anki with Alopex DB

Anki is a flashcard app that schedules each review for you. What if we rebuilt it with Alopex DB?

Updated: Oct 12, 2026

What the service does

Anki is a flashcard app. Cards are grouped into decks, and after each review the user rates their recall so the next review comes when the card is about to be forgotten. The data is cards, decks and review history.

Where the data lives

  • SQL tables: cards, decks and review history live in tables. Today’s cards come from a condition on the next review date.
  • Columnar tables: review history goes in a columnar table to aggregate recall over time per deck.
  • Vectors: embeddings of the front and back of each card find duplicate cards that ask the same thing.

Computing the next review date is app code.

Deployment

Alopex DB is embedded in the desktop and device apps. A deck lives in one file, so sharing a deck means sharing the file.

Why Alopex DB fits

A flashcard app gathers more cards and review history every year. Alopex DB handles the review schedule, history aggregation and duplicate detection inside one file.

See it in code

Cards, decks and the review history live in tables, and today’s due cards are pulled with SQL. Just the review core.

import alopex
from datetime import date

db = alopex.Database.open("anki.alopex")

# カード・デッキ・復習の履歴を持つ
db.execute_sql("""
    CREATE TABLE IF NOT EXISTS cards (
        id INTEGER PRIMARY KEY,
        deck TEXT,
        front TEXT,
        back TEXT,
        next_due DATE
    )
""")
db.execute_sql("""
    CREATE TABLE IF NOT EXISTS reviews (
        card_id INTEGER,
        correct INTEGER
    )
""")

cards = [
    (1, "英単語", "apple", "りんご", "2026-10-21"),
    (2, "英単語", "orange", "みかん", "2026-10-25"),
    (3, "歴史", "1192", "鎌倉幕府の成立", "2026-10-20"),
]
for c in cards:
    db.execute_sql("INSERT INTO cards (id, deck, front, back, next_due) VALUES (?, ?, ?, ?, ?)", list(c))
for card_id, correct in [(1, 1), (1, 0), (3, 1)]:
    db.execute_sql("INSERT INTO reviews (card_id, correct) VALUES (?, ?)", [card_id, correct])

# 今日までに期限が来たカード(今日復習する分)を引く
today = date.today().isoformat()
print("-- 今日の復習")
for row in db.execute_sql(
    "SELECT deck, front FROM cards WHERE next_due <= CAST(? AS DATE) ORDER BY next_due",
    [today],
):
    print("  ", row["deck"], row["front"])

# デッキごとの正答数を集計する
print("-- 正答数")
for row in db.execute_sql("""
    SELECT c.deck, SUM(r.correct) AS ok, COUNT(*) AS n
    FROM reviews AS r
    JOIN cards AS c ON c.id = r.card_id
    GROUP BY c.deck
"""):
    print("  ", row["deck"], f'{row["ok"]}/{row["n"]}')

The next-due-date condition pulls just today’s cards; the history aggregation is the same SQL.

Alopex DB runs the review schedule, history aggregation and duplicate detection in one file. Try it on a flashcard app; the quickstart shows how.