2012-01-01 9 views
28

Tôi đang viết luận án về khả năng ngoại tuyến của các ứng dụng web. Nhiệm vụ của tôi là thể hiện khả năng lưu trữ ngoại tuyến thông qua một ứng dụng web với cơ sở dữ liệu quan hệ phía máy chủ và lưu lượng truy cập Ajax/JSON giữa máy khách và máy chủ. Triển khai đầu tiên của tôi đã sử dụng một cách tiếp cận với localStorage, lưu từng phản hồi Ajax dưới dạng giá trị với URL yêu cầu làm khóa. Ứng dụng này hoạt động tốt. Tuy nhiên, trong bước tiếp theo, tôi muốn (nghĩa là luận án yêu cầu) triển khai phiên bản nâng cao hơn với cơ sở dữ liệu phía máy khách. Vì máy chủ duy trì một cơ sở dữ liệu quan hệ, nên cơ sở dữ liệu Web SQL sẽ là lựa chọn trực quan. Nhưng, như chúng ta biết, tiêu chuẩn không được chấp nhận và tôi không muốn sử dụng một công nghệ mà tương lai không chắc chắn. Vì vậy, tôi muốn sử dụng IndexedDB để thực hiện logic cơ sở dữ liệu phía máy khách. Thật không may, sau khi đọc rất nhiều tài liệu trên web mà chủ yếu là giữ rất nhiều trầy xước bề mặt (todo-ghi chú ứng dụng vv), tôi vẫn không biết làm thế nào để tiến hành.Các vấn đề khái niệm với IndexedDB (các mối quan hệ vv)

Tác vụ của tôi có vẻ khá đơn giản: triển khai cơ sở dữ liệu phía máy chủ trên máy khách với IndexedDB để sao chép tất cả dữ liệu đã được lấy từ máy chủ. Những vấn đề, mà làm này ít đơn giản là:

  • Cơ sở dữ liệu phía máy chủ được quan hệ, IndexedDB là (nhiều hơn hoặc ít hơn) hướng đối tượng
  • Không có cách nào trực quan để đồng bộ hóa khách hàng- và Server- cơ sở dữ liệu bên
  • không có cách nào trực quan để thực hiện các mối quan hệ trong IndexedDB được thực hiện với các phím nước ngoài và tham gia vào máy chủ

Ngay bây giờ, tôi có một khái niệm trong tâm trí mà tôi thực sự sợ để bắt đầu triển khai. Tôi đã nghĩ về việc tạo một cửa hàng đối tượng cho mỗi bảng trong cơ sở dữ liệu máy chủ và lập trình các đối tượng quan hệ trong các cửa hàng đối tượng khác nhau theo cách thủ công. Trong ứng dụng của tôi, trong đó, trong ngắn hạn, quản lý các khóa học của một trường đại học, tôi muốn có 7 cửa hàng đối tượng.

tôi muốn chứng minh ý tưởng của tôi với một ví dụ cho một phản ứng JSON từ máy chủ (/ * đây là những ý kiến ​​* /):

{ "course": { /* course object */ 
    "id":1, 
    "lecturer": { "id":"1", /* lecturer object with many attributes */ }, 
    "semester": { "id":"1", /* semester object with many attributes */ }, 
    /* more references and attributes */ 
}} 

Các thuật toán để cửa hàng dữ liệu với IndexedDB sẽ lưu trữ mỗi đối tượng áp dụng cho một đối tượng lưu trữ trong kho lưu trữ đối tượng thích hợp và thay thế các đối tượng có tham chiếu đến các đối tượng này. Ví dụ, đối tượng dĩ nhiên ở trên sẽ giống như sau trong các cửa hàng đối tượng 'nhiên':

{ "course": { /* course object */ 
    "id":1, 
    "lecturer": 
    { "reference": { /* reference to the lecturer in the object store 'lecturer' */ 
     "objectstore":"lecturer", 
     "id":"1" } 
    }, 
    "semester": 
    { "reference": { /* reference to the semester in the object store 'semester' */ 
     "objectstore":"semester", 
     "id":"1" } 
    } 
    /* more references and attributes */ 
}} 

Các thuật toán để lấy dữ liệu với IndexedDB sau đó sẽ thực hiện như sau (Tôi có một mẫu đệ quy mơ hồ trong tâm trí):

Retrieve the course object with id=1 from the object store 'course' 
For each reference object in the retrieved course object, do 
    Retrieve the object with id=reference.id from the object store reference.objectstore 
    Replace the reference object with the retrieved object 

Rõ ràng là việc triển khai này sẽ thực sự rườm rà, đặc biệt là do bản chất không đồng bộ của IndexedDB. Nó cũng sẽ dẫn đến nhiều giao dịch khác nhau cho cơ sở dữ liệu chỉ để lấy ra một đối tượng khóa học và hiệu suất sẽ phải chịu rất nhiều (tôi không thực sự biết hiệu suất của các giao dịch IndexedDB trông như thế nào).

Tôi có thể làm điều này tốt hơn và đơn giản hơn bằng cách nào?

Tôi đã xem các chủ đề này đại diện cho các sự cố tương tự: link1, link2.Tôi không thấy bất kỳ giải pháp đơn giản nào trong số này. Hơn nữa, tôi muốn tránh sử dụng một khuôn khổ wrapper IndexedDB do một số lý do.

Tôi cũng có thể tưởng tượng rằng tôi hoàn toàn theo dõi sai với IndexedDB cho vấn đề của mình.

Edit:

cuối cùng tôi đã kết thúc theo đuổi cách tiếp cận của tôi để lưu trữ các tài liệu tham khảo trong các đối tượng chính mình trong IndexedDB. Điều này có thể dẫn đến một số vấn đề về hiệu suất trong trường hợp một lượng lớn dữ liệu có nhiều tham chiếu. Tuy nhiên, nếu được sử dụng một cách thông minh, có thể tránh được một số lượng lớn các lần truy cập cơ sở dữ liệu và lặp lại trong hầu hết các trường hợp, và không cần lưu trữ lược đồ cơ sở dữ liệu phức tạp trong bộ nhớ hoặc trong chính IndexedDB.

Nói chung tôi phải nói, rằng tôi có ấn tượng rằng tôi hiểu sai ý tưởng năng động và thẳng thắn với IndexedDB như một cơ sở dữ liệu schemaless theo một cách nào đó. Nhưng bất cứ điều gì, tôi thực hiện tất cả mọi thứ trong JavaScript, nó hoạt động tốt và không có cơ hội cho bất kỳ mâu thuẫn nào.

+0

Liên kết tới tài khoản đăng bị xóa đã bị hỏng và cũng không phải là một phần của câu hỏi. –

Trả lời

21

Tôi mới làm quen với IndexedDB nhưng tôi cũng đã suy nghĩ rất nhiều về cách sử dụng IndexedDB cho các mục đích như thế này. Điều đầu tiên tôi sẽ đề nghị, nếu bạn chưa thực hiện nó, hãy xem xét cách các cơ sở dữ liệu khóa-giá trị/tài liệu khác (CouchDB, MongoDB, v.v.) làm việc, vì đó là loại cơ sở dữ liệu mà IndexedDB là.

Có một số phương pháp khác nhau để xử lý các mối quan hệ trong cơ sở dữ liệu tài liệu ... như đồng bộ hóa với cơ sở dữ liệu phía máy chủ quan hệ của bạn, có thể bạn sẽ cần tạo một loại ánh xạ tùy chỉnh có ý nghĩa đối với IndexedDB sẽ không ánh xạ rất rõ ràng tới một cơ sở dữ liệu quan hệ. Tuy nhiên, tôi nghĩ rằng việc thiết lập bản đồ như vậy chắc chắn có thể thực hiện được và vấn đề lớn hơn là cách xử lý các mối quan hệ trong IndexedDB, vì vậy đó là những gì tôi sẽ tập trung vào đây ...

Theo giải pháp đề xuất của bạn, tôi nghĩ rằng thực sự có thể hoạt động tốt và bạn có thể viết một thư viện truy vấn đơn giản giúp củng cố mã hệ thống ống nước (chi tiết hơn về điều đó bên dưới). Các cửa hàng có giá trị khóa được xây dựng rất hiệu quả khi tìm kiếm các mục theo khóa, do đó, làm như vậy cho từng đối tượng liên quan có thể không hiệu quả như bạn nghĩ ... tuy nhiên, tôi đã đưa ra một ý tưởng khác giúp sử dụng các chỉ mục tốt hơn.

Trước tiên, đối với giải pháp được đề xuất của tôi, bạn cần lưu trữ siêu dữ liệu "đối tượng" ở đâu đó ngoài chính đối tượng "tham chiếu" ... không cần thiết phải được lưu trữ trong IndexedDB tại tất cả các; bạn chỉ có thể sử dụng một lược đồ trong bộ nhớ cho rằng:

var schema = { 
    Course: { 
     fields: [id, title], 
     relationships: { 
      lecturers: {objectstore: 'lecturer'}, 
      semester: {objectstore: 'semester'}, 
     } 
    }, 
    Lecturer: { ... } 
    ... 
}; 

(Bằng cách này, ví dụ JSON của bạn có lỗi ... bạn có thể không có nhiều hơn một phím gọi là "tham khảo" - nó sẽ cần là một mảng "tham chiếu".)

Điều này giúp bạn lưu trữ trực tiếp các giá trị ID trong các trường mối quan hệ, để bạn có thể tạo chỉ mục trên chúng (tôi đã sử dụng tiền tố thư cho rõ ràng mặc dù trong thực tế tất cả trong số này có thể có ID là 1, vì giá trị ID không cần phải là duy nhất trên các cửa hàng):

var course1 = { 
    id:'C1', 
    lecturers:['L1'], 
    semester:1 
}; 

var lecturer1 = { 
    id:'L1', 
    courses:['C1'] 
} 

var semester1 = { 
    id:'S1', 
    courses:['C1'] 
} 

Bạn sẽ, tất nhiên, phải cẩn thận rằng tất cả các hoạt động lưu trữ/truy xuất đã xảy ra thông qua các chức năng truy cập dữ liệu (ví dụ: insert(), update(), delete()) đủ thông minh để đảm bảo rằng các mối quan hệ luôn được cập nhật chính xác trên cả hai đầu ... thực sự bạn có thể không cần tùy thuộc vào cách bạn định truy vấn dữ liệu, nhưng có vẻ như như một ý tưởng hay vì đôi khi bạn chỉ muốn nhận được ID của các đối tượng liên quan (để được tra cứu sau này, hay không) thay vì thực sự lấy chúng.

Giả sử bạn có chỉ mục trên trường "khóa học" trong cửa hàng giảng viên. Sử dụng một chỉ mục, bạn có thể tìm kiếm tất cả các giảng viên liên kết với một ID khóa học đặc biệt trong một ngã swoop:

lecturerStore.index("courses").get("C1").onsuccess = … 

Ví dụ rằng nó không quan trọng nhiều vì khóa học này sẽ thường chỉ có 1-2 giảng viên, nhưng xem xét làm thế nào một chỉ số có thể được sử dụng để hiệu quả tìm kiếm tất cả các khóa học trong một học kỳ đặc biệt:

coursesStore.index("semester").get("S1").onsuccess = … 

Lưu ý rằng trong ví dụ giảng viên (một mối quan hệ nhiều-nhiều), chỉ số này sẽ cần phải được xác định là "multientry", nghĩa là nếu bạn có một trường có giá trị là một mảng, mỗi phần tử của mảng sẽ được thêm vào chỉ mục. (Xem https://developer.mozilla.org/en/IndexedDB/IDBObjectStore#createIndex ... Tôi không chắc chắn về hỗ trợ của trình duyệt này là gì.)

Và tôi chắc chắn bạn cũng có thể làm những việc thông minh khác với lập chỉ mục, sử dụng con trỏ và IDBKeyRange để thực hiện một số loại " tham gia "hoạt động. Đối với những ý tưởng, hãy kiểm tra liên kết này, mà thể hiện cách các mối quan hệ xử lý trong CouchDB:

http://wiki.apache.org/couchdb/EntityRelationship

liên kết đó cũng đề cập đến sử dụng tài liệu nhúng, mà là một cái gì đó bạn chắc chắn nên xem xét - không phải tất cả đối tượng nhất thiết cần phải có cửa hàng đối tượng của riêng họ, đặc biệt là đối với mối quan hệ "tổng hợp".

(Bằng cách này, tôi không chắc chắn nó hữu ích như thế nào đối với bạn vì nó không cung cấp nhiều cách truy vấn, nhưng ai đó đã thực sự triển khai cơ sở dữ liệu giống như CouchDB trên đầu trang IndexedDB: https://github.com/mikeal/pouchdb)

Ngoài các chỉ mục, việc triển khai cơ chế lưu vào bộ nhớ cache cũng có thể giúp ích rất nhiều.

Bây giờ, để đơn giản hóa quy trình truy vấn, tôi biết bạn đã đề cập không muốn sử dụng thư viện trình bao ... nhưng tôi đã có ý tưởng về một API tiện lợi có thể được tạo, chấp nhận một đối tượng như sau:

//select all courses taught by 'Professor Wilkins' 
{ 
from: 'lecturer', //open cursor on lecturer store 
where: function(lecturer) { return lecturer.name=='Professor Wilkins' }, //evaluate for each item found 
select: function(lecturer) { return lecturer.courses }, //what to return from previous step 
//this should be inferred in this case, but just to make it clear... 
eagerFetch: function(lecturer) { return lecturer.courses } 
} 

Tôi không chắc sẽ khó thực hiện như thế nào, nhưng dường như nó sẽ giúp cuộc sống dễ dàng hơn.

Tôi đã rambled đủ lâu, nhưng tôi muốn đề cập đến một điều cuối cùng, đó là tôi cũng đã suy nghĩ về việc mượn một số ý tưởng từ cơ sở dữ liệu đồ thị, vì chúng xử lý tốt hơn mối quan hệ và tôi nghĩ rằng nó sẽ có thể thực hiện một cơ sở dữ liệu đồ thị trên đầu trang của IndexedDB, tôi chỉ chưa chắc chắn như thế nào thực tế nó sẽ được.

Chúc may mắn!

+0

Tôi biết điều này là muộn, nhưng cảm ơn bạn đã trả lời chi tiết của bạn, nó thực sự có giá trị. Tôi đăng một câu trả lời dưới đây ban đầu cũng bao gồm một số công cụ gratitute, nhưng những phần này đã được gỡ bỏ không may. – Felix

+0

Rất vui khi bạn thấy nó hữu ích! Và cảm ơn vì đã đăng bài của bạn ... Tôi chưa có cơ hội để xem nó nhưng tôi chắc chắn sẽ làm như vậy. –

+0

Tôi vừa phát hiện ra thư viện này, có vẻ là một cách tiếp cận đầy hứa hẹn để truy vấn dữ liệu từ IndexedDB, cũng như nhiều cơ sở dữ liệu khác, sử dụng cùng một API: http://jaydata.org/ –