计算Oracle中每个文档的给定搜索查询/术语的命中数

时间:2014-07-09 17:23:07

标签: sql oracle plsql oracle11g oracle-sqldeveloper

我有一个包含我正在加入的文档文本blob的表。使用oracle文本,我可以获得包含我的搜索词的文本片段(使用ctx_doc.snippet)。但是,我现在必须指定为与我的联接匹配的每个文档找到此搜索词的次数,而不是我拥有的所有文档。我总共有超过100K的文档,但我加入了,过滤返回了一个子集。

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

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

3 个答案:

答案 0 :(得分: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的OUT参数为HIGHLIGHT_TAB类型,其中包含文档中点击次数的计数。

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几乎肯定会存储文档的字节长度。

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

答案 1 :(得分:0)

如果通过“blob of document text”你的意思是“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;

答案 2 :(得分:-1)

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