Pull to refresh

Comments 27

Там реализовано так:

revision
--rev_id
--rev_page
--rev_text_id
--rev_comment

--rev_parent_id

page
--page_id
--page_namespace
--page_title

--page_latest (где хранится айдишник последней ревизии)

text
--old_id
--old_text (сам текст страницы)
--old_flags

Каждое изменение текста страницы создаёт новую ревизию и новую запись в таблице text.

Выборка страницы происходит так:

SELECT page_title,old_text,old_flags  
FROM `page`,`revision`,`text`  
WHERE page_is_redirect = '0' 
AND page_namespace = '8' 
AND (page_title NOT LIKE '%/%' ) 
AND (page_latest=rev_id) 
AND (rev_text_id=old_id) 
AND (page_len <= 10000)  


А теперь, почему нам не подходит
1) один текст на страницу vs много ячеек в открытом документе.
2) у одного текста в медиавики — одна ревизия. У нас контент у ячеек может появляться в разных ревизиях.
3) Сама суть — у медиавики — кто-то зашел подправить текст. Сделал необходимые изменения, посмотрел на превью, сохранил. У нас — несколько пользователей как умолишённые одновременно заполняют ячейки, подбирают им цвет и т.п.

Теперь ещё по-поводу настоящих google docs — при просмотре истории ревизий — по умолчанию ревизии включают в себя не каждое изменение ячейки — а несколько недавних изменений, но если выбрать «show more detailed revisions» — тогда уже любое изменение можно посмотреть/откатить. Видно они место не экономят… Для нашей структуры, если необходимы абсолютно все изменения, а не только в рамках «жизни» ревизии — можно просто сделать так, чтобы ревизии всегда просрачивались экспаерелись.
Так может в google docs не хранят предыдущие ревизии полностью, а просто diff'ы документа от ревизии к ревизии хранят.
Такой развернутый ответ можно было бы и в статью поместить как пример :)
не проще, для вашего случая, сделать отдельную таблицу типа changes со структурой doc_id, cell_id, val_before, val_after, used_id, timestamp.

все изменения просто пишутся в нее. если вдруг надо чтото откатить — берем по ключу из doc_id, cell_id, timestamp.

для выбора текущей версии не меняется ничего.

по грубой оценке, раз в 12 будет быстрее в работе, а по простоте выборок и поддержки кода — почти в бесконечность.
А что нужно будет сделать для того чтобы посмотреть как выглядел документ раньше? В этом случае вся разница сведётся к тому, чтобы использовать timestamp вместо revision_id и быстрее точно не будет.
выбрать все изменения до заданного таймстемпа.

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

Как я полагаю, если делать через таймстемп (как из вашего примера) — нам понадобятся совершенно другие выборки. Т.е. последний вариант документа мы выбираем одним образом, предыдущие — совершенно по другому и как будет выглядеть запрос выбирающий значения ячеек на какую-то дату? Кроме того, как я полагаю, удаление записи будет выглядеть как затирание значений полей, для которых нужна эта версионность?

Восстановление данных также как и просмотр документа потребуют программирования отдельной логики. В описанном в посте примере просмотр данных на определённую ревизию и восстановление этих данных больше усилий не потребует.

Потом, у нас ещё есть не только значение ячейки, но и её цвет (а на реальном примере — будет намного больше параметров). Получается, что пока пользователь будет подбирать цвет ячейки каждый новый цвет будет создавать запись в бд.
к сожалению, вы не поняли идею. там все проще гораздо.
Объясните, пожалуйста. Такая структура придумывалась не один день, и вариант с timestamp также рассматривался, но был отсеен как раз из-за непростой и небыстрой выборки данных, просмотра данных на какую-то дату и слишком быстрому разрастанию бд. Не говоря уже о том, что с ревизиями — при необходимости можно сделать несколько веток одного документа.

В итоге хотелось бы выяснить какая структура была бы идеальна для описанных в посте нужд.
Версионность node в Drupal (схема таблиц)
В двух словах — каждое изменение документа сохраняется как новый документ и помечается как текущая ревизия документа.

cells
--id
--sheet_id
--row_number
--col_number
--creator_id

почему количество ячеек является статичной информацией и не требует версионности?
Про то, как реализована версионность в друпале почитаю посмотрю, спасибо за ссылку.

Про количество ячеек не понял вопроса. Если это про «row_number» и «col_number» — то это номер рядка и номер колонки.
Исходя из предпочтений, я бы сделал проще. Не пишу всех деталей, только подскажу идею.

Зачем вам геморрой с ревизиями, новой таблицей и всей лабудой? Сделайте так:

cells
--id
--sheet_id
--color
--content
--row_number
--col_number
--creator_id
--active (bool) — булевое значение, которое сообщает о том что это (1) самая последняя ревизия (2) архивная.
--modified (timestamp) — тут как бы данные для отката в разное время


Все. Теперь выборка будет почти такой как прежде.

select * from cells where row_number=3 and col_number=2 and active=1
И не нужно ничего достраивать.

Кто-то отредактировал запись?
Добавляем новую запись, только еще приписываем апдейт чтобы старую отметил как не активную.
Лень писать апдейт или нету времени?

Тогда меняем выборку
select * from cells where row_number=3 and col_number=2 Order By timestamp DESC

Откат к ревизии можно сделать так же просто. Можно вообще одним таймстампом обойтись но в силу возможных оптимизаций может пригодится. С ним по идее шустрее будет работать.
А. Вы уже сделали. Ну тогда вот вам второй вариант того же дела :)
Добавляем новую запись, только еще приписываем апдейт чтобы старую отметил как не активную.

А какой будет id у новой записи? Они будут дублироваться?
Конечно же новый. Ведь у нас «гражданство» ячейки определяют поля:
Sheet, Row, Col.
А если у ячеек есть ещё дочерняя таблица, например `styles` у которой есть столбец `cell_id` и соответствующая связь один-ко-многим?

Или:
Если пользователь редактирует не ячейку, а документ? Тогда в таблице documents будет создана новая запись, но как же все связи?
Относительно первого. Во первых такого в условии не было, во вторых у cell не должно быть дочерних сущностей. Поэтому зачем их плодить?
Проще в таблицу Cells добавить поле style_id, которое будет референсом в таблицу Styles, где будут прописаны нормальные стили.
Если уже очень хочется дочернюю таблицу с верняковой привязкой, то либо еще одно поле либо координаты sheet,row,col использовать.
Хотя повторюсь. Дочерних полей быть уже не должно, но если будут, то выход можно найти такой же простой.

По второму не сильно понятно что вы имеете ввиду. Вроде бы как версионность таблицы documents была не нужна. Хотя ее можно сделать по тому же принципу. Просто нужно будет либо дублировать id (что не тру) либо добавить вторую id. Одна техническая для БД, а вторая уникальная для идентификации документа и создания постоянных связей.

Но вопрос таблицы стилей актуален.
Кроме того, что некоторым другим сущностям, кроме ячеек может понадобиться версионность, так и некоторым данным такая версионность будет не нужна — например, id, sheet_id, row_number, col_number, creator_id (т.е. вся таблица cells).

Не забывайте, что пример из поста сильно упрощён, чтобы объяснить алгоритм. В реальности — обязательно будут связи с другими таблицами. Всё тоже самое что и в обычной структуре любого проекта, только с версионностью некоторых данных.
Относительно первого, то на это можно закрыть глаза.

Если есть другие существенные связи, то да, такой простой метод не подойдет так как подходит только для самых дочерних значений.
Хотя я описал выше как можно с _минимальным_ затратов времени на код сделать версионность.

Как-то так.
Во первых такого в условии не было, во вторых у cell не должно быть дочерних сущностей

Это не универсальный подход. Появиться всегда может что угодно и когда угодно. Нельзя недооценивать воображение заказчика.

По второму не сильно понятно что вы имеете ввиду.

Это та же самая задача с дочерними элементами. А что если, по ТЗ надо реализовать версионность для всей системы: для документов, листов, колонок, строк и ячеек. Вы предлагаете для разных таблиц делать версионность по разному? По вашему: в некоторых таблицах id дублируются и создаётся технический столбец `uniq_id` (в `documents`), а в других таблицах id уникальный (в `cells`). Т.е. использованы разные паттерны для решения одной и той же задач.

Для реализации версионности БД должен быть какой-то паттерн, не зависящий от всего остального ни от ТЗ ни от воображения заказчика.

Кстати, по поводу дублирования id в таблице documents: придётся ещё дублировать `creator_id`, `date_create`, и другие. А дублирование инфы не очень хорошо и противоречит принципам нормализации. Что бы избежать дублирования, придётся создать таблицу с не изменяемыми данными — именно это и предложено в топике.
Ничто не универсальный подход. Как и нет волшебной таблетки.

Я не предлагаю разную. Я предлагаю самую простую и быструю в разработки с моей точки зрения. Всего лишь. Не вижу смысла придумывать абстрактные проблемы.

Если бы надо было ВСЕ данные версионить лично я бы сделал немного по другому, но тоже очень просто и топорно чтобы почти ничего править не пришлось.

Относительно дублирующихся «creator_id» считаю проблема надуманна. Ничто в нашем мире не идеально. ЖД сегодня дешевые а вот скорость разработки, нервов и поддержки дорогая.
Ну и самое главное то. Задача в реальности простая. Версионность документа. Вот ее мы быстро и лихо решили. Остальное не так существенно.
И всё же небольшое ТЗ было изложено в посте
Требования к структуре
Обязательные
  • Возможность сохранения предыдущих значений ячеек
  • Возможность просмотра предыдущих ревизий онлайн документа
  • Возможность отката на предыдущую версию
  • Некоторые параметры могут не иметь разных версий (id, sheet_id)
  • Внедрение в используемую ORM

Желательные
  • Минимально возможные трудозатраты на перенос данных в новую структуру, выборку, изменение и добавление данных
  • Экономия дискового пространства
  • Подсветка изменившихся ячеек при просмотре старых ревизий

>>Когда на проекте ближе к концу разработки становится понятно, что без версионности данных не обойтись
Отвинтите руки проектировщику.

>>И всё же небольшое ТЗ было изложено в посте
Обязательные:
ТЗ как-то не логино выглядит, зачем хранить предыдущее значение когда можно хранить diff и по необходимости на лету сравнивать?

Просмотр предыдущих и откат на любую версию — так-же без лишних трудностей делается через diff

Желательные:
Минимально возможные трудозатраты — не соблюдены)
Экономии дискового пространства в Вашем решении — пшик.

За старания в создании «Лисапеда» — плюс.
За реализацию — минус.

>>Объясните, пожалуйста. Такая структура придумывалась не один день, и вариант
Пользуйтесь Q&A — частенько подскажут куда рыть.
Не уверен, что вы поняли задачу. Дублировать ячейки всего документа при создании каждой ривизии никто не собирается.
Sign up to leave a comment.

Articles