4

一連のネストされたルールを作成できる UI ウィジェットを作成しました。たとえば、次のルールを指定できます。

Match ALL of these rules
  - Document Status == Open
  - Has Tag = 'sales'
  - Has Tag = 'question'
  - Match ANY of these rules
    - Has Tag = 'important'
    - Has Tag = 'high-priority'
    - Has Tag = 'critical-priority'

英語では、これは次のクエリに変換されます。

Find Documents where status = Open AND has tag 'sales' AND has tag 'question' 
    AND has at least one of these tags: 'important', 'high-priority', 'critical-priority'

テーブル構造はこれに似ています。

Documents {id, title, status}
Tags {document_id, tag_value}

ここで、この一連のルールを SQL クエリに変換する必要があります。サブクエリを使用するとかなり簡単に実行できますが、パフォーマンス上の理由からサブクエリは避けたほうがよいでしょう。Documents and tags テーブルには、それぞれ数百万のレコードが含まれる可能性があります。

SELECT 
    d.id
FROM
    Documents d
WHERE
    d.status = 'open'
    AND EXISTS (SELECT * FROM Tags t WHERE t.doc_id = d.id AND t.value = 'sales') 
    AND EXISTS (SELECT * FROM Tags t WHERE t.doc_id = d.id AND t.value = 'question') 
    AND (
        EXISTS (SELECT * FROM Tags t WHERE t.doc_id = d.id AND t.value = 'important')
        OR EXISTS (SELECT * FROM Tags t WHERE t.doc_id = d.id AND t.value = 'high-priority')
        OR EXISTS (SELECT * FROM Tags t WHERE t.doc_id = d.id AND t.value = 'critical-priority')   
    )

このクエリを書き直して、より効率的な結合を使用するにはどうすればよいですか?

最初の 2 つのタグ ルールを INNER 結合として追加できますが、ルール セットの後半部分を処理するにはどうすればよいですか? ドキュメントを表示するためにタグの存在を要求するルールがさらにある場合はどうなるでしょうか?

ルール セットは、その中のすべてまたは任意のルールに一致するように設定でき、理論的には何度もネストできることに注意してください。

この問題に取り組むための一般的な方向性に関するアイデアはありますか?

アップデート:

私は自分のテーブルを最適化し、非常に高速に見えるテーブルをクエリする方法を見つけました (別の問題である一致するレコードの数を数えることは別として)。一度に 100 を超えるドキュメントを選択することはありません。ドキュメント セットが 60 万以下でタグが 200 万以下の場合、このソリューションは結果を 0.02 秒以内に返します。これは以前よりもはるかに優れています。

問題のテーブル...

CREATE TABLE `app_documents` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `account_id` int(11) NOT NULL,
  `status_id` int(11) DEFAULT NULL,
  `subject` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `created` datetime NOT NULL,
  `updated` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `IDX_B91B1DB99B6B5FBA` (`account_id`),
  KEY `IDX_B91B1DB96BF700BD` (`status_id`),
  KEY `created_idx` (`created`),
  KEY `updated_idx` (`updated`),
  CONSTRAINT `FK_B91B1DB96BF700BD` FOREIGN KEY (`status_id`) REFERENCES `app_statuses` (`id`),
  CONSTRAINT `FK_B91B1DB99B6B5FBA` FOREIGN KEY (`account_id`) REFERENCES `app_accounts` (`id`),
) ENGINE=InnoDB AUTO_INCREMENT=500001 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

CREATE TABLE `app_tags` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `value` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`id`),
  KEY `value_idx` (`value`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci


CREATE TABLE `app_documents_tags` (
  `document_id` int(11) NOT NULL,
  `tag_id` int(11) NOT NULL,
  PRIMARY KEY (`document_id`,`tag_id`),
  KEY `IDX_A849587A700047D2` (`document_id`),
  KEY `IDX_A849587ABAD26311` (`tag_id`),
  CONSTRAINT `FK_A849587ABAD26311` FOREIGN KEY (`tag_id`) REFERENCES `app_tags` (`id`) ON DELETE CASCADE,
  CONSTRAINT `FK_A849587A700047D2` FOREIGN KEY (`document_id`) REFERENCES `app_documents` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

そして、私がテストしていたクエリ...

このクエリは、"blue" と "green" の両方のタグを持ち、"red" を持たないすべてのドキュメントとそのタグを検索します。

SELECT
    d.*
FROM 
    app_documents d
LEFT JOIN
    app_documents_tags dtg ON ttg.document_id = d.id
LEFT JOIN
    app_tags tg ON tg.id = dtg.tag_id
WHERE
    d.account_id = 1
    AND EXISTS (
        SELECT
            *
        FROM 
            app_tags t1 
        CROSS JOIN 
            app_tags t2
        CROSS JOIN
            app_tags t3
        INNER JOIN
            app_documents_tags dtg1 ON t1.id = ttg1.tag_id
        INNER JOIN
            app_documents_tags dtg2 ON dtg1.ticket_id = dtg2.ticket_id AND dtg2.tag_id = t2.id
        LEFT JOIN
            app_documents_tags dtg3 ON dtg2.ticket_id = dtg3.ticket_id AND dtg3.tag_id = t3.id
        WHERE
            t1.value = 'blue' AND t2.value = 'green' AND t3.value = 'red' AND dtg3.ticket_id IS NULL AND dtg2.document_id = t.id
    )
ORDER BY
    d.created
LIMIT 45

ただし、これはより良いインデックスを使用して改善できると確信しています。

4

5 に答える 5

1

質問からのクエリを次のようにフォーラム化します。

  • 販売タグと質問タグの両方を持つドキュメントIDを収集します(サブクエリAA)
  • タグの1つ(重要、高優先度、重要優先度)を持つドキュメントIDを収集します(サブクエリBB)
  • AAとBBをマージすると、サブクエリDocsWithValidTagRulesが得られます
  • DocsWithValidTagRulesをDocumentsテーブルに結合して、開いた状態にします
  • あなたのページネーションを実行します

その説明が与えられると、結果のクエリは次のようになります。

SELECT Documents.id
FROM
(
    SELECT AA.document_id
    (
        SELECT B.document_id,COUNT(1) tagcount FROM
        (
            SELECT id FROM app_tags
            WHERE `value` IN ('sales','question')
        ) A
        INNER JOIN app_documents_tags B
        ON A.id = B.tag_id
        GROUP BY B.document_id
        HAVING COUNT(1) = 2
    ) AA
    INNER JOIN
    (
        SELECT B.document_id,COUNT(1) tagcount FROM
        (
            SELECT id FROM app_tags
            WHERE `value` IN ('important','high-priority','critical-priority')
        ) A
        INNER JOIN app_documents_tags B
        ON A.id = B.tag_id
        GROUP BY B.document_id
    ) BB
) DocsWithValidTagRules
INNER JOIN Documents
ON DocsWithValidTagRules.document_id = Documents.id
WHERE Documents.status = 'open'
LIMIT page_offset,page_size;

ドキュメントにこのインデックスがあることを確認してください

ALTER TABLE Documents ADD INDEX status_id_index (status,id);

試してみる !!!

于 2012-06-14T23:29:03.820 に答える
0

これが私がこの問題に対して行うことです。上記のリレーショナルモデルに加えて、2つの列のみを持つ別のテーブルを作成します"DocumentID"|"MetadataXML"。ドキュメントを作成/更新するときは、各ドキュメントのすべてのメタデータを正確に含むXMLドキュメント(できればスキーマ検証済み)を作成します。次に、XPATH式を使用してドキュメントを検索します。

それは速く、あるいは速くさえ燃えないかもしれません。ただし、このアイデアの最大の利点は、データモデルとインデックスおよびワークフローが安定していることです。今後発生するすべての複雑さは、XMLスキーマによって抽象化されます。

さらに、これに加えてLucene / Solrを実装して、基本的な検索を高速化します。

Fast basic full text search -> Lucene/Solr
Advanced Search -> XML/XPATH expression search
Federated Searches, Rest APIs etc -> SQL 
于 2012-06-13T20:10:26.440 に答える
0

Lucene/Solr を検討しましたか

于 2012-06-13T20:02:21.587 に答える
0

それは純粋なSQLソリューションである必要がありますか?

このようなものでデータセットを絞り込むことができます.1つの結合があり、データを取得する言語を使用して、小さなデータセットをフィルタリングし、適切なロジックを使用します.

SELECT 
    d.id,
    t.value
FROM
    Documents d 
    JOIN Tags t_required ON t.doc_id=d.id
WHERE
    d.status = 'open'
    and t.value IN ('sales', 'question', 'important', 'high-priority', 'critical-priority' )
于 2012-06-08T00:53:02.353 に答える
0

それは猛烈な速さではないかもしれませんし、速くさえないかもしれません。しかし、このアイデアの最大の利点は、データ モデルとインデックス、およびワークフローが安定していることです。今後発生するすべての複雑さは、XML スキーマによって抽象化されます。

さらに、これに加えて Lucene/Solr を実装して、高速な基本検索を提供します。

于 2012-06-14T22:17:02.060 に答える