最近我一直在寻找XML搜索工具,我编写的应用程序需要定期的搜索一些有关联的XML文件,我本来的意思是为了看一看文件中是否有与我想要的数据匹配的数据,但是有时候,我也想把找到的这些数据输出出来。一开始,我试用了一下XSLT和XPath,想通过把搜索的问题转化成使用XSLT能够解决的问题,但是经过一段时间的试验,我发现,使用XSLT并没有真正解决我想要处理的搜索问题,因为我想要输出的数据是使用逗号隔开的数,而XSLT不能满足这个要求,而且XLST也不能提供全文搜索功能。然后我想尝试一下使用XML查询语言(XQL),来看看能不能解决,所以我仔细的着了一下XQL的各种版本的实现,很巧,正好发现一个叫XQEngine的小工具能解决这个问题,所以,在本文中我想介绍一下如何使用XQEngine来在你的XML文件中搜寻你想要找的字符串数据。
XQEngine可以在www.fatdog.com网站下找到,它是一个JavaBean,使用一个SAX解析器来索引一个或多个XML文档,然后你就可以在这些文档中进行复合式搜索了。它所使用的搜索语言是XQL的超集,与XPath有相似的语法。
使用XQEngine的Java类必须实现一个result()方法,完成搜索后,引擎将调用这个方法把搜索结果传到result()方法中,可以使用三种显示数据的格式来输出数据结果。使用命令行参数指明你所需要的搜索参数,比如说你可以指明一个文件假如含有stop这个词,就不会被索引;又如你可以在参数中命令引擎忽略那些少于指定子数的词。
下面,我给出了一个使用XQEngine的例程,现在让我们来分析一下。首先,main()方法实例化一个搜索引擎:XmlEngine engine = new XmlEngine(),然后它从命令行中取得文件名、返回结果格式和搜索请求这三个参数,再使用各种配置方法来设置引擎,接着调用setSaxParserName()方法来设置SAX解析器的全名,因为我们使用的是Xerces解析器,所以要用到 "org.apache.xerces.parsers.SAXParser"。然后我们就需要设置搜索参数,再本例中,我们将不索引数字或任何少于3个字符的词。在你下载到的XQEngine的API文档当中会有详细的配置参数说明,所以在此我就不细说如何配置参数了,请大家自己参阅相关文档。最后,setDocument()方法指定XQEngine将要索引或搜索的XML文件。当然,如果你想要索引多个文件的话,只需设置几个相应的setDocument()方法就可以了。
从下面的代码中我们还可以看到,XQEngine引擎将用三种不同的格式返回搜索结果:STANDARD、SUMMARY和CSV(使用逗号分开的数值)为了简单起见,我为每种返回结果类型定义了一个数字来代替(1,2,3),然后使用相应的参数调用setListenerType()方法。我将在后面详细介绍每一种返回结果类型。还有个方法printSessionState()用来输出索引和引擎的信息,但是我没有把它写进例程中,所以上面的程序只会输出搜索结果;下一步再调用addXQLResultListener()方法,并传递Search的一个实例,用来实现XQLResultListener的接口;然后再把查询字符串作为一个参数来调用setQuery方法,引擎就会开始执行查询任务。等到查询结束后,引擎调用Search类的result()方法,把查询结果传回,在我提供的例程中,result()方法只是简单的把结果输出出来。
代码:
import java.io.*; import com.fatdog.textEngine.XmlEngine; import com.fatdog.textEngine.exceptions.*; import com.fatdog.textEngine.query.XQLResultListener; public class Search implements XQLResultListener { public static void main( String[] args ) { XmlEngine engine = new XmlEngine(); String searchFile = args[0]; String searchType = args[1]; String query = args[2]; try { file://配置引擎 engine.setSaxParserName( "org.apache.xerces.parsers.SAXParser"); engine.setMinIndexableWordLength( 3 ); engine.setDoIndexNumbers( false ); engine.setDocument( searchFile ); if (searchType.equals("1")) { engine.setListenerType( XmlEngine.STANDARD_LISTENER); } else if (searchType.equals("2")) { engine.setListenerType( XmlEngine.SUMMARY_LISTENER); } else { engine.setListenerType( XmlEngine.CSV_LISTENER); } } catch( MissingOrInvalidSaxParserException e ){ System.out.println( "缺少或不可用的 SAX解析器" ); return; } catch( FileNotFoundException e ) { System.out.println( "不能找到 XML 文件: "); return; } catch( CantParseDocumentException e ) { System.out.println( "不能解析 XML 文件: "); return; } // engine.printSessionStats(); engine.addXQLResultListener( new Search() ); try { engine.setQuery( query ); } catch( InvalidQueryException e ) { System.out.println( "不可用的查询请求: " + e.getMessage() ); return; } } public void results( String xqlResults ) { System.out.println( xqlResults ); } } |
好,我们已经把一个使用XQEngine的程序编写出来了,那么就让我们来运行这段代码,在编译这段代码之前,我们需要下载到XQEngine和SAX解析器。我是从xml.apache.org上下载到Xerces解析器的。我使用的操作系统是Windows 2000 Professional,JDK为1.3版,好,搞定这些以后就跟我来设置CLASSPATH吧,在"环境变量"中修改CLASSPATH,添加"c:\xql\XQEngine.jar;c:\xql\antlr.jar; c:\xerces\xerces.jar"。现在就可以编译代码了,不过为了能够运行程序,我们还需要一个XML文件,我使用了Apache Tomcat里的web.xml文件作为演示。前面我也介绍过了,我们使用1,2,3来分别代替三种返回查询结果格式:
1、使用STANDARD_LISTENER (数字1)和查询项"//welcome-file-list/welcome-file",C:\xql\xql1>java Search web.xml 1 "//welcome-file-list/welcome-file"
Parser.installSaxParser:
<org.apache.xerces.parsers.SAXParser> installed successfully 1: indexing web.xml Query: ( // ( / welcome-file-list welcome-file ) ) 3 hit(s) for file://welcome-file-list/welcome-file <?xml version="1.0"?> <xql:result query="//welcome-file-list/welcome-file" hitCount="3" elemCount="3" docCount="1" xmlns:xql="http://www.fatdog.com/ Standard_Listener.html"> <welcome-file> index.jsp </welcome-file> <welcome-file> index.html </welcome-file> <welcome-file> index.htm </welcome-file> </xql:result> |
上面的例子中,查询项要求找到任何"welcome-file-list"元素的所有的"welcome-file"子元素。请注意,搜索的结果基本上是从原XML文档中摘录出来的,不能够建立搜索结果和原文档之间的关系。SUMMARY_LISTENER(2)返回类型则有些不同,它包括一个"docID"号和一个"elemlx"号,这样就能够把结果和原文档联系起来了。
如下是返回结果的示例:
C:\xql\xql1>java Search web.xml 2 "//welcome-file-list/welcome-file" Parser.installSaxParser: <org.apache.xerces.parsers.SAXParser> installed successfully 1: indexing web.xml Query: ( // ( / welcome-file-list welcome-file ) ) 3 hit(s) for file://welcome-file-list/welcome-file <?xml version="1.0"?> <xql:result query="//welcome-file-list/welcome-file" hitCount="3" elemCount="3" docCount="1" xmlns:xql="http://www.fatdog.com/ Summary_Listener.html"> <welcome-file xql:docID="0" xql:elemIx="270"/> <welcome-file xql:docID="0" xql:elemIx="271"/> <welcome-file xql:docID="0" xql:elemIx="272"/> </xql:result> |
我前面也说过,对于我的应用程序来说,最重要的是返回使用逗号隔开的返回结果,所以CSV_LISTENER(3)就很有用了,它能够返回一个使用使用逗号隔开的结果,如下:
C:\xql\xql1>java Search web.xml 3 "//welcome-file-list/welcome-file" Parser.installSaxParser: <org.apache.xerces.parsers.SAXParser> installed successfully 1: indexing web.xml Query: ( // ( / welcome-file-list welcome-file ) ) 3 hit(s) for file://welcome-file-list/welcome-file 3,3,1,0 0,270,welcome-file 0,271,welcome-file 0,272,welcome-file |
当然,XQEngine还有很多很强大的功能,在此我不可能一一介绍,它所附带的文档中有丰富的源程序和使用方法,你可以对照着自己学习使用,当然,如果你愿意的话你甚至还可以开发出一个GUI程序,文档中就自带了一个基于GUI的搜索程序:SwingQueryDemo,你可以看一看研究研究。