Anki 2.0 Annotated Schema

Аннотированная схема на русском языке Формат Базы Данных Anki DataBase Format по-русски

Спасибо, люди! Хотел уже делать самостоятельно,
когда додумался погуглить anki ord mod usn и нашёл это!

Note

Краткое описание структуры базы данных Anki 2.0 из вики АнкиДроид

Взятое за основу: github.com/dae/anki/blob/master/anki/storage.py

Благодарности @sartak и @fasiha за то, что они и замутили всё это

Немного иначе оформленный перевод структуры в форматах SQL и JSON есть и на github wiki.
Поменьше шрифт, разная длина строки. Возможно, в каком-то варианте и проще покажется.

Anki 2.0 Структура базы данных

usn: Update Sequence Number

Во всех таблицах вы встретите это магическое поле: usn

Порядковый Номер Обновления

Из исходного кода появляется догадка, что Anki последовательно увеличивает этот номер каждый раз, когда выполняет синхронизацию с AnkiWeb, и применяет этот номер к карточкам, которые были синхронизированы.

Например, если в базе данных установлен максимальный usn 1230 для коллекции, то карточки будут иметь различные номера вплоть до 1229.

Используется, чтобы выяснить различия при синхронизации.

Значение -1 сигнализирует о необходимости
выгрузить изменения на сервер с компа.

Если локальный usn меньше < usn на сервере,
то это значит, что надо загрузить изменения с сервера на комп.

cards

Карточки суть то, что вы просматриваете.

Для каждой записи могут существовать несколько карточек,
если это определено шаблонами типа записей.

Карточки

CREATE TABLE cards (
    id              integer primary key,
      -- уникальный идентификатор карточки
      -- по сути это количество миллисекунд с 01.01.1970
      -- на момент, когда она была создана
    nid             integer not null,
      -- notes.id
      -- идентификатор записи данной карточки
    did             integer not null,
      -- deck id
      -- идентификатор колоды данной карточки
      -- (все колоды - в таблице коллекции col)
    ord             integer not null,
      -- ordinal
      -- порядковый номер карточки среди шаблонов типа записей
      -- допустимые номера от 0 до количества шаблонов - 1
    mod             integer not null,
      -- время изменения карточки
      -- по сути это количество миллисекунд с 01.01.1970
    usn             integer not null,
      -- Update Sequence Number: Порядковый Номер Обновления
    type            integer not null,
      -- 0=новая, 1=разучиваемая, 2=проверяемая, 3=переучиваемая
    queue           integer not null,
      -- 0=новая, 1=разучиваемая, 2=проверяемая, 3=переучиваемая
      -- -1=исключена, -2=отложена пользователем, -3=отложена программно(?)
    due             integer not null,
      -- Пора имеет различное значение для разных типов:
      --  новая: note.id или случайное целое
      --   разучиваемая: время следующего показа сегодня (вплоть до миллисекунд)
      --    проверяемая: номер дня относительно времени создания коллекции(?)
    ivl             integer not null,
      -- интервал: отрицательный = секунды, положительный = дни
    factor          integer not null,
      -- фактор (десятикратная лёгкость)
    reps            integer not null,
      -- количество просмотров
    lapses          integer not null,
      -- количество раз, когда повторяемая карточка переходила из состояния
      --  `последний раз отвечена верно` в состояние `последний раз ответ дан неверно`
    left            integer not null,
      -- количество повторов, которое осталось до завершения(?)
    odue            integer not null,
      -- original due: исходное место в очереди (только для фильтр-колод)
    odid            integer not null,
      -- original did: домашняя колода (только для фильтр-колод)
    flags           integer not null,
      -- в настоящее время не используется
    data            text not null
      -- в настоящее время не используется.
      -- Возможно от перехода с Anki1? Или данные для дополнений?
);

cards.odid и cards.odue сохраняют для карточек, находящихся в фильтрованной колоде, значения их полей cards.did и cards.due на момент перестроения фильтр-колоды (надо же знать, какая у карточки домашняя колода, куда её рано или поздно придётся возвращать, и какое у неё там место в очереди).

notes

То, что в Anki2 называется записи, в Anki1 называлось факты.
То, что называется типы записей, называлось модели.

Записи являются основой для создания карточек

и содержат исходную информацию, которая в отформатированном виде показывается на карточке.

Может быть, даже на нескольких карточках одновременно, если так определено в шаблонах карточек для данного типа записей.

Записи

CREATE TABLE notes (
    id              integer primary key,
      -- уникальный идентификатор записи
      -- по сути это количество миллисекунд с 01.01.1970
      -- на момент, когда она была создана
    guid            text not null,
      -- какой-то глобальный идентификатор,
      -- скорее всего используемый при синхронизации
    mid             integer not null,
      -- идентификатор типа записей (модели)
    mod             integer not null,
      -- время изменения записи
      -- по сути это количество миллисекунд с 01.01.1970
    usn             integer not null,
      -- update sequence number: для отыскания различий при синхронизации
    tags            text not null,
      -- метки, разделённые пробелом. похоже, содержит пробелы и в начале
      -- и в конце поля, так что можно делать так: LIKE "% tag %"
      -- что находить в точности метку и отсекать другие метки,
      -- где она является лишь частью.
    flds            text not null,
      -- значения полей в этой записи, разделены 0x1f (31).
    sfld            integer not null,
      -- поле сортировки: используется для ускорения поиска дубликатов
    csum            integer not null,
      -- контрольная сумма, используется при проверке на дубликаты.
      -- первые восемь цифр хэша sha1 первого поля
    flags           integer not null,
      -- не используется
    data            text not null
      -- не используется
);

col

Собрание. Полное собрание колод, настроек, типов записей и групп настроек колод.

И всё это изобилие в одной единственной строке, значение в поле — в формате JSON.

Коллекция

CREATE TABLE col (
    id              integer primary key,
      -- произвольное число, поскольку
      -- строка в этой таблице всегда одна
    crt             integer not null,
      -- штемпель времени создания коллекции
    mod             integer not null,
      -- последние изменения в миллисекундах
    scm             integer not null,
      -- schema mod time
      -- время последних изменений в схеме БД
      -- если поле на сервере отличается,
      -- то будет запрошена полная синхронизация.
    ver             integer not null,
      -- версия? у меня *11* (?)
    dty             integer not null,
      -- dirty: всегда 0. не используется(?)
    usn             integer not null,
      -- update sequence number: для синхронизации.
    ls              integer not null,
      -- "last sync time" - время последней синхронизации
    conf            text not null,
      -- json объект содержит опции, которые были синхронизированы
    models          text not null,
      -- json массив с типами записей
    decks           text not null,
      -- json массив с колодами
    dconf           text not null,
      -- json массив с группами настроек колод
    tags            text not null
      -- массив меток, которые используются в данной коллекции
);

revlog

revlog — это история просмотров.

содержит строку для каждого раза,

когда вы нажимали кнопку
Не помню, Трудно, В самый раз, Очень легко

Журнал просмотров карточек

CREATE TABLE revlog (
    id              integer primary key,
       -- момент времени, когда был дан ответ
    cid             integer not null,
       -- cards.id идентификатор карточки
    usn             integer not null,
       -- update sequence number: для синхронизации
    ease            integer not null,
       -- какой ответ был дан:
       -- значение зависит от количества кнопок,
       -- т.е. от состояния карточки на момент ответа
       -- (новая, обучение, пора, переучивание).
       --   1(Не помню), 2(Трудно), 3(В самый раз), 4(Очень легко)
       --   1(Не помню), 2(В самый раз), 3(Очень легко)
       --   1(Не помню), 2(В самый раз)
    ivl             integer not null,
       -- interval: интервал
    lastIvl         integer not null,
       -- last interval: предыдущий интервал
    factor          integer not null,
       -- десятикратная лёгкость
    time            integer not null,
       -- сколь долго вы смотрели на карточку, вплоть до 60000 (60s)
    type            integer not null
);

Спасибо, это очень полезно! Кстати, revlog.time в действительности может быть и больше 60e3, поскольку вы можете настроить Anki Не учитывать время ответа больше 0 секунд в Группе настроек колод на вкладке Общие.

graves

Информация об удалённых объектах

для их последующего удаления
на другой стороне при синхронизации (?)

Могилы

CREATE TABLE graves (
    usn             integer not null,
    oid             integer not null,
    type            integer not null
);

usn должен быть установлен в -1

oid оригинальный идентификатор удалённого объекта

graves.type обозначает тип данных, которые были удалены:

  • 0 выставляется для карточек,

  • 1 для записей,

  • 2 для колод.

Об этом нам говорят константы REM_CARD, REM_NOTE и REM_DECK
определённые в github.com/dae/anki/blob/master/anki/consts.py
и используемые в github.com/dae/anki/blob/master/anki/sync.py

indexes

Индексы

CREATE INDEX ix_cards_nid on cards (nid);
CREATE INDEX ix_cards_sched on cards (did, queue, due);
CREATE INDEX ix_cards_usn on cards (usn);
CREATE INDEX ix_notes_csum on notes (csum);
CREATE INDEX ix_notes_usn on notes (usn);
CREATE INDEX ix_revlog_cid on revlog (cid);
CREATE INDEX ix_revlog_usn on revlog (usn);

Типы записей в JSON

col.models описывает специфическую конфигурацию типа записей в формате JSON

Каждый объект суть величина ключа, который есть идентификатор модели
ключи — это идентификаторы типов записей (миллисекунды с начала эпохи 01.01.1970)

Модель — так в первой версии Anki назывались типы записей.

И вот описание этих полей:

{
    css : "CSS, единый для всех шаблонов карточек",
    did :
        "идентификатор предпочитаемой колоды, 1 для колоды по умолчанию",
    flds : [
             "Для каждого поля данного типа записей",
             {
               font : "отображаемый шрифт",
               size : "размер шрифта",
               media : "? массив медиа-файлов, ? не используется ?",
               name : "имя поля",
               ord : "как и cards.ord: порядковый номер поля -1",
               rtl : "ложь/истина (булеан), шрифт справа налево",
               sticky : "Помнить последние введённые данные"

             }
           ],
    id : "cards.mid",
    latexPost : "послесловие для LaTeX",
    latexPre : "предисловие для LaTeX",
    mod : "? число",
    name : "имя типа записей",
    req : [
            "Массив из трёх элементов:",
            [
              "индекс массива, 0, 1, ...",
              "? строка, all",
              "ещё один массив",
              ["кажется, стова индекс массива"]
            ]
          ],
    sortf : "номер поля с 0, используемого для сортировки в Обзоре",
    tags : "сохраняются метки, использованные последними для данного типа записей",
    tmpls : [
              "массив шаблонов",
              {
                qfmt : "строка формата вопроса",
                afmt : "строка формата ответа",
                bafmt : "?",
                bqfmt : "?",
                did : "? null",
                name : "имя шаблонов",
                ord : "номер шаблонов"
              }
            ],
    type : "0 для Основных, 1 для Пропусков",
    usn : "порядковый номер обновления",
    vers : "Номер устаревшей версии (неиспользуемый), пустой массив"
}

cards.ord в действительности, кажется, является
индексом макета карточки в типе записей, стартующим с 0

Скажем, для карточки с пропуском To be{{c6::,}} or not to be
значением cards.ord будёт 5(пятёрочка).

Якобы(?) Найдено в исходном коде Anki:

  • cards.lapses сколько раз была забыта повторяемая карточка (количество попаданий в очередь переучиваемых).

    • Факты забывания новых и разучиваемых (обучение) карточек сюда НЕ суммируются.

  • cards.type кажется, так:

    • learning = 0,

    • review = 1,

    • lapse/relearn = 2,

    • filtered = 3

  • cards.queue кажется, так:

    • suspended = -1

    • new = 0,

    • learn = 1,

    • review = 2,

    • relearn = 3

Нашёл такое пояснение в github.com/dae/anki/blob/master/anki/cards.py:

# Type: 0=new, 1=learning, 2=due
# Queue: same as above, and:
#        -1=suspended, -2=user buried, -3=sched buried
# Due is used differently for different queues.
# - new queue: note id or random int
# - rev queue: integer day
# - lrn queue: integer timestamp
Type тип
  • 0 новые

  • 1 обучение

  • 2 пора проверить

Queue очередь
  • 0 новые

  • 1 обучение

  • 2 пора проверить

  • -1 исключённые

  • -2 отложенные пользователем

  • -3 отложенные как связанные(?)

Due используется по-разному для разных типов
  • новые: идентификатор записи или случайное целое?

  • обучение: целое - отметка времени

  • пора проверить: целое - номер дня

Колоды в JSONObjects

Аннотированное описание объектов JSON в поле decks таблицы col:

{
    id:		"идентификатор колоды (автоматически генерируемое длинное целое)",
    name:	"имя колоды",
    desc:	"описание колоды"
    usn:	"Update Sequence Number: Порядковый Номер Обновления",
    mod:	"время последней модификации",
    collapsed:	"правда, когда колода свёрнута",
    browserCollapsed: "правда, когда колода свёрнута в Обозревателе",
    dyn:	"1 если динамическая колода (больше известная как фильтрованная)",
    conf:	"id of option group from dconf in `col` table",
    extendNew:	"увеличенный лимит новых карточек (для дополнительного обучения)",
    extendRev:	"увеличенный лимит просмотров карточек (для дополнительного обучения)",
    newToday:	"Двухчисловой массив, как-то используемый для дополнительного обучения",
    timeToday:	"Двухчисловой массив, как-то используемый для дополнительного обучения",
    lrnToday:	"Двухчисловой массив, как-то используемый для дополнительного обучения",
    revToday:	"Двухчисловой массив, как-то используемый для дополнительного обучения",
}

Примечания

Не уверен за каждую букву, поскольку эти имена придумал Дамиен Элмс, но:

crt

created time / время создания

ver

collection version / версия коллекции

scm

schema version / версия схемы

dty

dirty flag?

usn

universal serial number / используется при синхронизации

Note
прим. перев. — не знаю, что делать со всем этим

как ни пытаюсь улучшить — ощущение какой-то каши
Есть идеи, что можно сделать?

.

 

2017-04-17

The content here is distributed under the CC BY-SA license:
creativecommons.org/licenses/by-sa/4.0/   (лицензия по-русски)

Свои вопросы и предложения по Anki засылайте  на форум  reddit.com/r/AnkiRu/ 

.