2009-09-02 39 views
5

Tôi chưa bao giờ tìm kiếm từ MYSQL trước đây, nhưng tôi cần phải thực hiện tìm kiếm. Tôi có ba bảng, 'bài viết', 'articles_tags' và 'thẻ'.MySQL Toàn văn boolean tìm kiếm với các thẻ

'bài viết' giữ điều đầu tiên tôi muốn tìm kiếm trên trường 'tiêu đề'.

'articles_tags' là bảng tổng hợp có liên quan 'bài viết' và 'thẻ' với nhau. 'articles_tags' có hai trường: 'articles_id' và 'tag_id'.

'thẻ' giữ điều thứ hai tôi muốn tìm kiếm, trường 'tên'.

Vấn đề của tôi là, tôi cần một cách để tìm kiếm trường 'tiêu đề' và mỗi thẻ liên quan đến bài viết đó ('tags.name') và trả về mức độ liên quan (hoặc sắp xếp theo mức độ phù hợp) cho bài viết.

Điều gì sẽ là một cách hay để thực hiện điều này? Tôi khá chắc chắn rằng nó không thể được thực hiện từ chỉ một truy vấn vì vậy hai truy vấn, và sau đó 'trộn' các mức độ liên quan với nhau, sẽ là ok.

Cảm ơn.

Chỉnh sửa: Quên để nói, nếu tôi có thể cung cấp thêm trọng số cho khớp với thẻ so khớp từ trong tiêu đề, điều đó thật tuyệt vời. Tôi không thực sự yêu cầu bất cứ ai viết điều này, nhưng cho tôi một số hướng. Tôi là một chút của một newbie trong cả PHP và MySQL.

Trả lời

0

vui nó là câu hỏi thứ 3 về khá nhiều cùng một vấn đề tôi thấy trong 2 ngày, hãy kiểm tra hai bài viết này: 1, 2

+0

Tôi nhìn hai nhưng không thấy làm thế nào họ liên quan đến tôi vấn đề. –

+2

Điều thú vị hơn là đây thực sự là một nhận xét chứ không phải là câu trả lời. – TheCarver

0

truy vấn bản demo nhanh này còn xa mới được tối ưu hóa nhưng phải là một điểm khởi đầu tốt

SELECT * FROM 
(SELECT a.id, a.title, 
    MATCH (a.title) AGAINST ('$s_search_term') AS title_score, 
    SUM(MATCH (t.name) AGAINST ('$s_search_term') 
) AS tag_score 
FROM articles AS a 
LEFT JOIN articles_tags AS at 
    ON a.id = at.article_id 
LEFT JOIN tags AS t 
    ON t.id = at.tag_id 
WHERE MATCH (a.title) AGAINST ('$s_search_term') 
    OR MATCH (t.name) AGAINST ('$s_search_term') 
GROUP BY a.id) AS table1 
ORDER BY 2*tag_score + title_score DESC 

Bạn có thể muốn bình thường hóa tag_score bằng cách chia cho COUNT (t.id). Rất tiếc nhưng việc cung cấp truy vấn dễ hơn là giải thích cách thực hiện.

2

Bắt đầu từ câu trả lời được đưa ra bởi @ james.c.funk nhưng thực hiện một số thay đổi.

SELECT a.id, a.title, 
    MATCH (a.title) AGAINST (?) AS relevance 
FROM articles AS a 
LEFT OUTER JOIN (articles_tags AS at 
    JOIN tags AS t ON (t.id = at.tag_id AND t.name = ?)) 
    ON (a.id = at.article_id) 
WHERE MATCH (a.title) AGAINST (? IN BOOLEAN MODE) 
ORDER BY IF(t.name IS NOT NULL, 1.0, relevance) DESC; 

Tôi giả sử bạn muốn khớp với thẻ khớp với chuỗi đầy đủ, thay vì sử dụng tìm kiếm toàn văn.

Cũng sử dụng một bên ngoài bên ngoài tham gia thay vì hai, bởi vì nếu tham gia articles_tags là hài lòng, thì chắc chắn có một thẻ. Đặt so sánh tên thẻ trong điều kiện kết nối thay vì trong mệnh đề WHERE.

Chế độ boolean làm cho MATCH() trả về 1.0 trên một kết quả phù hợp, điều này làm cho nó vô ích như một thước đo mức độ liên quan. Vì vậy, hãy so sánh thêm trong danh sách chọn để tính mức độ liên quan. Giá trị này nằm trong khoảng từ 0,0 đến 1,0. Bây giờ chúng ta có thể làm cho một thẻ phù hợp với sắp xếp cao hơn bằng cách xử lý nó như có sự liên quan của 1.0.

+0

Xin chào Bill. Tôi đã đọc ở một vài nơi sử dụng JOIN với FULLTEXT là xấu, vì nó buộc MySQL chạy một bảng quét đầy đủ và mất hiệu suất có giá trị. Tôi sẽ chạy một bài kiểm tra về điều này ngay bây giờ để xem những gì tôi đọc là sự thật. – TheCarver

+0

@PaparazzoKid, phụ thuộc vào bảng nào được truy cập trước tiên. MySQL tham gia bằng cách sử dụng thuật toán vòng lặp lồng nhau, vì vậy nếu bạn sử dụng FULLTEXT để hạn chế số lượng hàng phù hợp trong bảng đầu tiên, sau đó sử dụng để tra cứu các hàng trong bảng được nối, nó sẽ không thành vấn đề. Nhưng nếu bạn quét bảng bảng khác trước, sau đó sử dụng FULLTEXT trong điều kiện kết nối hoặc thậm chí tệ hơn, hãy sử dụng * cột * của bảng đầu tiên làm mẫu để tìm kiếm trong tìm kiếm FULLTEXT (Tôi không biết nếu nó là thậm chí có thể), sau đó nó sẽ là tốn kém. Bạn có thể phải sử dụng STRAIGHT_JOIN. –

1

Dưới đây là cách tôi đã làm điều này trong quá khứ. Có vẻ chậm, nhưng tôi nghĩ bạn sẽ tìm thấy nó không phải là.

Tôi đã thêm một chút phức tạp để hiển thị những việc khác có thể dễ dàng thực hiện.Trong ví dụ này, một bài viết sẽ nhận được 1 điểm cho một kết quả trùng khớp một phần, 2 điểm cho một kết hợp thẻ một phần, 3 điểm cho một kết hợp thẻ chính xác và 4 điểm cho một kết hợp tiêu đề chính xác. Sau đó, thêm những người lên và sắp xếp theo số điểm.

SELECT 
    a.*, 
    SUM(
    CASE WHEN a.title LIKE '%keyword%' THEN 1 ELSE 0 END 
    + 
    CASE WHEN t.name LIKE '%keyword%' THEN 2 ELSE 0 END 
    + 
    CASE WHEN t.name = 'keyword' THEN 3 ELSE 0 END 
    + 
    CASE WHEN a.title = 'keyword' THEN 4 ELSE END 
) AS score 
FROM article a, articles_tags at, tags t 
WHERE a.id = at.article_id 
AND at.tag_id=t.id 
AND (a.title LIKE '%keyword%' OR t.name LIKE '%keyword%') 
GROUP BY a.id 
ORDER BY score; 

GHI CHÚ: Điều này sẽ không trả lại các bài viết không có thẻ. Tôi đã sử dụng các phép nối đơn giản để giảm nhiễu trong truy vấn và chỉ làm nổi bật những gì đang làm. Để bao gồm các bài viết không có thẻ, chỉ cần thực hiện các phép nối tham gia trái.

2

Có giá trị tại thời điểm này hay không, đề xuất bạn xem xét việc tải công việc tìm kiếm vào một thứ thực sự được viết chỉ vì mục đích đó?

Trong các sản phẩm của mình, chúng tôi sử dụng MySQL để lưu trữ dữ liệu, nhưng lập chỉ mục tất cả dữ liệu của chúng tôi với Lucene (thông qua Solr - nhưng điều đó không liên quan).

Nó đáng để xem xét, vì nó tương đối đơn giản để thiết lập, nó rất mạnh và dễ dàng hơn nhiều so với việc cố gắng thao tác cơ sở dữ liệu để làm những gì bạn muốn.

Xin lỗi đây không phải là một câu trả lời trực tiếp cho câu hỏi, tôi chỉ cảm thấy rằng loại điều luôn luôn là đáng nói trong kịch bản này :)

+1

làm cách nào để giữ cho mysql và lucene được đồng bộ hóa? cám ơn –

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