2009-11-21 56 views
13

Tôi có thể tối ưu hóa truy vấn Dữ liệu cốt lõi khi tìm kiếm các từ phù hợp trong văn bản không? (Câu hỏi này cũng liên quan đến sự khôn ngoan của SQL tùy chỉnh so với dữ liệu cốt lõi trên iPhone.)Cách tối ưu hóa truy vấn Dữ liệu lõi để tìm kiếm toàn văn

Tôi đang sử dụng một ứng dụng mới (iPhone) là công cụ tham khảo cầm tay cho cơ sở dữ liệu khoa học. Giao diện chính là chế độ xem bảng có thể tìm kiếm tiêu chuẩn và tôi muốn trả lời khi bạn nhập khi người dùng nhập từ mới. Từ phù hợp phải là tiền tố của các từ trong văn bản. Văn bản bao gồm 100.000 từ.

Trong nguyên mẫu của tôi, tôi đã mã hóa SQL trực tiếp. Tôi đã tạo một bảng "từ" riêng biệt chứa mọi từ trong các trường văn bản của thực thể chính. Tôi đã lập chỉ mục các từ và thực hiện tìm kiếm dọc theo các dòng của

SELECT id, * FROM textTable 
    JOIN (SELECT DISTINCT textTableId FROM words 
     WHERE word BETWEEN 'foo' AND 'fooz') 
    ON id=textTableId 
LIMIT 50 

Điều này chạy rất nhanh. Việc sử dụng IN cũng có thể hoạt động tốt, tức là

SELECT * FROM textTable 
WHERE id IN (SELECT textTableId FROM words 
       WHERE word BETWEEN 'foo' AND 'fooz') 
LIMIT 50 

Giới hạn rất quan trọng và cho phép tôi hiển thị kết quả nhanh chóng. Tôi thông báo cho người dùng rằng có quá nhiều thứ để hiển thị nếu đạt đến giới hạn. Đây là kludgy.

Tôi đã dành nhiều ngày cuối cùng cân nhắc lợi thế của việc chuyển sang Dữ liệu cốt lõi, nhưng tôi lo lắng về việc thiếu kiểm soát trong lược đồ, lập chỉ mục và truy vấn một truy vấn quan trọng.

Về mặt lý thuyết, NSPredicate của textField MATCHES '.*\bfoo.*' sẽ chỉ hoạt động, nhưng tôi chắc chắn nó sẽ chậm. Loại tìm kiếm văn bản này dường như quá phổ biến đến nỗi tôi tự hỏi cuộc tấn công thông thường là gì? Bạn sẽ tạo một thực thể từ như tôi đã làm ở trên và sử dụng một vị từ của "từ BEGINSWITH 'foo'"? Liệu nó có hoạt động nhanh như nguyên mẫu của tôi không? Dữ liệu cốt lõi có tự động tạo các chỉ mục phù hợp không? Tôi không thể tìm thấy bất kỳ phương tiện rõ ràng nào để tư vấn cho kho lưu trữ liên tục về các chỉ mục.

Tôi thấy một số lợi thế tốt đẹp của Dữ liệu cốt lõi trong ứng dụng iPhone của tôi. Việc cân nhắc các lỗi và bộ nhớ khác cho phép truy xuất cơ sở dữ liệu hiệu quả cho các truy vấn tableview mà không đặt giới hạn tùy ý. Việc quản lý đồ thị đối tượng cho phép tôi dễ dàng duyệt qua các thực thể mà không cần viết nhiều SQL. Các tính năng di chuyển sẽ tốt đẹp trong tương lai. Mặt khác, trong môi trường tài nguyên hạn chế (iPhone), tôi lo lắng rằng một cơ sở dữ liệu được tạo tự động sẽ bị cồng kềnh với siêu dữ liệu, các mối quan hệ nghịch đảo không cần thiết, các kiểu dữ liệu thuộc tính không hiệu quả, v.v.

Tôi có nên đi sâu vào hoặc thận trọng không? .

Trả lời

10

Tôi đã thực hiện giải pháp khắc phục sự cố. Tôi nghĩ nó tương tự như this post. Tôi đã thêm mã nguồn hỗn hợp vào dự án Core Data của mình, sau đó tạo một lớp tìm kiếm toàn văn bản không phải là một lớp con đối tượng được quản lý. Trong lớp FTS, tôi #import "sqlite3.h" (tệp nguồn) thay vì khung sqlite. Lớp FTS lưu vào một tệp .sqlite khác với kho lưu trữ dữ liệu cốt lõi.

Khi tôi nhập dữ liệu của mình, đối tượng Dữ liệu cốt lõi lưu trữ hàng của đối tượng FTS có liên quan dưới dạng thuộc tính số nguyên. Tôi có một tập dữ liệu tĩnh, vì vậy tôi không lo lắng về tính toàn vẹn tham chiếu, nhưng mã để duy trì tính toàn vẹn sẽ là tầm thường.

Để thực hiện FTS, tôi MATCH truy vấn lớp FTS, trả về tập hợp các hàng. Trong lớp đối tượng được quản lý của tôi, tôi truy vấn các đối tượng tương ứng với [NSPredicate predicateWithFormat:@"rowid IN %@", rowids]. Tôi tránh đi qua bất kỳ mối quan hệ nhiều-nhiều như thế này.

Cải thiện hiệu suất thật ấn tượng. Tập dữ liệu của tôi là 142287 hàng, bao gồm 194MB (Dữ liệu chính) và 92MB (FTS có các từ dừng được loại bỏ). Tùy thuộc vào tần suất cụm từ tìm kiếm, tìm kiếm của tôi đã tăng từ vài giây đến 0,1 giây cho các cụm từ không thường xuyên (< 100 lần truy cập) và 0,2 giây đối với các cụm từ thông dụng (> 2000 lần truy cập).

Tôi chắc chắn có vô số vấn đề với cách tiếp cận của tôi (mã bloat, xung đột không gian tên có thể, mất một số tính năng Dữ liệu cốt lõi), nhưng có vẻ như nó đang hoạt động.

2

Dive trong

Dưới đây là một cách để đi về nó:

  1. Đặt hồ sơ của bạn vào một cửa hàng khăng khăng Core Data
  2. Sử dụng NSFetchedResultsController để quản lý Kết quả là thiết lập trên Word tổ chức của bạn (Core Tương đương dữ liệu với bảng "từ" SQL)
  3. Sử dụng UISearchDisplayController để áp dụng NSPredicate trên tập kết quả theo thời gian thực

Khi bạn có kết quả được đặt qua NSFetchedResultsController, việc áp dụng biến vị ngữ là khá dễ dàng. Theo kinh nghiệm của tôi, nó cũng sẽ được đáp ứng.Ví dụ:

if ([self.searchBar.text length]) { 
    _predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"(word contains[cd] '%@')", self.searchBar.text]]; 
    [self.fetchedResultsController.fetchRequest setPredicate:_predicate]; 
} 

NSError *error; 
if (![self.fetchedResultsController performFetch:&error]) { 
    // handle error... 
} 
NSLog(@"filtered results: %@", [self.fetchedResultsController fetchedObjects]); 

sẽ lọc kết quả thiết [self.fetchedResultsController fetchedObjects] khi đang bay, làm một tìm kiếm case-insensitive trên word.

+0

Cảm ơn câu trả lời của bạn. Tôi chỉ đang viết công cụ dòng lệnh để lấy dữ liệu sqlite ban đầu được nạp vào một db tuân thủ xcdatamodel. Lao động đáng kể có liên quan. Tôi sẽ báo cáo lại về kinh nghiệm của tôi. –

+0

Để theo dõi ví dụ của bạn, tôi nghĩ rằng vấn đề là một yêu cầu tìm nạp sẽ không nằm trong thực thể Word, nhưng trên thực thể textTable. (Ví dụ giả sử textTable chứa email và Word chứa tất cả các từ trong tất cả các trường email.) Tôi nghĩ điều này làm phức tạp đáng kể vấn đề vì fetchResultsController phải giữ các thực thể textTable được lọc qua vị từ - và một biến vị ngữ ANY hoặc SUBQUERY như vậy chậm. Có thể có cách để thực hiện điều này theo hướng "ngược lại": bằng cách bắt đầu từ phù hợp với từ, theo mối quan hệ nghịch đảo và soạn thảo textTable. Hừm. –

+0

Nếu phần đầu tiên của biến vị ngữ của bạn làm giảm không gian tìm kiếm càng nhiều càng tốt, phần còn lại của biến vị ngữ sẽ thực hiện nhanh hơn, tổng thể, với ít không gian hơn mà nó phải tìm kiếm bên trong. Xem nhanh phần hiệu suất của Hướng dẫn dữ liệu cốt lõi tại đây: http://developer.apple.com/mac/library/documentation/cocoa/conceptual/CoreData/Articles/cdPerformance.html#//apple_ref/doc/uid/ TP40003468 –

3

Để theo dõi câu hỏi này, tôi nhận thấy rằng truy vấn là con chó chậm sử dụng Dữ liệu cốt lõi. Tôi đã gãi đầu vào cái này trong nhiều giờ.

Như trong ví dụ SQL trong câu hỏi của tôi, có hai thực thể: textTable và các từ mà từ chứa mỗi từ, nó được lập chỉ mục và có mối quan hệ nhiều-nhiều giữa textTable và các từ. Tôi đã điền vào cơ sở dữ liệu chỉ với 4000 từ và đối tượng textTable 360. Giả sử mối quan hệ textTable những lời phản đối được gọi là searchWords, sau đó tôi có thể sử dụng một vị trên thực thể textTable trông giống như

predicate = [NSPredicate predicateWithFormat:@"ANY searchWords.word BEGINSWITH %@", query]; 

(tôi có thể thêm liên từ các vị này cho nhiều thuật ngữ truy vấn.)

Trên iPhone, truy vấn này mất nhiều giây. Câu trả lời cho SQL được mã hóa bằng tay của tôi sử dụng một tập kiểm tra lớn hơn là ngay lập tức.

Nhưng đây không phải là kết thúc của nó. Có những hạn chế đối với NSPredicate khiến các truy vấn khá đơn giản chậm và phức tạp. Ví dụ, hãy tưởng tượng trong ví dụ trên mà bạn muốn lọc bằng cách sử dụng nút phạm vi. Giả sử thực thể từ chứa tất cả các từ trong tất cả các trường văn bản, nhưng phạm vi sẽ giới hạn từ đó thành các từ từ các trường cụ thể. Do đó, các từ có thể có thuộc tính "nguồn" (ví dụ: phần đầu và nội dung thư của email).

Tự nhiên, khi đó, toàn bộ văn bản sẽ bỏ qua thuộc tính nguồn, như trong ví dụ trên, nhưng truy vấn được lọc sẽ giới hạn tìm kiếm đối với giá trị nguồn cụ thể. Thay đổi có vẻ đơn giản này yêu cầu một SUBQUERY. Ví dụ: tính năng này không hoạt động:

ANY searchWords.word BEGINSWITH "foo" AND ANY searchWords.source = 3 

vì các thực thể đúng cho hai biểu thức có thể khác nhau. Thay vào đó, bạn phải làm điều gì đó như:

SUBQUERY(searchWords, $x, $x.word BEGINSWITH "foo" AND $x.source = 3)[email protected] > 0 

Tôi nhận thấy rằng các truy vấn phụ này, có lẽ không đáng ngạc nhiên, chậm hơn các biến vị ngữ sử dụng "BẤT CỨ". Tại thời điểm này tôi rất tò mò làm thế nào các lập trình viên Cocoa sử dụng hiệu quả Core Data để tìm kiếm toàn văn vì tôi không khuyến khích bởi cả tốc độ đánh giá vị ngữ và tính biểu cảm của NSPredicates. Tôi đã chạy lên tường.

+1

Xem xét xem phần hiệu suất tại đây: http://developer.apple.com/mac/library/documentation/cocoa/conceptual/CoreData/Articles/cdPerformance.html#//apple_ref/doc/uid/TP40003468 –

+0

Cảm ơn bạn đã liên kết. Từ đó tôi phát hiện ra rằng đối số thực thi "-com.apple.CoreData.SQLDebug 1" sẽ gửi gỡ lỗi sqlite tới stderr. Từ bãi chứa đó tôi thấy truy vấn. Không có gì thực sự sai với truy vấn, nhưng bởi vì từ <=> mối quan hệ textTable là nhiều-nhiều, có một bảng quan hệ để tham gia. Do đó, truy vấn phải tham gia trên 3 bảng. Khi tôi loại bỏ các nghịch đảo truy vấn bây giờ chạy nhanh hơn nhiều trên phần cứng iPhone! Than ôi, lược đồ mới có khóa ngoài trong bảng Word, do đó chính từ và siêu dữ liệu được lặp lại cho mỗi lần xuất hiện. Không gian lãng phí. –

+0

Bạn có thể đạt được tốc độ, nhưng Apple khuyến cáo duy trì các mối quan hệ nghịch đảo để duy trì tính toàn vẹn dữ liệu. Dữ liệu cốt lõi sử dụng thông tin này để đảm bảo tính nhất quán của biểu đồ đối tượng nếu có thay đổi (xem “Thao tác các mối quan hệ và tính toàn vẹn đồ thị đối tượng”). Hãy xem tại đây để biết thêm thông tin: http://developer.apple.com/DOCUMENTATION/Cocoa/Conceptual/CoreData/Articles/cdRelationships.html –

2

Sau khi đấu tranh với cùng một vấn đề này, tôi đã chạy qua một loạt các bài đăng mà tác giả có cùng một vấn đề và đã đưa ra this solution. Ông báo cáo một sự cải tiến từ 6-7 giây thời gian tìm kiếm đến giữa 0,13 và 0,05 giây.

Tập dữ liệu của anh ấy cho FTS là 79 tài liệu (kích thước tệp 175k, 3600 mã thông báo rời rạc, 10000 tài liệu tham khảo). Tôi chưa thử giải pháp của anh ấy, nhưng tôi nghĩ tôi sẽ gửi ASAP. Xem thêm Part 2 bài đăng của anh ấy để biết tài liệu của anh ấy về vấn đề và Part 1 cho tài liệu của anh ấy về tập dữ liệu.

+0

Vấn đề tôi có với giải pháp này là truy vấn và từ khóa phải là đối sánh chính xác. Đối với kết quả thời gian thực, bạn muốn bất kỳ tiền tố từ khóa nào phù hợp với truy vấn.Trong trường hợp đó, không thể sử dụng đối tượng thay vì chuỗi trong vị từ. –

+0

Cố gắng thực hiện điều này bản thân mình và không có cải tiến, có lẽ vì tôi đã sử dụng có chứa [cd]. Tôi đã từ bỏ và bắt đầu với sqlite3 fts. Peter, cảm ơn các liên kết thêm. Tôi bị giới hạn chỉ một người. – jluckyiv

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