您可以继续使用 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 几乎肯定会存储文档的字节长度。
如果您不想在每次运行查询时解析文档,则可能值得在加载/更新数据时执行一次运行,并分别存储每个文档的单词和出现次数。