托管元数据(1)——隐藏列表以及一些相关问题
托管元数据服务(Managed Metadata Service)是SharePoint 2010新加入的一个服务(其实我们在2007上自己做过一个类似的自定义字段),关于这个服务的介绍应该就不用再多说了,关于编程访问托管元数据、托管元数据字段的方法也有很多博客都介绍过了(包括中文的),也不再废话了。
这篇Blog主要是介绍一下托管元数据背后的隐藏列表,以及因为这个隐藏列表所带来的一些问题。
如果我们去看一下托管元数据字段类型(TaxonomyField)的继承关系,我们会发现TaxonomyField是继承LookupField的,换句话说,托管元数据在本质上是个查阅项,它查阅的是网站集根网站上,一个名字叫TaxonomyHiddenList的隐藏列表(没错,查阅项是可以跨网站查阅的,从2007的时候就可以,只不过微软没有开放设置界面而已,用代码可以创建出来跨网站的查阅项)。
这个隐藏列表保存了所有在网站集中正在使用以及使用过的所有托管元数据的值(包括企业关键字,这也是个托管元数据类型的字段),当我们在托管元数据字段中选择了某个值并保存之后,SharePoint会判断这个隐藏列表中是否包含这个值,如果不包含的话,会自动把这个值加到列表里。此外,SharePoint在后台有一个TimerJob去把托管元数据服务中的数据同步到这个隐藏列表中(比如改了名字、换了路径、删除之类的)。
自然,这样的设计提供了托管元数据在网站集中的缓存,本意上是在一定程度上提高了性能。但是因为这个背后隐藏列表的存在,以及这种随用随加的机制,导致了一些问题。目前遇到的有如下三个可能存在的问题或不方便之处:
1、权限问题
这种问题出现的可能性不大,不过确实在实际的客户那里遇到过。
TaxonomyHiddenList是不继承网站权限的,他默认的权限设置是所有用户有读取权限(如果你是Windows认证的网站的话,这里的所有用户是Authenticated Users这个AD组),系统账号拥有完全控制权限,如果网站开启了匿名的话,匿名用户拥有查看项目的权限。这样的设置即保证了用户能够正常读取到其中的值,也能够防止其他用户修改列表中的内容(针对这个列表的所有修改都是通过托管元数据服务以及后台的TimerJob来完成的)。
但是上次在客户现场遇到过一个问题,这个Authenticated Users的权限不知道因为什么原因被去掉了,于是就造成了如下的诡异现象:
(1)用户可以在托管元数据导航的地方看到完整的元数据信息,但是点击进行筛选的时候报错(除非系统账号之前点过)
(2)用户在视图、查看页面上看不到任何托管元数据的值,但是在编辑页面和新建页面都能够正常看到。
这个很明显就是用了查阅项、而且查阅的那个列表上没有权限的现象;而新建、编辑、托管元数据导航之所以正常,因为这些数据都是直接来自托管元数据服务的。系统账号点过之后就可以用,估计是因为有缓存神马的。
2、视图筛选
如果你想在一个托管元数据字段上设置视图的筛选条件,那么你就只能使用“等于”和“不等于”这两种运算符,而且这两种运算符都会把托管元数据字段退化成一个“单行文本”行为的字段,换句话说,只能筛选到特定的那一个值,而不包含其中的子项的值。
举个例子来说,如果有下面一个托管元数据字段的设置:
动物;动物/猫;动物/猫/波斯猫;动物/猫/孟买猫;动物/狗;动物/狗/哈士奇;动物/狗/雪纳瑞
如果你想筛选这个字段的值 = 波斯猫的,那么ok,因为波斯猫是处于叶子结点的位置;但是如果你想通过视图去筛选所有的猫,这个是实现不了的。
你会说,如果我用API或者PowerShell设置视图的Query属性,把所有可能的值都包含进去(2010在CAML查询中新提供了一个In的运算符,行为就和SQL里面的In是一样的)呢?
如果你用文本的话,如果管理员修改了某个元数据的值,那查询条件就不对了;如果你用查阅项ID,那么因为背后那个隐藏列表有着随用随加的机制,如果你在设置视图的时候,某些值还从来没有使用过,当这些值被使用的时候,你的视图就查不到这些值了。而且不管你用哪一种方式,当管理员在后台更改了托管元数据的结构或者增加了新的节点的时候,这个视图条件都不再正确了。
一种变通的方式,是使用托管元数据导航,选中你需要的节点,然后把Url记下来,以后使用这个Url作为你这个视图的入口(SharePoint会在后台动态生成一个使用In运算符,包含所有子节点的CAML查询)。但是如果你要想在一个页面上放置多个视图,那就彻底没辙了。同样,对于“内容查询Web部件”来说,针对托管元数据的查询也会因为类似的原因而无能为力。
只能自己写代码做Web部件了(如果你有其他更好的方式,欢迎告诉我erucy9[_at_]gmail[_dot_]com)
3、API不正常的By Design行为
托管元数据提供了一套完整的API,这里面有一个方法需要特别说明,那就是GetTerms。
在TaxonomySession、TermStore、TermSet上都提供了这个方法,这个方法可以通过托管元数据的字符串值(Label)获取到特定名称的那一个(或多个)Term。
BUT……
当我们通过TaxonomySession和TermStore去使用这个方法的时候,只能获取到那些已经存在于TaxonomyHiddenList中的值(也就是网站中曾经使用过的值);只有当通过TermSet去使用GetTerms的时候,才能够获取到所有的值……
预告:
下一篇的内容是介绍托管元数据在SharePoint搜索结果页面中的“精简面板”(Refinement Panel)中的设置。