我有一个包含我正在加入的文档文本blob的表。使用oracle文本,我可以获得包含我的搜索词的文本片段(使用ctx_doc.snippet)。但是,我现在必须指定为与我的联接匹配的每个文档找到此搜索词的次数,而不是我拥有的所有文档。我总共有超过100K的文档,但我加入了,过滤返回了一个子集。
在线阅读,我可以使用CTX_QUERY.COUNT_HITS,但这会给出所有文件的计数。如果我有一个用于COUNT_HITS的textkey paraemeter,生活会很好但不存在。
如何在Oracle中的文档中完成给定查询的点击次数?
答案 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。