Документ о дизайне обновления CouchDB

У меня есть приложение nodejs, в котором я подключаюсь к своему CouchDB, используя nano со следующим скриптом:

const { connectionString } = require('../config');

const nano = require('nano')(connectionString);

// creates database or fails silent if exists
nano.db.create('foo');

module.exports = {
  foo: nano.db.use('foo')
}

Этот сценарий запускается при каждом запуске сервера, поэтому он пытается создать базу данных «foo» каждый раз, когда сервер (повторно) запускается, и просто молча терпит неудачу, если база данных уже существует.

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

Сделав еще один шаг в этом подходе, я также попытался поддерживать свою проектную документацию на уровне приложения.

... 
nano.db.create('foo');

const foo = nano.db.use('foo');

const design = {
  _id: "_design/foo",
  views: {
    by_name: {
      map: function(doc) {
        emit(doc.name, null);
      }
    }
  }
}

foo.insert(design, (err) => {
  if(err)
    console.log('design insert failed');
})

module.exports = {
  foo
}

Очевидно, что это вставит проектный документ только в том случае, если он не существует. Но что, если я обновил свой проектный документ и хочу его обновить?

Я старался:

foo.get("_design/foo", (err, doc) => {
  if(err)
    return foo.insert(design);

  design._rev = doc._rev
  foo.insert(design);
})

Теперь проблема заключается в том, что проектный документ обновляется каждый раз при перезапуске сервера (например, он получает новый _rev при каждом перезапуске).

Теперь ... мой вопрос (ы) :)

1: Это плохой подход для начальной загрузки моего CouchDB с базами данных и проектами? Должен ли я рассматривать некоторые шаги миграции как часть моего процесса развертывания?

2: Является ли проблемой то, что мой проектный документ получает много _rev, в основном при каждом развертывании и перезапуске сервера? Даже если сам документ не изменился? И если да, то есть ли способ обновить документ, только если он изменился? (Я думал вручную установить _rev какое-то значение в моем приложении, но очень не уверен, что это будет хорошей идеей).


person Sander Garretsen    schedule 14.07.2016    source источник


Ответы (3)


  1. Ваш подход кажется вполне разумным. Если проверки происходят только при перезагрузке, это даже не будет проблемой производительности.
  2. Слишком много _revs может стать проблемой. История _revs хранится как _revs_info вместе с самим документом (подробности см. в документации CouchDB). В зависимости от вашей настройки создание ненужных ревизий может быть плохим решением.

У нас была аналогичная проблема с некоторыми сценариями на стороне сервера, которые требовали определенных представлений. Наше решение состояло в том, чтобы вычислить хэш старого и нового проектного документа и сравнить их. Для этого задания можно использовать любую функцию хеширования, например sha1 или md5. Просто не забудьте удалить _rev из старого документа перед его хешированием, иначе вы каждый раз будете получать разные значения хэша.

person Bernhard Gschwantner    schedule 15.07.2016
comment
Спасибо, Бернхард. Ваше объяснение было действительно полезным. Я последовал вашему совету, просто сравнив документы по дизайну, но столкнулся с некоторыми проблемами при сравнении их с md5... Я опубликовал свое собственное решение, основанное на ваших предложениях ниже. Не могли бы вы оставить свой отзыв? - person Sander Garretsen; 16.07.2016

Я попробовал сравнение md5, как предложил @Bernhard Gschwantner. Но я столкнулся с некоторыми трудностями, потому что в моем случае я хотел бы написать функции map/reduce в проектных документах на чистом javascript в своем коде.

const design = {
  _id: "_design/foo",
  views: {
    by_name: {
      map: function(doc) {
        emit(doc.name, null);
      }
    }
  }
}

при получении проектного документа от CouchDb возвращаются функции map/reduce, преобразованные в строки:

  ...
  "by_name": {
    "map": "function (doc) {\n        emit(doc.name, null);\n      }"
  },
  ...

Очевидно, что сравнение md5 здесь не работает.

Я пришел к очень простому решению, просто указав номер версии в дизайн-документе:

const design = {
  _id: "_design/foo",
  version: 1,
  views: {
    by_name: {
      map: function(doc) {
        emit(doc.name, null);
      }
    }
  }
}

Когда я обновляю дизайн-документ, я просто увеличиваю номер версии и сравниваю его с номером версии в базе данных:

const fooDesign = {...}
foo.get('_design/foo', (err, design) => {
  if(err)
    return foo.insert(fooDesign);

  console.log('comparing foo design version', design.version, fooDesign.version);
  if(design.version !== fooDisign.version) {
    fooDesign._rev = design._rev;
    foo.insert(fooDesign, (err) => {
      if(err)
        return console.log('error updating foo design', err);
      console.log('foo design updated to version', fooDesign.version)
    });
  }
});
person Sander Garretsen    schedule 16.07.2016
comment
Это также здорово и намного проще. Однако, если вы хотите использовать хеширование md5, вы можете преобразовать функции javascript в строки, преобразовав функции в строки, перебирая свойства объекта и вызывая prop.toString(). Но это больше работы, поэтому поле версии — гораздо более простое и понятное решение. - person Bernhard Gschwantner; 28.07.2016

Возвращаясь к вашему вопросу снова: в недавнем проекте я использовал отличный модуль couchdb-push Йоханнеса Шмидта. . Вы получаете условные обновления бесплатно, а также многие другое преимущества, унаследованные от его зависимости couchdb-compile.

Эта библиотека оказалась для меня скрытой жемчужиной. Настоятельно рекомендуется!

person Bernhard Gschwantner    schedule 05.01.2017