Вам нужна вторая или вспомогательная база данных couchdb, назовем ее «complete_dependencies». Его документами будут рассчитанные полные зависимости каждого узла. Документы будут иметь вид
{Имя: 'D', CompleteDepends: ['C', 'A']}
Также этот БД будет иметь представление «подразумевает», показывающее для каждого элемента все элементы, которые зависят от него. Для предыдущего элемента он выдаст:
[{Имя: 'C', подразумевает: 'D'}, {Имя: 'A', подразумевает: 'D'}]
Вы будете использовать API уведомлений об изменениях для актуализации этой базы данных. Он обработает каждый документ, который был изменен в исходной БД, и рассчитает его полные зависимости. Обратите внимание, что эта обработка не является представлением. Является ли программа сделанной на ваш язык, слушая изменения API и обрабатывая каждый документ. Для каждого документа будет сделано следующее:
1) вычислить его полные зависимости, используя нашу базу данных complete_dependencies.
2) посмотрите в представлении «подразумевает», какие документы зависят от измененного документа, и пересчитайте их. Вот одна вещь, чтобы позаботиться. Чтобы использовать complete_dependencies, они должны быть правильно рассчитаны, поэтому перед использованием записи complete_dependencies документа вы должны быть уверены, что документ отсутствует в списке «подразумеваемых» или что он рассчитан.
Важным моментом является то, что ни одна из этих функций не является рекурсивной.
Из этого следует:
Первый документ: {Имя: 'A', Зависит от: []}. Поскольку нет зависимостей (1), он ничего не будет делать, так как представление подразумевает, что оно пустое (2) также ничего не сделает.
Второй {Имя: 'B', Зависит: ['A']}. Он будет смотреть на 'A' в db complete_dependencies, чтобы увидеть, зависит ли это от чего-то. Поскольку нет никаких зависимостей от 'A', он будет писать только {Имя: 'B', CompleteDepends: ['A']}. (2) не найдет элемент, который зависит от 'B', поэтому он ничего не будет делать.
То же самое для {Имя: 'C', Зависит: ['A']} -> {Имя: 'C', CompleteDepends: ['A']}.
Когда он читает следующий документ {Имя: 'D', Зависит: ['C']}, он обнаруживает, что 'C' зависит от 'A', поэтому он будет писать
{Имя: 'D', CompleteDepends: ['C', 'A']}
Важно: он не должен проверять рекурсивные зависимости 'A', поскольку он ищет в C "CompleteDependencies", и все зависимости A должны находиться в полных зависимостях C.
Когда он обрабатывает следующий документ {Имя: 'E', Зависит: ['D', 'B']}, он будет искать зависимости 'D' и 'B' Complete и обнаружит, что D complete зависит от C и A и B только в A. {Имя: 'E', CompleteDepends: ['D', 'B', 'C', 'A']}.
Теперь давайте предположим, что он получает новую версию документа 'A': {Имя: 'A', Зависит: ['Z']}. Теперь это должно
1) вычислить полные зависимости 'A' -> {Имя: 'A', CompleteDepends: ['Z']}
2) посмотреть, какие элементы зависят от «A», и пересчитать его полные документы зависимостей. Он пересчитает полные зависимости B, C, D, E.
Давайте рассмотрим другой случай. Предположим, мы получили новую версию документа C: {Имя: 'C', Зависит от: ['Z']}.
(1) будет сгенерирован документ {Имя: 'C', CompleteDepends: ['Z']}.
Поиск элементов, зависящих от C, найдет D и E. Теперь, если он сначала вычислит E, он обнаружит, что он зависит от D, и, поскольку D имеет значение для C, он должен сначала пересчитать D. Это единственное эта функция (2) должна заботиться.
Я думаю, что для этого функция «Пересчитать» должна иметь список «грязных» элементов, которые включают в себя весь список «подразумеваемых» и исключаются при вычислении одного из них.