为了让SharePoint的搜索组件能够检索外部内容源(外部的数据库、业务系统、二进制文件等等等等),通常需要创建一个自定义的Indexing Connector。Indexing Connector是一种基于SharePoint 2010中的Business Connectivity Services和Search Connector Framework的组件,它替代了以前的Protocol Handler,成为了SharePoint 2010(和FAST Search for SharePoint 2010)所支持的主要的外部数据爬网扩展方式。(SharePoint 2010仍然支持自定义的Protocol Handler。)
在通过BCS的方式创建一个Connector之后,可能面临的问题之一,就是需要使用它去爬非常大的数据量。比如,几百万甚至上千万的数据项。如果Connector需要面对这样的挑战,那么就需要小心的设计Connector。
首先,Connector需要能够良好的支持增量爬网。你肯定不希望在增量爬网的速度和完全爬网一样。
Connector可以以两种方式来支持增量爬网:基于最后修改时间(Timestamp-based),和基于修改日志(Changelog-based)。基于最后修改时间也就是你需要指定一个日期时间字段,Crawler会将此字段作为数据项的最后修改时间,当增量爬网时,Crawler会通过比对这个数据,来知道自己是否需要对一个数据项重新进行处理。基于修改日志则是专门通过额外的方法,直接将某个时间之后新增、被修改、被删除的数据项,返回给搜索引擎,这样搜索引擎就直接知道了从上次爬网到现在,有哪些数据项需要重新处理。
如果外部内容源的数据量非常庞大,即使是第一次的完全爬网,也可能导致Crawler不能正常工作,或者导致外部内容源在短时间内承受过大的压力。
首先,慎重考虑是否直接使用一个Finder方法,来一次性的从外部内容源返回所有所需要的数据(类似于select * from db_table操作)。在小数据量的情况下,这样做很方便,但是如果数据量太大,这么做很可能是不合适的。
比较谨慎的做法,是仅仅使用IdEnumerator方法和SpecificFinder方法获取数据。IdEnumerator方法(类似于select id from db_table)可以返回数据项的ID,然后使用这些ID,重复调用SpecificFinder方法(类似于select * from db_table where id=@id),来一个一个的获取数据项。这时,你需要告诉Connector,这个IdEnumerator方法才是整个Entity的RootFinder。在大部分情况下,你甚至不需要去定义一个Finder方法,因为Crawler根本就不应该一次从外部内容源获取过多的数据。
如果数据量超大,甚至IdEnumerator方法的执行可能也是有问题的。想象一下,一次性从外部数据源返回所有几千万个数据项的ID。这个时候就需要更进一步,让IdEnumerator方法每次只返回特定数量(比如1000)的数据项ID。
我们可以为IdEnumerator方法定义一个类型为LastId的筛选器和对应的输入参数(即方向为In的Parameter),这样Crawler会尝试重复调用IdEnumerator方法,每次都将上一次调用后所得到的最后一个ID,作为参数传入。在IdEnumerator方法的实现代码中,就可以根据这个传入的参数,仅从外部内容源检索此ID之后的数据项ID。
Crawler会尝试以下面这种方式,来重复调用IdEnumerator方法,直到它返回0个结果项为止。(每次调用返回多少数据项,只取决于我们在IdEnumerator方法的实现代码里面返回了多少项。)
搜索引擎的Crawler有可能会多次调用SpecificFinder方法来获取同一个数据项(具体原因未知...),所以在Connector里面,可以考虑使用一些cache的技巧,来减少向外部内容源请求数据的次数。如果内容源的数据量很大,将所有数据都一股脑保存到内存里面并不是一个好主意。可以考虑使用HttpRuntime.Cache,虽然看起来它仅能用于Web程序,但实际上只需要在程序中引用System.Web这个程序集,在你的Connector里面使用它是没有问题的(注:微软MSDN文档中注明了它在非ASP.NET程序中可能无法正常工作,但似乎并不存在无法正常工作的情况,但我并不担保...)。这个Cache的实现已经内置了诸如自动清除、优先级设置、缓存时间设置等等功能,比自己写一个要方便很多。另外EntLib里面也有一个可以用于任意类型程序的Cache实现。
除了上面所说的这些之外,Eric Wang还贡献过一个idea。那就是,(按照他的观点,)一次性获取大量数据是没有问题的,如果真的碰到超大规模的数据,可以在搜索管理中创建多个内容源,每个内容源针对外部数据的一部分进行爬网。比如,如果外部数据有200万项,可以考虑创建4个内容源,每个内容源根据某种划分规则,获取并爬网其中的50万项数据。