2009-10-09 42 views
24

Có bất kỳ kỹ thuật/đề xuất nào để thực thi các ràng buộc duy nhất không? Có, chúng tôi có thể tạo khóa duy nhất, nhưng chúng tôi không thể thay đổi khóa và khóa và cách tiếp cận này không phù hợp để xác thực phức tạp (đăng nhập riêng biệt riêng biệt, email riêng biệt, v.v.)Các ràng buộc duy nhất trong couchdb

Ví dụ: Tài khoản nên có đăng nhập và email duy nhất. Phát sinh một chìa khóa từ lĩnh vực này sẽ dẫn đến mâu thuẫn:

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

Nhìn tốt, nhưng:

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

Rất tiếc, bây giờ chúng tôi có 2 tài khoản với đăng nhập: "mary"

Trả lời

13

Đây là một trong những bit ít thú vị của CouchDB. Cách tốt nhất mà tôi đã tìm thấy để xử lý các trường duy nhất có thể thay đổi (như trong ví dụ người dùng) là tạo tài liệu "con trỏ" với các giá trị duy nhất làm thành phần của khóa và sau đó sử dụng chúng để cho phép bạn xác nhận các giá trị duy nhất. Phần quan trọng của việc này là có khóa dự đoán cho tài liệu chính, sau đó lưu các tài liệu xác nhận trường duy nhất trước khi lưu tài liệu chính (và cho phép xung đột chính ngăn tài liệu chính lưu).

Cho một người dùng với một tên người dùng độc đáo và email độc đáo, tài liệu chính của bạn có thể trông như thế này:

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

Các con trỏ lĩnh vực độc đáo sẽ giống như thế này:

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" } 

Tạo hoặc cập nhật người dùng sẽ thực hiện các bước sau:

  1. Chuẩn bị tài liệu người dùng của bạn, tạo khóa cho nó nếu ne cessary
  2. Lưu một "con trỏ" tài liệu cho mỗi thay đổi lĩnh vực độc đáo
  3. Nếu tiết kiệm bất kỳ của những thất bại, ngăn chặn và khắc phục lỗi
  4. Giữ hồ sơ chính tài liệu

Bước 3 sẽ mất một suy nghĩ. Ví dụ: bạn sẽ không muốn thử xác nhận các giá trị duy nhất cho các trường chưa thay đổi. Bạn có thể, nhưng sau đó bạn sẽ phải đặt một số logic bổ sung để xử lý một trường hợp bạn đang xác nhận một giá trị cho người dùng đã sở hữu giá trị đó.

Bước 3 sẽ là một nơi tốt để cho phép mọi người lấy các giá trị đã xác nhận quyền sở hữu cũ. Nếu một người dùng đã "giải phóng" tên người dùng kurt, ví dụ, tôi chỉ có thể cập nhật tài liệu cụ thể đó để trỏ đến khóa mới của tôi sau khi xác minh rằng nó không còn được sử dụng nữa. Cách khác là xóa các giá trị duy nhất được xác nhận khi chúng thay đổi. Tôi không chắc chắn sẽ ít hoạt động hơn. Để lại các giá trị đã được tuyên bố cũ trong ý nghĩa nhất đối với tôi.

Điều thú vị về giải pháp này là bạn không cần phải sử dụng các tài liệu con trỏ đó cho bất kỳ thứ gì khi chúng được tạo. Bạn có thể tạo các chế độ xem như bình thường trên tài liệu người dùng của mình và sử dụng chúng để truy vấn bằng email hoặc tên người dùng.

Cài đặt này cũng cho phép bạn thực hiện các mối quan hệ mà không phải lo lắng về việc khóa tầng người dùng. Tôi không biết về bạn, nhưng tài liệu người dùng của tôi được tham chiếu bởi mọi tài liệu khác trong hệ thống. Thay đổi một khóa người dùng sẽ là một nỗi đau lớn trong ass.

+3

Vâng, đây là một giải pháp, nhưng cá nhân tôi thích khóa với Redis. Các giải pháp như vậy trông kỳ lạ khi các nhà phát triển đến từ thế giới RDBMS. CouchDB là khác nhau sau đó RDBMS, nhưng chúng ta nên từ chối tất cả các thực hành hiện có? Không có ACID và ổ khóa, nhưng không có tất cả những thứ này thì không thể thư giãn và chỉ viết các ứng dụng. CouchDB thiếu các tính năng chính và điều này thật đáng buồn. – Sam

+0

Việc băm giá trị duy nhất trước khi thêm nó vào ID tài liệu giúp việc thêm giá trị có thể có các ký tự đặc biệt thuận tiện hơn. –

+2

Lưu ý rằng nếu bạn có kế hoạch giữ cho mọi thứ thân thiện với nhiều người, điểm 2.sẽ ** không ** thất bại trừ khi khóa đã tồn tại trên nút * current *. Các khóa chỉ là duy nhất trên nút mà chúng tồn tại. Hãy xem câu trả lời của Raffi M. cho một giải pháp thân thiện với cụm đơn giản cho vấn đề này. – user1973386

9

Nó phụ thuộc. Hãy xem xét trường hợp nhân bản đa chủ, có thể có các mục nhập xung đột được thêm vào có nhất quán trong mỗi chủ, nhưng không nhất quán khi chúng lặp lại. Bạn chỉ có thể đang sử dụng một máy chủ couchdb, nhưng nói chung họ thiết kế nó giả sử một trường hợp đa chủ, và không đưa vào bất kỳ tính năng nào sẽ chỉ hoạt động chính xác trong máy chủ đơn lẻ chưa được giải thích.

Nếu bạn chỉ quan tâm đến trường hợp máy chủ duy nhất, bạn có thể xây dựng lại ghế sofa của mình với hỗ trợ mạng và thực hiện truy vấn http trong chức năng validate_doc_update() của bạn sẽ thực hiện một truy vấn đối với DB để xem địa chỉ email đã được sử dụng chưa và không cập nhật nếu có. Kiểm tra here để biết thêm chi tiết về cơ chế xác thực. Tôi không khuyên bạn làm điều đó, thay vào đó tôi sẽ nhúng tất cả tính duy nhất vào trường id (trực tiếp hoặc qua băm) và chỉ xử lý việc di chuyển tài liệu nếu người dùng thay đổi bất kỳ thứ gì có hiệu lực.

+0

đây là cách thông minh độc ác để làm mọi thứ – Prospero

+1

Đây là cách tiếp cận tốt trừ khi giá trị trường duy nhất (ví dụ địa chỉ email) có thể thay đổi. Sau đó, bạn sẽ cần phải rehash cho một id mới. Vì bạn không thể thay đổi id của một bản ghi hiện có, bạn sẽ phải tạo một bản ghi mới và xóa bản ghi cũ, mà sau đó làm cho vấn đề này tròn. –

19

Lõi trả lời

Cấu trúc bài viết của mình/đặt cho các tài liệu trong đó có lĩnh vực bạn muốn giữ độc đáo như sau:

  1. Tạo một view. Trong chức năng bản đồ sử dụng trường bạn muốn thực thi duy nhất làm phím . Giá trị có thể là không có gì. Sử dụng hàm giảm để nhận số đếm cho mỗi khóa của bạn. Cách tốt nhất (để thực hiện) là sử dụng chức năng giảm chức năng được xây dựng trong _count.

  2. Ngay sau khi bạn PUT/POST một tài liệu mới vào cơ sở dữ liệu, lấy lại idrev nhóm và GET/yourdb/_design/yourapp/_view/viewname? = True & key =" giá trị -of-your-unique-field-from-step-1 ".

  3. Nếu kết quả cuối cùng GET mang đến cho bạn một giá trị đếm khác hơn , sau đó bạn chỉ cần chèn một bản sao. Ngay lập tức DELETE/yourdb/id-from-step-2? Rev = rev-from-step-2.

  4. Thư giãn.


Rough Ví dụ

phép nói rằng bạn đang lưu trữ các tài khoản người dùng và bạn muốn chắc chắn địa chỉ email là duy nhất, nhưng bạn không muốn làm cho nó id tài liệu (vì lý do gì). Tạo chế độ xem để nhanh chóng kiểm tra tính duy nhất trên địa chỉ email như được mô tả ở trên. Cho phép gọi nó là email. Nó có chức năng bản đồ có thể tương tự như vậy ...

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

Và chỉ _count như giảm chức năng. Nếu bạn phát ra một số điện thoại như trên _sum cũng sẽ hoạt động. Có và kiểm tra một lĩnh vực type trên tài liệu của bạn như được hiển thị ở trên chỉ là một quy ước nhưng đôi khi tôi thấy nó hữu ích. Nếu tất cả các bạn đang lưu trữ là tài khoản người dùng, có thể bạn không cần điều đó.

Bây giờ cho phép nói rằng chúng ta đang chèn một tài liệu như vậy ...

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

Và CouchDB sẽ trả lời với một cái gì đó giống như ...

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

Kiểm tra để xem nếu bây giờ chúng tôi có hơn một [email protected] trong cơ sở dữ liệu ...

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

Và CouchDB sẽ trả lời bằng cái gì đó như ...

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

Nếu value là bất cứ điều gì khác hơn là 1 trả lời rằng, có lẽ bạn muốn chèn một email trùng lặp. Vì vậy, xóa tài liệu và trả về lỗi dọc theo các dòng Địa chỉ email phải là duy nhất, tương tự như phản hồi cơ sở dữ liệu SQL điển hình hoặc bất kỳ điều gì bạn muốn.

Các delete sẽ đi một cái gì đó như thế này ...

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

ngắn Thảo luận (nếu bạn quan tâm)

Nếu bạn đang đến từ một tuổi * nền SQL tốt, điều này sẽ có vẻ sai và lạ. Trước khi bạn lật ra xem xét những điểm này. Có ràng buộc trên một trường, chẳng hạn như tính duy nhất hoặc bất kỳ điều gì khác, ngụ ý một lược đồ . CouchDB là schemaless. Đến từ * SQL có nghĩa là bạn sẽ phải thay đổi cách bạn nghĩ. ACID và Khóa không phải là cách duy nhất. CouchDB đi kèm với rất nhiều tính linh hoạt và sức mạnh. Làm thế nào bạn sử dụng để có được những gì bạn muốn là sẽ phụ thuộc vào các chi tiết của trường hợp sử dụng của bạn và làm thế nào bạn có thể thoát khỏi sự hạn chế của tư duy cơ sở dữ liệu quan hệ truyền thống.

Lý do tại sao đôi khi tôi thực hiện tính duy nhất theo cách này là bởi vì nó cảm thấy rất Couchy với tôi. Nếu bạn nghĩ về cách CouchDB làm việc với xử lý xung đột cập nhật, vv, cách tiếp cận này theo cùng một luồng. Chúng tôi lưu trữ tài liệu mà chúng tôi đưa ra, sau đó chúng tôi kiểm tra xem trường của chúng tôi là duy nhất hay không. Nếu không, hãy lấy tài liệu mà chúng ta vừa chèn vào bằng tay cầm thuận tiện mà chúng ta vẫn có trên đó, và làm một cái gì đó để giải quyết nhu cầu về tính duy nhất, chẳng hạn như xóa nó đi.

Bạn có thể bị cám dỗ trong ví dụ trên để kiểm tra tính duy nhất của địa chỉ email trước bạn POST tài liệu. Hãy cẩn thận!!Nếu có nhiều điều xảy ra, có thể một tài liệu khác có cùng địa chỉ email có thể được chèn vào cơ sở dữ liệu ngay lập tức sau bạn kiểm tra xem email có tồn tại không, nhưng trước bạn làm POST! Điều đó sẽ để lại cho bạn một email trùng lặp.

Không có gì sai khi đồng thời kiểm tra địa chỉ email trước khi bạn POST. Ví dụ: đây là ý tưởng hay nếu người dùng của bạn điền vào biểu mẫu và bạn có thể ajax giá trị trường email ra khỏi cơ sở dữ liệu. Bạn có thể cảnh báo trước cho người dùng rằng địa chỉ email tồn tại hoặc ngăn gửi, v.v. Tuy nhiên, trong mọi trường hợp, bạn nên cũng luôn luôn kiểm tra tính duy nhất sau bạn POST tài liệu. Sau đó phản ứng khi cần thiết. Từ quan điểm của phía giao diện người dùng, các bước ở trên sẽ trông không khác với kết quả thu được từ một cơ sở dữ liệu truyền thống * SQL puking trên một ràng buộc duy nhất.

Có thể xảy ra sự cố không? Vâng. Xem xét điều này. Giả sử [email protected] không tồn tại trong cơ sở dữ liệu. Hai tài liệu đến gần như cùng một lúc để được lưu vào cơ sở dữ liệu. Đốc APOSTed, sau đó Doc BPOSTed nhưng trước khi chúng tôi có thể kiểm tra sự độc đáo cho Đốc APOST. Vì vậy, bây giờ khi chúng tôi thực hiện kiểm tra tính duy nhất cho Doc APOST, chúng tôi thấy rằng [email protected] có trong cơ sở dữ liệu hai lần. Vì vậy, chúng tôi sẽ xóa nó và báo cáo lại với vấn đề. Nhưng giả sử trước khi chúng tôi có thể xóa Doc A, kiểm tra tính duy nhất cho Doc B cũng xảy ra, nhận cùng một giá trị tính cho [email protected]. Bây giờ cả hai POSTs sẽ bị từ chối, mặc dù [email protected] ban đầu không có trong cơ sở dữ liệu! Nói cách khác, nếu hai tài liệu có giá trị khớp vào ứng dụng của bạn gần như cùng một lúc, thì có thể là mà chúng có thể nhìn thấy lẫn nhau POSTs và nhầm lẫn kết luận rằng giá trị mà chúng mang theo đã có trong cơ sở dữ liệu! Chúng tôi thực sự không thể ngăn chặn điều này vì không có kiểu RDB kiểu truyền thống nào đang khóa trong CouchDB. Nhưng để đổi lấy mức giá nhỏ đó, chúng ta có được sự sao chép chủ nhân và hàng tấn những thứ tuyệt vời khác. Tôi sẽ lấy nó! Và nếu đây là một vấn đề lớn đối với việc triển khai của bạn, bạn có thể thử giải quyết nó bằng cách thực hiện cơ chế thử lại một số loại, v.v.

Cuối cùng, CouchDB thực sự là một viên ngọc của cơ sở dữ liệu, nhưng không chỉ lấy nó và hy vọng nó sẽ là một phương pháp chống đạn. Rất nhiều thực sự sẽ phụ thuộc vào các chi tiết của ứng dụng cụ thể của bạn.

+5

Trong tham chiếu đến điều này: _ "nhưng bạn không muốn biến nó thành id tài liệu (vì bất kỳ lý do gì)" _ Tôi chỉ muốn chỉ ra rằng việc sử dụng email và/hoặc tên người dùng là _id là không mong muốn. Nếu người dùng thay đổi email hoặc tên người dùng của họ, nó sẽ phá vỡ mọi tham chiếu bên ngoài thành id tài liệu đó (và có thể sẽ có một nhóm). –

+2

Đúng. Điều đó rơi vào "vì lý do gì" tôi đoán. :) –

+2

Vâng, đó là một lý do chính đáng :) BTW - Tôi đã đánh giá cao Thảo luận ngắn của bạn rất nhiều. Thông tin rất tốt. –

Các vấn đề liên quan