【问题标题】:Counting the number of hits for a given search query/term per document in Oracle计算 Oracle 中每个文档的给定搜索查询/术语的命中数
【发布时间】:2014-08-30 20:33:10
【问题描述】:

我有一个表,其中包含要加入的文档文本块。使用 oracle 文本,我可以获得包含我的搜索词的文本的 sn-p(使用 ctx_doc.sn-p)。但是,我现在必须指定为每个与我的连接匹配的文档而不是我拥有的所有文档找到此搜索词的次数。我总共有超过 10 万个文档,但我加入并过滤返回一个子集。

在线阅读,我可以使用 CTX_QUERY.COUNT_HITS ,但它给出了所有文档的计数。如果我有一个 COUNT_HITS 的 textkey 参数,生活会很好,但不存在。

如何在 Oracle 的文档中完成给定查询的命中次数?

【问题讨论】:

  • 你能给出一些代码吗?
  • SELECT CTX_DOC.SNIPPET('COSI.DOC_BLOB_CONTENT_IDX', 625660, 'NOTIFICATION LETTER') FROM DUAL;
  • 上面的代码目前是我如何从数据库中获取给定搜索词的文档 sn-p。我尝试使用 CTX_DOC.HIGHLIGHT 过程,但它需要我浏览每个文档并计算命中数,坦率地说,这在计算上是昂贵的。 link

标签: sql oracle plsql oracle11g oracle-sqldeveloper


【解决方案1】:

您可以继续使用 CTX_DOC;过程HIGHLIGHT 可以稍微扭曲以完全按照您的要求进行。

使用这个环境:

create table docs ( id number, text clob, primary key (id) );

Table created.

insert all
 into docs values (1, to_clob('a dog and a dog'))
 into docs values (2, to_clob('a dog and a cat'))
 into docs values (3, to_clob('just a cat'))
select * from dual;

3 rows created.

create index i_text_docs on docs(text) indextype is ctxsys.context;

Index created.

CTX_DOC.HIGHLIGHT 有一个 HIGHLIGHT_TAB 类型的 OUT 参数,其中包含文档中的命中数。

declare
   l_highlight ctx_doc.highlight_tab;
begin
  ctx_doc.set_key_type('PRIMARY_KEY');

  for i in ( select * from docs where contains(text, 'dog') > 0 ) loop
     ctx_doc.highlight('I_TEXT_DOCS', i.id, 'dog', l_highlight);
     dbms_output.put_line('id: ' || i.id || ' hits: ' || l_highlight.count);
  end loop;

end;
/
id: 1 hits: 2
id: 2 hits: 1

PL/SQL procedure successfully completed.

显然,如果您在查询中执行此操作,那么过程并不是世界上最好的东西,但您可以根据需要将其包装在函数中:

create or replace function docs_count (
        Pid in docs.id%type, Ptext in varchar2
         ) return integer is

   l_highlight ctx_doc.highlight_tab;
begin
  ctx_doc.set_key_type('PRIMARY_KEY');
  ctx_doc.highlight('I_TEXT_DOCS', Pid, Ptext, l_highlight);
  return l_highlight.count;
end;

这样就可以正常调用了

select id
     , to_char(text) as text
     , docs_count(id, 'dog') as dogs
     , docs_count(id, 'cat') as cats
  from docs;

        ID TEXT                  DOGS       CATS
---------- --------------- ---------- ----------
         1 a dog and a dog          2          0
         2 a dog and a cat          1          1
         3 just a cat               0          1

如果可能,按照 Gordon 的说明替换关键字可能会更简单。我会使用DBMS_LOB.GETLENGTH() 函数而不是简单的LENGTH() 来避免潜在的问题,但REPLACE() 适用于CLOB,所以这不会成为问题。类似于以下内容(假设我们仍在寻找狗)

select (dbms_lob.getlength(text) - dbms_lob.getlength(replace(text, 'dog')))
         / length('dog')
  from docs

值得注意的是,随着字符串变大,字符串搜索会逐渐变慢(因此需要文本索引),因此虽然这在小示例上表现良好,但在较大文档上可能会遇到性能问题。


我刚刚看到your comment

...但这需要我浏览每个文档并计算点击次数,坦率地说,这在计算上是昂贵的

无论您做什么,您都必须浏览每个文档。您想在另一个字符串中找到一个字符串实例的确切数量,而 only 的方法是查看整个字符串。 (我强烈推荐阅读Joel's post on strings;它对 XML 和关系数据库提出了观点,但我认为它也很适合这里。)如果您正在寻找一个估计值,您可以计算一个单词在前 100 个中出现的次数字符,然后在 LOB 的长度上对其进行平均(我知道的废话算法),但你想要准确。

显然我们不知道 Oracle 是如何在内部实现所有功能的,但让我们做一些假设。要计算字符串的长度,您需要逐字计算其中的字节数。这意味着迭代整个字符串。 There are some algorithms to improve this,但它们仍然涉及迭代字符串。如果你想用另一个字符串替换一个字符串,你必须遍历原始字符串,寻找你想要替换的字符串。

理论上,根据 Oracle 实现所有内容的方式,使用 CTX_DOC.HIGHLIGHT 应该比其他任何方法都快,因为它只需遍历原始字符串一次,查找您要查找的字符串并存储字节/字符偏移量原始字符串的开头。

建议length(replace(<original string>, <new string>)) - length(<original string) 可能必须对原始字符串(或长度接近的字符串)进行三次单独的迭代。我怀疑它是否真的会这样做,因为所有内容都可以被缓存,并且 Oracle 应该存储字节长度以使LENGTH() 高效。这就是我建议使用DBMS_LOB.GETLENGTH 而不仅仅是LENGTH() 的原因; Oracle 几乎肯定会存储文档的字节长度。

如果您不想在每次运行查询时解析文档,则可能值得在加载/更新数据时执行一次运行,并分别存储每个文档的单词和出现次数。

【讨论】:

  • 感谢您的反馈!做一个离线方法可能是理想的,但我希望 Oracle 实现了一些可以与他们的文档 sn-p 调用一起工作的东西。这样一来,当返回 sn-ps 时,您可以一次性获得文本中搜索词的数量。
  • 我试图真正避免在用户使用应用程序时需要时间的另一个此类调用。目前,由于性能问题,我已经能够消除需求
  • 当你得到 sn-p 时,你可以写一些东西来得到一切;但我怀疑这会减慢 sn-p 搜索速度,因为一旦找到它正在寻找的内容,它就会停止遍历文档。如果事先不知道搜索词并且您无法足够快地进行完整搜索,您是否考虑过在您的应用程序中异步进行?
  • 抱歉回复晚了。是的,这会减慢 sn-p 搜索。如果通过应用程序,您的意思是在结果中异步查找搜索词或调用数据库来执行此操作?我有一个必须坚持的时间;搜索时间不得超过 5 分钟。现在排除命中数,我几乎每次搜索都需要一分钟,因为我还必须搜索共享点文档库(它们很大)。所以任何会成为性能成本的东西,我都在努力消除
【解决方案2】:

如果“文档文本的blob”是指“clob”,那么您可以使用这种尝试过的真实方法。获取文档长度与将搜索字符串替换为其他内容的文档长度之间的差异。这会给你匹配的数量。

例如:

select t.*
from (select t.*,
             length(replace(t.doc, KEYWORD, KEYWORD || 'x')) - length(t.doc) as nummatches
      from table t
     ) t
order by nummatches desc;

【讨论】:

    【解决方案3】:

    当然,您可以直接在 pl/sql 上处理该问题(解释会帮助您),但是快速而肮脏,动态创建一个视图(过滤到您的子集的视图)并执行 count_hits。

    【讨论】:

    • 你能进一步解释一下吗?这将如何与 oracle 中的文档 sn-p 一起使用。我需要计算给定搜索词或其变体在文档中存在的次数。文档可以从 1 页到 1000 页不等,而且这个计数算法需要超快
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-12
    • 1970-01-01
    • 2012-09-09
    • 2021-10-30
    相关资源
    最近更新 更多