2013-07-30 101 views
11

Tôi đang cố gắng sử dụng MongoDB để triển khai từ điển ngôn ngữ tự nhiên. Tôi có một bộ sưu tập các từ ngữ, mỗi từ có một số dạng từ như là các phần phụ. Đây là những gì một lexeme đơn trông giống như:Tăng tốc tìm kiếm chuỗi regex trong MongoDB

{ 
    "_id" : ObjectId("51ecff7ee36f2317c9000000"), 
    "pos" : "N", 
    "lemma" : "skrun", 
    "gloss" : "screw", 
    "wordforms" : [ 
     { 
      "number" : "sg", 
      "surface_form" : "skrun", 
      "phonetic" : "ˈskruːn", 
      "gender" : "m" 
     }, 
     { 
      "number" : "pl", 
      "surface_form" : "skrejjen", 
      "phonetic" : "'skrɛjjɛn", 
      "pattern" : "CCCVCCVC" 
     } 
    ], 
    "source" : "Mayer2013" 
} 

Hiện nay tôi có một bộ sưu tập của một số 4000 lexemes, và mỗi trong số này có trên trung bình một danh sách của một số 1000 wordforms (như trái ngược với chỉ 2 ở trên). Điều này có nghĩa là tôi có 4.000.000 biểu mẫu từ duy nhất trong bộ sưu tập một cách có ảnh hưởng và tôi cần có khả năng tìm kiếm chúng trong một khoảng thời gian hợp lý.

Một truy vấn bình thường sẽ trông như thế này:

db.lexemes.find({"wordforms.surface_form":"skrejjen"}) 

Tôi có một chỉ mục trên wordforms.surface_form, và tìm kiếm này là rất nhanh. Tuy nhiên, nếu tôi muốn có các ký tự đại diện trong tìm kiếm của mình, hiệu suất sẽ không giống nhau. Ví dụ:

db.lexemes.find({"wordforms.surface_form":/skrej/}) 

mất hơn 5 phút (tại thời điểm này tôi đã bỏ cuộc chờ). Như đã đề cập in this question, tìm kiếm regex trên các chỉ mục được biết là xấu. Tôi biết rằng việc thêm^anchor vào các tìm kiếm regex helps a lot, nhưng nó cũng giới hạn nghiêm trọng khả năng tìm kiếm của tôi. Ngay cả khi tôi sẵn sàng hy sinh, tôi nhận thấy thời gian phản ứng vẫn có thể thay đổi rất nhiều tùy thuộc vào regex. Truy vấn

db.lexemes.find({"wordforms.surface_form":/^s/}) 

Mất 35 giây để hoàn tất.

Kết quả tốt nhất mà tôi đã có cho đến nay trên thực tế là khi tôi tắt chỉ mục bằng cách sử dụng hint. Trong trường hợp này, mọi thứ dường như được cải thiện đáng kể. Truy vấn này:

db.lexemes.find({"wordforms.surface_form":/skrej/}).hint('_id_') 

mất khoảng 3 giây để hoàn thành.

Câu hỏi của tôi là tôi có thể làm gì khác để cải thiện các thời gian tìm kiếm này không? Như họ đang có, họ vẫn còn một chút chậm và tôi đã xem xét việc chuyển sang MySQL với hy vọng nhận được hiệu suất. Nhưng tôi thực sự muốn giữ tính linh hoạt của Mongo và tránh tất cả sự bình thường tẻ nhạt trong một RDBMS. Bất kỳ đề xuất? Bạn có nghĩ rằng tôi sẽ chạy vào một số chậm đi bất kể động cơ DB, với số lượng dữ liệu văn bản này?

Tôi biết về tính năng mới text search của Mongo nhưng lợi thế của việc này (tokenisation và bắt nguồn) không liên quan trong trường hợp của tôi (chưa kể ngôn ngữ của tôi không được hỗ trợ). Nó không phải là rõ ràng nếu tìm kiếm văn bản thực sự là nhanh hơn hơn bằng cách sử dụng regex anyway.

Trả lời

7

Theo đề xuất của Derick, tôi đã tái cấu trúc dữ liệu trong cơ sở dữ liệu của mình sao cho tôi có "dạng chữ" dưới dạng bộ sưu tập chứ không phải là phần phụ trong "từ vựng". Kết quả trên thực tế tốt hơn! Dưới đây là một số so sánh tốc độ. Ví dụ cuối cùng sử dụng hint đang cố ý bỏ qua các chỉ mục trên surface_form, mà trong lược đồ cũ thực sự nhanh hơn.

Cũ schema (xem original question)

Query                Avg. Time 
db.lexemes.find({"wordforms.surface_form":"skrun"})    0s 
db.lexemes.find({"wordforms.surface_form":/^skr/})     1.0s 
db.lexemes.find({"wordforms.surface_form":/skru/})     > 3mins ! 
db.lexemes.find({"wordforms.surface_form":/skru/}).hint('_id_') 2.8s 

schema mới (xem Derick's answer)

Query                Avg. Time 
db.wordforms.find({"surface_form":"skrun"})      0s 
db.wordforms.find({"surface_form":/^skr/})       0.001s 
db.wordforms.find({"surface_form":/skru/})       1.4s 
db.wordforms.find({"surface_form":/skru/}).hint('_id_')   3.0s 

Đối với tôi đây là bằng chứng khá tốt mà một schema refactored sẽ làm cho tìm kiếm nhanh hơn, và đáng giá dữ liệu dư thừa (hoặc yêu cầu thêm tham gia).

9

Một khả năng là lưu trữ tất cả các biến thể mà bạn đang nghĩ có thể hữu ích như một phần tử mảng - không chắc liệu điều đó có thể thực hiện được hay không!

{ 
     "number" : "pl", 
     "surface_form" : "skrejjen", 
     "surface_forms: [ "skrej", "skre" ], 
     "phonetic" : "'skrɛjjɛn", 
     "pattern" : "CCCVCCVC" 
    } 

Tôi cũng có thể đề xuất không lưu trữ 1000 biểu mẫu từ với mỗi từ, nhưng xoay vòng để có tài liệu nhỏ hơn.Nhỏ hơn tài liệu của bạn càng ít MongoDB sẽ phải đọc vào bộ nhớ cho mỗi lần tìm kiếm (miễn là các điều kiện tìm kiếm không yêu cầu quét toàn bộ khóa học):

{ 
    "word": { 
     "pos" : "N", 
     "lemma" : "skrun", 
     "gloss" : "screw", 
    }, 
    "form" : { 
     "number" : "sg", 
     "surface_form" : "skrun", 
     "phonetic" : "ˈskruːn", 
     "gender" : "m" 
    }, 
    "source" : "Mayer2013" 
} 

{ 
    "word": { 
     "pos" : "N", 
     "lemma" : "skrun", 
     "gloss" : "screw", 
    }, 
    "form" : { 
     "number" : "pl", 
     "surface_form" : "skrejjen", 
     "phonetic" : "'skrɛjjɛn", 
     "pattern" : "CCCVCCVC" 
    }, 
    "source" : "Mayer2013" 
} 

Tôi cũng nghi ngờ rằng MySQL sẽ thực hiện tốt hơn ở đây với các tìm kiếm các biểu mẫu từ ngẫu nhiên vì nó sẽ phải thực hiện quét toàn bộ bảng giống như MongoDB. Điều duy nhất có thể giúp có bộ nhớ cache truy vấn - nhưng đó là thứ mà bạn có thể xây dựng trong giao diện người dùng/API tìm kiếm trong ứng dụng của bạn khá dễ dàng.

+0

Cảm ơn bạn đã đề xuất! Điều này tất nhiên giới thiệu rất nhiều thông tin dư thừa và sẽ làm cho bộ sưu tập tổng thể lớn hơn, tuy nhiên nếu nó làm tăng thời gian phản hồi tìm kiếm thì tôi có thể xem xét nó. Tôi sẽ chạy một vài thử nghiệm để xem đây có phải là trường hợp không và đăng cập nhật tại đây. –