Подтвердить что ты не робот

Уникальные ограничения в couchdb

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

Например, учетная запись должна иметь уникальный логин и адрес электронной почты. Получение ключа из этих полей приведет к несоответствие:

key1: "[email protected]", { email: "[email protected]", login: "john"}
key2: "[email protected]", { email: "[email protected]", login: "mary"}

Хорошо выглядит, но:

key1: "[email protected]", { email: "[email protected]", login: "mary"}
key2: "[email protected]", { email: "[email protected]", login: "mary"}

Упс, теперь у нас есть 2 аккаунта с логином: "mary"

4b9b3361

Ответ 1

Это один из менее забавных бит CouchDB. Лучший способ, который я нашел для обработки уникальных полей, которые могут измениться (например, в вашем примере пользователя), - создать "указатель" с уникальными значениями в качестве компонента ключа, а затем использовать их, чтобы вы могли требовать уникальные значения. Важной частью этого является наличие предсказуемого ключа для первичного документа, а затем сохранение уникальных документов с претензиями на местах до сохранения первичного (и разрешения конфликтов ключа предотвратить сохранение основного документа).

Учитывая пользователя с уникальным именем пользователя и уникальным электронным письмом, ваши первичные документы могут выглядеть так:

user-1234: { username: "kurt", email: "[email protected]" }
user-9876: { username: "petunia", email: "[email protected]" }

Уникальные указатели поля выглядят примерно так:

user-username-kurt: { primary_doc: "user-1234" }
[email protected]: { primary_doc: "user-1234" }
user-username-petunia: { primary_doc: "user-9876" }
[email protected]: { primary_doc: "user-9876" }

Создание или обновление пользователя выполняет следующие действия:

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

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

Шаг 3 был бы хорошим местом, чтобы люди могли принимать старые заявленные ценности. Например, если один пользователь "выпустил" имя пользователя kurt, я могу просто обновить этот конкретный документ, чтобы указать на мой новый ключ после проверки того, что он больше не используется. Альтернативой является устранение заявленных уникальных значений при их изменении. Я не уверен, что на самом деле будет меньше работать. Оставив устаревшие заявленные ценности, мне больше всего нравится.

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

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

Ответ 2

Основной ответ

Структурируйте свои POST/PUTs для документа, у которого есть поле, которое вы хотите сохранить уникальным, следующим образом:

  • Создайте view. В функции карты используйте поле, которое вы хотите использовать уникальным, в качестве клавиши . Значение может быть ничем. Используйте функцию уменьшения, чтобы получить счет для каждого из ваших ключей. Лучший способ (для производительности) - использовать встроенную функцию _count.

  • Сразу после того, как вы PUT/POST добавите новый документ в базу данных, возьмите возвращенные id и rev и GET/yourdb/_design/? YourApp/_view/ViewName группа = истина &. ключ = "стоимость в вашем уникальном поле-с шагом-1"

  • Если результат последнего GET дает вам значение счета, отличное от 1, то вы просто вставили дубликат. Сразу DELETE/yourdb/id-from-step-2? Rev = rev-from-step-2.

  • Relax.


Грубый пример

Предположим, что вы храните учетные записи пользователей, и вы хотите убедиться, что адрес электронной почты уникален, но вы не хотите, чтобы он был идентификатором документа (по какой-либо причине). Создайте представление, чтобы быстро проверить уникальность адреса электронной почты, как описано выше. Позволяет называть его электронными письмами. У него будет функция карты, возможно, подобная этой...

function(doc) {  
  if(doc.type === 'account') {
    emit(doc.email, 1);
  }
}

И просто _count в качестве функции уменьшения. Если вы выбрали 1, как указано выше, _sum также будет работать. Наличие и проверка поля type на вашем документе, как показано выше, является просто конвенцией, но иногда я считаю ее полезной. Если все, что вы храните, это учетные записи пользователей, вам, вероятно, это не понадобится.

Теперь скажем, что мы вставляем такой документ...

POST /mydb/
{
  "name": "Joe",
  "email": "[email protected]"
}

И CouchDB ответит чем-то вроде...

{
  ok: true,
  id: 7c5c249481f311e3ad9ae13f952f2d55,
  rev: 1-442a0ec9af691a6b1690379996ccaba6
}

Проверьте, есть ли у нас в базе данных более одного [email protected]

GET /mydb/_design/myapp/_view/emails/?group=true&key="[email protected]"

И CouchDB ответит чем-то вроде...

{
  rows: [
    {
      key: "[email protected]",
      value: 1
    }
  ]
}

Если value является чем-то другим, кроме 1 в этом ответе, вы, вероятно, просто вставили дублирующее письмо. Таким образом, удалите документ и верните ошибку в строке адреса электронной почты, которая должна быть уникальной, аналогичной типичной реакции SQL-базы данных или тому, что вы хотите.

Удаление будет выглядеть примерно так:

DELETE /mydb/7c5c249481f311e3ad9ae13f952f2d55?rev=1-442a0ec9af691a6b1690379996ccaba6

Краткая дискуссия (если вам интересно)

Если вы исходите из старого старого фона * SQL, это будет казаться неправильным и странным. Перед тем, как вы переверните, рассмотрите эти моменты. Наличие ограничения на поле, такое как уникальность или что-то еще, подразумевает схему. CouchDB является схематичным. Исходя из * SQL означает, что вам придется изменить то, как вы думаете. ACID и Locks - не единственный способ. CouchDB обладает большой гибкостью и мощностью. Как вы используете это, чтобы получить то, что вы хотите, будет зависеть от деталей вашего прецедента и насколько хорошо вы можете избежать ограничений традиционного мышления реляционной базы данных.

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

В приведенном выше примере может возникнуть соблазн проверить уникальность адреса электронной почты перед тем, как POST документ. Быть осторожен!! Если происходит несколько событий, возможно, что другой документ с тем же адресом электронной почты может быть вставлен в базу данных в тот момент, когда вы проверите, существует ли это письмо, но прежде чем вы выполните POST! Это оставит вас с дублирующимся письмом.

Нет ничего плохого в том, что вы также выполняете проверку адреса электронной почты перед POST. Например, это хорошая идея, если ваш пользователь заполняет форму, и вы можете изменить значение поля электронной почты в базе данных. Вы можете предварительно предупредить пользователя о существовании адреса электронной почты или о недопустимости отправки и т.д. Однако во всех случаях вам следует также всегда проверять уникальность после POST документа. Затем реагируйте при необходимости. С точки зрения стороны пользовательского интерфейса вышеописанные этапы выглядят иначе, чем результат, полученный в результате традиционной базы данных SQL *, налагающей ограничения на уникальность.

Неужели что-то пошло не так? Да. Учти это. Предположим, [email protected] еще не существует в базе данных. Два документа поступают почти одновременно, чтобы быть сохраненными в базе данных. Doc A POSTed, то Doc B является POSTed, но прежде чем мы сможем проверить единственность для Doc A POST. Итак, теперь, когда мы выполняем проверку уникальности для Doc A POST, мы видим, что [email protected] находится в базе данных дважды. Итак, мы удалим его и сообщим о проблеме. Но скажем, прежде чем мы удалим Doc A, проверка уникальности для Doc B также происходит, получая то же значение счета для [email protected] Теперь оба POSTs будут отклонены, хотя [email protected] изначально не был в базе данных! Другими словами, если два приложения с подходящим значением приходят в ваше приложение почти одновременно, возможно, что они могут видеть друг друга POSTs и ошибочно заключают, что значение, которое они переносят, уже находится в базе данных! Мы не можем этого предотвратить, потому что в CouchDB нет традиционного стиля RDB. Но в обмен на эту небольшую цену мы получаем репликацию мастер-мастера и тонну других интересных вещей. Я возьму это! И если это огромная проблема для вашей реализации, вы можете попытаться устранить ее, реализовав какой-то механизм повтора и т.д.

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

Ответ 3

Это зависит. Рассмотрим реплицируемый случай с несколькими мастерами, там могут быть конфликтующие записи, добавленные там, согласованные внутри каждого мастера, но не последовательные после их репликации. Возможно, вы используете только один сервер couchdb, но в целом они проектируют его, предполагая случай с несколькими файлами, и не вставляют никаких функций, которые будут работать только на одном нереплицированном сервере.

Если вы только заботитесь о случае с одним сервером, возможно, вы можете перестроить свои couchjs с поддержкой сети и выполнить HTTP-запрос в вашей функции validate_doc_update(), которая будет выполнять запрос к БД, чтобы узнать, уже ли уже этот адрес электронной почты и если это произойдет, обновите его. Подробнее о механизме валидации читайте здесь. Я не рекомендую это делать, вместо этого я бы вставлял всю уникальность в поле id (либо напрямую, либо через хэширование), и просто имел дело с перемещением документа, если пользователь изменил все, что это сделало.

Ответ 4

CouchDB имеет "ленту изменений" http://docs.couchdb.org/en/2.3.1/api/database/changes.html#changes

Действие обновления аналогично новому действию, отличающееся только свойством _rev и объектом 'new'.

Функции фильтра работают как списки или функции шоу, но отличаются ориентацией на "действия в очереди изменений" http://docs.couchdb.org/en/2.3.1/ddocs/ddocs.html#filterfun

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

https://docs.couchdb.org/en/master/ddocs/index.html