Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/75.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
PostgreSQL中的全文搜索,根据每个关键字的不同分数进行自定义排名_Sql_Postgresql_Search_Full Text Search - Fatal编程技术网

PostgreSQL中的全文搜索,根据每个关键字的不同分数进行自定义排名

PostgreSQL中的全文搜索,根据每个关键字的不同分数进行自定义排名,sql,postgresql,search,full-text-search,Sql,Postgresql,Search,Full Text Search,有没有办法在PostgreSQL中“扩展”ts_rank函数或创建自定义的设置权重 我有两个表,记录和标记,记录可以有多个标记。它是使用表记录\u标记的多对多关联records\u tags有一列score,这意味着同一个标签的得分对于每个记录都不同,并且PostgreSQL的setweight中的权重级别不止4个 简化数据示例: 记录表格 id | title | description | privacy

有没有办法在PostgreSQL中“扩展”ts_rank函数或创建自定义的设置权重

我有两个表,
记录
标记
记录
可以有多个标记。它是使用表
记录\u标记的多对多关联
records\u tags
有一列
score
,这意味着同一个标签的得分对于每个记录都不同,并且PostgreSQL的setweight中的权重级别不止4个

简化数据示例:

记录
表格

 id |         title          |             description              | privacy
----+------------------------+--------------------------------------+---------
  1 | 'The best record ever' | 'Long and meaningful description...' |       1
  2 |       'Another record' | 'Description of the other record...' |       2
 id |           name
----+---------------------------
  1 | 'artificial intelligence'
  2 |        'machine learning'
  3 |            'life science'
标签
表格

 id |         title          |             description              | privacy
----+------------------------+--------------------------------------+---------
  1 | 'The best record ever' | 'Long and meaningful description...' |       1
  2 |       'Another record' | 'Description of the other record...' |       2
 id |           name
----+---------------------------
  1 | 'artificial intelligence'
  2 |        'machine learning'
  3 |            'life science'
记录\u标签
表格

 record_id | tag_id | score
-----------+--------+-------
         1 |      1 |    87
         1 |      2 |    23
         2 |      1 |    54
         2 |      2 |    67
         2 |      3 |    90
这些表中的数据合并到另一个表
search\u documents
,该表的列
body
为jsonb类型,并包含每个记录的聚合标记名

search\u documents.body
如下所示:

{
  title: 'The best record ever',
  description: 'Long and meaningful description...',
  tags: ['artificial intelligence', 'machine learning']
}
setweight(to_tsvector('simple', (body ->> 'tags')), 'A') || ' ' ||
setweight(to_tsvector('english', (body ->> 'title')), 'B') || ' ' ||
setweight(to_tsvector('english', (body ->> 'description')), 'C')
SELECT
  ts_rank(sd.tsv, to_tsquery('english', ''' ' || :query || ' ''' || ':*'), 1) +
  ts_rank(sd.tsv, to_tsquery('simple', ''' ' || :query || ' ''' || ':*'), 1) AS rank
  sd.id AS id
FROM
  search_documents sd
WHERE
  sd.tsv @@ to_tsquery('english', ''' ' || :query || ' ''' || ':*') OR
  sd.tsv @@ to_tsquery('simple', ''' ' || :query || ' ''' || ':*')
IF NEW.searchable_type = 'record' THEN
  NEW.tsv := (
    setweight(to_tsvector('simple', (NEW.body ->> 'tags')), 'A') || ' ' ||
    setweight(to_tsvector('english', (NEW.body ->> 'title')), 'B') || ' ' ||
    setweight(to_tsvector('english', (NEW.body ->> 'description')), 'C')
  )::tsvector;
ELSE
  NEW.tsv := (
    setweight(to_tsvector('simple', (NEW.body ->> 'name')), 'A') || ' ' ||
    setweight(to_tsvector('english', (NEW.body ->> 'description')), 'C')
  )::tsvector;
END IF;
return NEW;
现在,我已经使用tsvector和setweight实现了全文搜索,如下所示:

{
  title: 'The best record ever',
  description: 'Long and meaningful description...',
  tags: ['artificial intelligence', 'machine learning']
}
setweight(to_tsvector('simple', (body ->> 'tags')), 'A') || ' ' ||
setweight(to_tsvector('english', (body ->> 'title')), 'B') || ' ' ||
setweight(to_tsvector('english', (body ->> 'description')), 'C')
SELECT
  ts_rank(sd.tsv, to_tsquery('english', ''' ' || :query || ' ''' || ':*'), 1) +
  ts_rank(sd.tsv, to_tsquery('simple', ''' ' || :query || ' ''' || ':*'), 1) AS rank
  sd.id AS id
FROM
  search_documents sd
WHERE
  sd.tsv @@ to_tsquery('english', ''' ' || :query || ' ''' || ':*') OR
  sd.tsv @@ to_tsquery('simple', ''' ' || :query || ' ''' || ':*')
IF NEW.searchable_type = 'record' THEN
  NEW.tsv := (
    setweight(to_tsvector('simple', (NEW.body ->> 'tags')), 'A') || ' ' ||
    setweight(to_tsvector('english', (NEW.body ->> 'title')), 'B') || ' ' ||
    setweight(to_tsvector('english', (NEW.body ->> 'description')), 'C')
  )::tsvector;
ELSE
  NEW.tsv := (
    setweight(to_tsvector('simple', (NEW.body ->> 'name')), 'A') || ' ' ||
    setweight(to_tsvector('english', (NEW.body ->> 'description')), 'C')
  )::tsvector;
END IF;
return NEW;
并按如下方式搜索查询:

{
  title: 'The best record ever',
  description: 'Long and meaningful description...',
  tags: ['artificial intelligence', 'machine learning']
}
setweight(to_tsvector('simple', (body ->> 'tags')), 'A') || ' ' ||
setweight(to_tsvector('english', (body ->> 'title')), 'B') || ' ' ||
setweight(to_tsvector('english', (body ->> 'description')), 'C')
SELECT
  ts_rank(sd.tsv, to_tsquery('english', ''' ' || :query || ' ''' || ':*'), 1) +
  ts_rank(sd.tsv, to_tsquery('simple', ''' ' || :query || ' ''' || ':*'), 1) AS rank
  sd.id AS id
FROM
  search_documents sd
WHERE
  sd.tsv @@ to_tsquery('english', ''' ' || :query || ' ''' || ':*') OR
  sd.tsv @@ to_tsquery('simple', ''' ' || :query || ' ''' || ':*')
IF NEW.searchable_type = 'record' THEN
  NEW.tsv := (
    setweight(to_tsvector('simple', (NEW.body ->> 'tags')), 'A') || ' ' ||
    setweight(to_tsvector('english', (NEW.body ->> 'title')), 'B') || ' ' ||
    setweight(to_tsvector('english', (NEW.body ->> 'description')), 'C')
  )::tsvector;
ELSE
  NEW.tsv := (
    setweight(to_tsvector('simple', (NEW.body ->> 'name')), 'A') || ' ' ||
    setweight(to_tsvector('english', (NEW.body ->> 'description')), 'C')
  )::tsvector;
END IF;
return NEW;
但它根本不允许我使用标签的分数

我的想法是,我有一个用于标记分数的标准化函数,它返回0到1范围内的分数,并使用该函数乘以a权重。它看起来像这样-
(x_i− 最小(x))/(最大(x)− 最小值(x))

在计算
等级
时,除了当前实现之外,还有什么方法可以使用标记的
分数

编辑:

search\u documents
是一个表,不是物化视图,在搜索过程开始时,它拥有(或将拥有)所有数据。它包含我要搜索的所有项目,不仅包括
记录
,还包括其他项目-
帐户
扬声器
。更新源表时,
搜索文档也会更新。还有隐私栏,因为每个用户都有不同的权限,我不希望他们在无法访问搜索结果时看到搜索结果中的项目

搜索\u文档
表格示例:

 tsv | searchable_id | searchable_type | privacy | body
-----+---------------+-----------------+---------+-----------------------------------------------------
 ... |             1 |        'record' |       1 | { title: '...', description: '...', tags: ['...'] }
 ... |             1 |       'account' |       1 | { name: '...', description: '...' }
 ... |             1 |       'speaker' |       1 | { name: '...', description: '...' }
tsv
是在插入/更新表时使用触发器创建的ts_向量。它是这样创建的:

{
  title: 'The best record ever',
  description: 'Long and meaningful description...',
  tags: ['artificial intelligence', 'machine learning']
}
setweight(to_tsvector('simple', (body ->> 'tags')), 'A') || ' ' ||
setweight(to_tsvector('english', (body ->> 'title')), 'B') || ' ' ||
setweight(to_tsvector('english', (body ->> 'description')), 'C')
SELECT
  ts_rank(sd.tsv, to_tsquery('english', ''' ' || :query || ' ''' || ':*'), 1) +
  ts_rank(sd.tsv, to_tsquery('simple', ''' ' || :query || ' ''' || ':*'), 1) AS rank
  sd.id AS id
FROM
  search_documents sd
WHERE
  sd.tsv @@ to_tsquery('english', ''' ' || :query || ' ''' || ':*') OR
  sd.tsv @@ to_tsquery('simple', ''' ' || :query || ' ''' || ':*')
IF NEW.searchable_type = 'record' THEN
  NEW.tsv := (
    setweight(to_tsvector('simple', (NEW.body ->> 'tags')), 'A') || ' ' ||
    setweight(to_tsvector('english', (NEW.body ->> 'title')), 'B') || ' ' ||
    setweight(to_tsvector('english', (NEW.body ->> 'description')), 'C')
  )::tsvector;
ELSE
  NEW.tsv := (
    setweight(to_tsvector('simple', (NEW.body ->> 'name')), 'A') || ' ' ||
    setweight(to_tsvector('english', (NEW.body ->> 'description')), 'C')
  )::tsvector;
END IF;
return NEW;
这就是在
搜索文档中创建
记录
数据的方式:

SELECT GREATEST(MAX(r.privacy), MAX(f.privacy), MAX(a.privacy)) AS privacy,
  'record' AS searchable_type,
  r.id AS searchable_id,
  json_build_object(
    'tags', array_remove(array_agg(t.name), NULL),
    'title', r.title,
    'description', r.description
  ) AS body
FROM records r
  LEFT JOIN folders f ON r.folder_id = f.id
  LEFT JOIN accounts a ON r.account_id = a.id
  LEFT JOIN records_tags rt ON r.id = rt.record_id
  LEFT JOIN tags t ON rt.tag_id = t.id
WHERE r.id = :id
GROUP BY searchable_id
ON CONFLICT(searchable_type, searchable_id)
  DO UPDATE
    SET privacy = EXCLUDED.privacy,
        body = EXCLUDED.body

您可以使用的三参数形式设置特定词素的权重,而不是为整个tsvector设置相同的所有权重。您可能会将其构建到表“search_documents”的创建过程中,但我不能建议具体的实现,因为您没有向我们展示该创建过程。一旦获得了正确加权的tsvector,将其存储为单独的列,而不是存储在JSONB中可能是有意义的。

您可以使用的3参数形式设置特定词素的权重,而不是为整个tsvector设置相同的所有权重。您可能会将其构建到表“search_documents”的创建过程中,但我不能建议具体的实现,因为您没有向我们展示该创建过程。一旦获得了正确加权的tsvector,将其存储为单独的列,而不是存储在JSONB中可能是有意义的。

实际上,我已经展示了ts_向量的创建过程,并且我已经在setweight函数中使用了权重,但只有4个级别。无论如何,我编辑了这个问题,并添加了数据样本。除非我错放了什么,否则您使用的是2参数形式。您需要使用3参数形式。您没有显示search_documents表(物化视图?)本身的创建,这可能需要更改以适应此情况。正如我在文章中所写,search_documents是一个表。我在问题上增加了结构。我试图在没有其他数据的情况下使问题尽可能简单,因为我认为这些数据并不重要。一切都有点复杂,因为我不仅要搜索记录,还要搜索其他表-帐户和扬声器。事实上,我已经展示了ts_向量创建过程,我已经在setweight函数中使用了权重,但只有4个级别。无论如何,我编辑了这个问题,并添加了数据样本。除非我错放了什么,否则您使用的是2参数形式。您需要使用3参数形式。您没有显示search_documents表(物化视图?)本身的创建,这可能需要更改以适应此情况。正如我在文章中所写,search_documents是一个表。我在问题上增加了结构。我试图在没有其他数据的情况下使问题尽可能简单,因为我认为这些数据并不重要。一切都有点复杂,因为我不仅要搜索记录,还要搜索其他表-帐户和扬声器。
“在计算排名时,除了当前的实现之外,还有什么方法可以使用标记的分数吗?”
我能想到的一个解决方案是修改“排名”作为后处理步骤。在根据文本的ts_rank获得排名后,您可以使用自定义公式使用分数值修改排名。这正是我最终得到的结果,它就像一个符咒一样工作。
“在计算排名时,除了当前实现之外,还有什么方法可以使用标记的得分?”
我能想到的一个解决方案是将“排名”修改为后处理步骤。在你根据文本的ts_等级获得等级后,你可以使用你的自定义公式使用分数值修改等级。这正是我最终得到的结果,它就像一个符咒。