2009-10-09 2 views
24

Существуют ли какие-либо методы/предложения для обеспечения уникальных ограничений? Да, мы можем создать уникальный ключ, но мы не можем изменить ключ и ключи, а также этот подход не подходит для сложной проверки (отдельный уникальный логин, отдельный уникальный адрес электронной почты и т. Д.)Уникальные ограничения в 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 счета с авторизацией: "МЭРИ"

ответ

13

Это один из менее забавных бит 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" } 

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

  1. Подготовить документ пользователя, сгенерировать ключ для него, если не указано иное обходимо
  2. Сохранение «указатель» документ для каждого изменилось уникальное поле
  3. Если сохранение какой-либо из тех, кто не помогает, остановить и исправить ошибки
  4. Сохранить основной пользователь документ

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

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

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

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

+3

Да, это решение, но лично я предпочитаю блокировку с Редисом. Такие решения выглядят странно, когда разработчики приходят из мира РСУБД. CouchDB отличается от РСУБД, но должны ли мы отказаться от всех существующих практик? Нет ACID и замков, но без всего этого невозможно расслабиться и просто написать приложения. CouchDB не хватает ключевых функций, и это печально. – Sam

+0

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

+2

Обратите внимание, что если вы планируете держать вещи с несколькими мастерами, пункт 2.будет ** не ** сбой, если ключ уже не существует на * текущем * узле. Ключи уникальны только на узле, в котором они существуют. Проверьте ответ Раффи М. на простое кластерное решение этой проблемы. – user1973386

9

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

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

+0

Это злой способ сделать вещи – Prospero

+1

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

19

Ядро Ответ

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

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

  2. Сразу после PUT/POST новый документ в базу данных, захватить возвращенное id и rev и GET/yourdb/_design/YourApp/_view/ViewName? Группа = истинный & ключ =»значение -of-your-unique-field-from-step-1 ".

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

  4. Relax.


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

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

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

И только _countкак уменьшить функцию. Если вы испустите , как и выше, _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 

Short Обсуждение (если вы заботитесь)

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

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

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

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

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

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

+5

Относительно этого: _ "но вы не хотите, чтобы он был идентификатором документа (по какой-либо причине)" _ Я хотел бы указать, что использование адреса электронной почты и/или имени пользователя в качестве _id нежелательно. Если пользователь изменяет свой адрес электронной почты или имя пользователя, он нарушит любые внешние ссылки на этот идентификатор документа (и, вероятно, будет куча). –

+2

Правда. Думаю, это подпадает под «по какой-то причине». :) –

+2

Ну, это хорошая причина :) BTW - Я очень ценю вашу короткую дискуссию. Очень хорошая информация. –

Смежные вопросы