【问题标题】:PostgreSQL: Full text search multitenant site, plus only parts of sitePostgreSQL:全文搜索多租户站点,仅加上部分站点
【发布时间】:2013-07-13 14:35:48
【问题描述】:

我正在开发一个多租户 Web 应用程序,我想添加全文搜索,以便人们能够:

1) 仅搜索他们当前正在访问的网站(但不是所有网站),并且
2) 仅搜索该网站的一部分(例如,将搜索限制为网站上的博客或论坛),并且
3) 仅搜索单个论坛主题。

我想知道我应该添加哪些索引?

请假设数据库很大(例如,按站点 ID 进行索引扫描,然后按全文搜索进行过滤太慢了)。

我能想到三种方法:

  1. 创建三个索引。 1) 一个基于每个站点索引所有内容的索引。 和 2) 一个在每个站点加上站点部分的基础上索引所有内容的索引。 3) 一个基于每个站点和页面 ID 索引所有内容的索引。

  2. 创建一个单一的索引,并插入到 [要索引的文本] 中,如: “站点_” 和“section_”和“page_”,然后当我搜索 对于站点 YYY 中的第 XX 部分,我可以像这样为搜索查询添加前缀: “site_XX AND section_YYY AND ...”。

  3. 在创建新站点或站点部分时动态添加数据库索引:

     create index dw1_posts__search_site_YYY
       on dw1_posts using gin(to_tsvector('english', approved_text))
       where site_id = 'YYY';
    

以上这三种方法中的任何一种都有意义吗?有更好的选择吗?


(详情:但是,也许方法 1 是不可能的?尝试同时索引列和索引全文搜索,会导致语法错误:

> create index dw1_posts__search_site
    on dw1_posts (site_id)
    using gin(to_tsvector('english', approved_text));
ERROR:  syntax error at or near "using"
LINE 1: ...dex dw1_posts__search_site on dw1_posts(site_id) using gin(...
                                                             ^

> create index dw1_posts__search_site
    on dw1_posts
    using gin(to_tsvector('english', approved_text))
    (site_id);
ERROR:  syntax error at or near "("
LINE 1: ... using gin(to_tsvector('english', approved_text)) (site_id);

(如果方法 1 可能的,那么我可以进行如下查询:

select ... from ... where site_id = ... and <full-text-search-column> @@ <query>;

并让 PostgreSQL 首先检查 site_id,然后使用一个索引检查全文搜索列。) )
/ 结束详情。)


一周后更新:我改用ElasticSearch。我的印象是,对于分面搜索,关系数据库/PostgreSQL 不存在可扩展的解决方案。与 ElasticSearch 集成似乎与实施、测试和调整此处建议的方法一样简单。 (例如,PostgreSQL 的词干分析器/它的名称可能会将“section_NNN”拆分为两个词:“section”和“NNN”,从而索引页面上不存在的词!修复这些烦人的小问题很棘手。)

【问题讨论】:

    标签: postgresql full-text-search categories faceted-search


    【解决方案1】:

    通常的方法是创建:

    • 一个全文索引:

      创建索引 idx1 ON dw1_posts 使用 gin(to_tsvector('english',approved_text));

    • site_id 上的简单索引:

      创建索引 idx2 在 dw1_posts(page_id);

    • page_id 上的另一个简单索引:

      创建索引 idx3 在 dw1_posts(site_id);

    然后,SQL 规划器的工作就是根据查询和列中值的分布来决定使用哪些(如果有)以及使用的顺序。在您真正目睹缓慢的查询之前,试图智取规划器是没有意义的。

    【讨论】:

    • 谢谢,这是一个好方法,只要数据库中的页面不多。而且(我说得对吗?)当页面确实很多时它会崩溃:Postgres 会执行以下任一操作:1)全文搜索并按页面/站点 ID 过滤,或 2)顺序索引扫描,并按全文搜索过滤。 - 我将更新问题以澄清我正在寻找如果数据库中确实有很多页面也可以工作的东西。
    • 如果行数太大,你可以实现partitioning。在这种情况下,索引对于每个分区都是本地的。但是您的问题集中在将 FTS 与其他索引结合起来,而分区是一个更笼统的主题。
    • 分区可能是一个好方法。就我而言,我认为设置和测试需要相当多的工作。另外,既然是DDL,我想知道它如何与Play Framework's automatic database evolutions module一起工作。
    • 分区不能很好地扩展,如果数据库变得非常大——来自the docs“所有分区上的所有约束......在约束排除期间被检查,所以大量的分区可能会显着增加查询计划时间。分区......最多可以处理一百个分区;不要尝试使用数千个分区。"
    【解决方案2】:

    另一种替代方法,类似于“site_”和“section_”和“page_”替代方法,应该将文本添加到 index 与:

    SiteSectionPage_<site-id>_<section-id>_<subsection-id>_<page-id>
    

    然后搜索时使用prefix matching (i.e. :*)

    select ... from .. where .. @@ 'SiteSectionPage_NN_MMM:* AND (the search phrase)'
    

    其中 NN 是站点 ID,MMM 是部分 ID。

    但这不适用于中文?我认为在索引中文时使用三元组是合适的,但是SiteSectionPage...会被拆分为:Sit,ite,teS,eSe,这没有任何意义。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-12-15
      • 1970-01-01
      • 2011-08-18
      • 2011-01-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多