2013-03-12 31 views
10

Tôi hiện đang có một lĩnh vực tìm kiếm duy nhất tìm kiếm đối với nhiều cột sử dụng mã này:Làm thế nào để thực hiện một tìm kiếm cột mysql toàn văn nhiều nơi từ một phần được kết hợp

$searchArray = explode(" ", $searchVal); 
$query="SELECT * FROM users WHERE "; 
$i=0; 
foreach ($searchArray as $word) { 
    if ($i != 0) $query .= " OR "; 
    $query .= " MATCH (`first_name`, `last_name`, `email`) AGAINST ('".$word."*' IN BOOLEAN MODE)"; 
    $i++; 
} 

phép nói rằng tôi có hai hàng này trong bảng:

id | last_name | first_name | email 
1 | Smith  | John  | [email protected] 
2 | Smith  | Bob  | [email protected] 

Nếu tôi nhập "John S", chỉ kết quả đầu tiên cho thấy hành vi mong muốn.

Nếu tôi nhập "John Smith", chỉ kết quả đầu tiên cho biết hành vi mong muốn là gì.

Nếu tôi nhập "Smith J", cả hai kết quả sẽ hiển thị mặc dù Bob không khớp.

Nếu tôi nhập "Smith John", cả hai kết quả sẽ hiển thị mặc dù Bob không khớp.

Cuối cùng, nếu tôi nhập "Jo S", không có kết quả nào được trả về mặc dù kết hợp từng phần trên "Jo" và "S".

Bất kỳ ai có thể giúp tôi khắc phục truy vấn của mình để xử lý chức năng mong muốn của đơn đặt hàng không phải là kết quả quan trọng và một phần phù hợp không? Nếu nó có thể được sắp xếp theo các kết quả phù hợp nhất (tức là phần dài nhất của từ, bắt đầu từ chữ cái đầu tiên không chỉ là một phần ở giữa, ở số cột cao nhất), đó cũng sẽ là một trợ giúp lớn.

UPDATE:

Chỉ muốn gửi mã thức mà làm việc dựa trên các giải pháp. Vòng lặp của tôi tạo nhiều câu lệnh trùng khớp không chính xác như là ft_min_word_len của tôi.

Mã của tôi bây giờ là:

$searchArray = explode(" ", $searchVal); 
$query="SELECT * FROM users WHERE MATCH (`first_name`, `last_name`, `email`) AGAINST ('"; 
$i=0; 
foreach ($searchArray as $word) { 
    $query .= "+".$word."* "; 
} 
$query .= "' IN BOOLEAN MODE)"; 

Trả lời

10

Trong chế độ boolean, đòi hỏi chuỗi có mặt (thay vì chỉ ghi cao hơn), được thực hiện với +. kết hợp tiền tố được thực hiện với một kết thúc *. Điều này có vẻ là những gì bạn muốn, vì vậy hãy tìm kiếm:

+John* +S* 
+John* +Smith* 
+Smith* +J* 
+Jo* +S* 

Lưu ý rằng chỉ mục Toàn văn không thể giúp bạn tìm kiếm 'ở bất kỳ đâu trong một từ'. do đó, một cái gì đó như *mith* bị ràng buộc không thành công: chúng có nghĩa là khớp với ký tự 1 trong chỉ mục.

Nếu bạn cũng muốn đặt chúng bằng giá trị trận đấu, và ví dụ, cần John SmithtrướcJohnny Smithson, bạn muốn làm điều này:

SELECT * FROM user 
WHERE MATCH(..fields..) AGAINST ('match' IN BOOLEAN MODE) 
ORDER BY MATCH(..fields..) AGAINST ('match' IN BOOLEAN MODE) DESC; 

Mà bạn sẽ thấy sẽ giúp bạn có được đâu trừ khi bạn thêm tất cả các từ> = ft_min_word_len lại riêng biệt:

+John* +S* John 
+John* +Smith* John Smith 
+Smith* +J* Smith 
+Jo* +S* 

Đối với người cuối cùng, cả hai đều < 4 ký tự mặc định, vì vậy chúng tôi không thể thêm sắp xếp p arams cho rằng trong mysql mặc định, nhưng bạn có thể thiết lập ft_min_world_len khác nhau là mong muốn.

+0

Dường như vẫn có vấn đề với thứ tự các điều khoản. Tôi đã thêm Pablo Picasso vào DB để lấy tên dài hơn để thử nghiệm.Thuật ngữ "Pablo Pica" trả về kết quả. "Pica Pablo" thì không. Tôi có nên chuyển toàn bộ các cụm từ với dấu + và dấu * vào một câu lệnh MATCH duy nhất hoặc thực hiện nhiều câu lệnh MATCH với một OR như tôi có ở trên không? – Max

+0

@Max: Tôi không thể tạo lại điều này, cả 'Pica Pablo' là' Pablo Pica' (hoặc '+ Pica * + Pablo *'/'+ Pablo * + Pica *') trả về cùng một người dùng cho tôi. Có, trong một câu lệnh 'MATCH() AGAINST()' duy nhất. Mỗi thuật ngữ đơn lẻ trong 'MATCH()' rất có thể sẽ yêu cầu định dạng '+ term *'. Do đọc bình luận của @ PatrickB mặc dù: tên <4 ký tự sẽ không bao giờ khớp. – Wrikken

+0

Cảm ơn! Sự kết hợp của ft_min_word_len và thay đổi nó thành một câu lệnh MATCH đã sửa nó. Tôi sẽ cập nhật câu hỏi bằng mã cuối cùng của mình. – Max

2

IN BOOLEAN MODE bạn có thể sử dụng + -modifier để buộc AND hoặc - -modifier để buộc NOT. Không có nhà điều hành, trường hợp của bạn, có nghĩa là tùy chọn.

Và bạn cần phải kiểm tra độ dài từ tối thiểu trong cấu hình mysql của mình để làm cho các từ chỉ mục FULLTEXT INDEX nhỏ hơn một độ dài nhất định.

tôi đã phải thiết lập

ft_min_word_len = 2 

trong my.cnf và phải xây dựng lại các chỉ số để làm cho điều này có hiệu quả. Theo mặc định nó là 3.

Để tìm hiểu kiểm tra của bạn min_word_len (và phiếu bầu tán thành) this question

+1

Trường 'ft_min_word_len' không dành cho truy vấn MATCH, nó là dành cho chỉ mục đã được tạo và truy vấn hiện đang khớp với. Vì vậy, một người được gọi là Jo Smith sẽ không khớp với một trận đấu '+ Jo *'. –

+0

Đã xóa nhận xét đầu tiên của tôi (vì nó sai, boolean mode _does_ sử dụng độ dài từ min, lời xin lỗi của tôi cho điều đó). – Wrikken

2

Xem http://dev.mysql.com/doc/refman/5.5/en//fulltext-boolean.html

Bạn có thể đặt một "+", "-", hoặc không điều hành trước một từ để làm cho nó tìm kiếm cho "VÀ chứa từ này", "KHÔNG chứa từ này", và không điều hành là "HOẶC chứa từ này"

Nếu tôi nhập "John S", chỉ kết quả đầu tiên cho thấy hành vi mong muốn là gì.

Chỉ có một John, vì vậy công trình này, S nằm dưới độ dài từ tối thiểu và được loại bỏ

Nếu tôi gõ "John Smith", chỉ cho thấy kết quả đầu tiên là hành vi mong muốn .

Chỉ có một John để làm việc này

Nếu tôi gõ "Smith J", cả hai kết quả cho thấy mặc dù Bob không phải là một trận đấu.

J nằm dưới độ dài từ tối thiểu, vì vậy nó chỉ phù hợp với smith mà là cả hai hàng

Nếu tôi gõ "Smith John", cả hai kết quả cho thấy mặc dù Bob không phải là một trận đấu.

Vì bạn đang ở chế độ BOOLEAN MODE MySQL diễn giải điều này là Smith HOẶC John ... Smith khớp cả hai.

Cuối cùng, nếu tôi nhập "Jo S", không có kết quả nào được trả về mặc dù kết hợp từng phần trên "Jo" và "S".

Jo và S dưới độ dài từ tối thiểu - Tôi tin rằng MySQL đối xử này như tìm kiếm gì

Bạn sẽ muốn thêm một "+" trước khi các thông số tìm kiếm của bạn để biến chúng thành một VÀ tìm kiếm ... +Smith +John

+0

Dường như vẫn có vấn đề với thứ tự các điều khoản. Tôi đã thêm Pablo Picasso vào DB. Thuật ngữ "Pablo Pica" trả về kết quả. "Pica Pablo" thì không. Tôi có nên chuyển toàn bộ các cụm từ với dấu + và dấu * vào một câu lệnh MATCH duy nhất hoặc thực hiện nhiều câu lệnh MATCH với một OR như tôi có ở trên không? – Max