我需要一个 JCR 的基于标签的搜索系统,比如 Modeshape。我想通过一些标签搜索节点。问题是实现它的最佳方法是什么?
- 为标签添加新的节点类型和 mixin,如果这是真的,我在哪里可以定义用户可以查看的标签名称?
- 实现标签层次结构并在我的节点中引用它们。所以如果这是真的,我该如何参考它们?
- 任何其他方式。
在 JCR 中有几种实现标签的方法。您选择哪个选项将取决于您自己的应用程序的需求。这是我知道的四个选项。
选项 1:使用 Mixins
为每个标记定义一个混合节点类型定义,它是一个标记混合(它没有属性定义或子节点定义),使用 NodeTypeManager 动态注册它们。然后,当您想要“标记”一个节点时,只需将代表该标记的 mixin 添加到该节点即可。任何节点都可以有多个标签,您可以查询具有特定标签的所有节点。
(在此响应的其余部分中,“acme”用作通用名称空间。您应该将其替换为适合您自己的应用程序和组织的名称空间。)
例如,给定标签“acme:tag1”,您可以使用简单查询找到所有具有此标签的节点:
SELECT * FROM [acme:tag1]
这种方法的缺点是维护标签很麻烦。创建新标签需要注册新的节点类型。您不能轻松地重命名标签,而是必须为具有新名称的标签创建 mixin;找到所有具有代表旧标签的mixin的节点,删除旧的mixin,并添加新的;最后删除旧标签的节点类型定义(在它不再在任何地方使用之后)。删除旧标签以类似的方式完成。另一个缺点是不容易将附加元数据(例如,显示名称)与标签相关联,因为节点类型定义上不允许有额外的属性。
这种方法应该表现得很好。
选项 2:使用分类法和强引用
在这种方法中,您将在存储库的一个区域中创建一个简单的节点结构,您可以在其中为每个标签(例如,分类)创建一个节点。在此节点上,您可以设置描述标签的属性(例如,显示名称);这些属性可以随时更改(例如,重命名标签)。
然后要将标签“应用”到节点,您只需创建与标签的某种关系。一种方法是定义一个包含 REFERENCE 类型的“acme:tags”多值属性的 mixin 节点类型。当您想将一个或多个标签应用于节点时,只需将 mixin 添加到节点并将“acme:tags”属性设置为标签节点即可。
要查找特定标记的所有节点,您可以在标记节点上调用“getReferences()”以查找包含对标记节点的引用的所有节点。
这种方法的好处是所有标签都必须在一个或多个分类法(可能包括用户特定的分类法)内进行控制/管理。但是,也有一些缺点。首先,REFERENCE 属性的性能可能不是很好。一些 JCR 实现完全不鼓励使用 REFERENCES。ModeShape 不会,但是当有许多节点包含对同一节点的引用时(例如,许多节点具有单个标记),ModeShape 可能会开始降低 REFERENCE 性能。
选项 3:使用分类法和弱引用
这个选项是类似于上面的选项 2 的混合,除了“acme:tags”属性将是 WEAKREFERENCE 而不是 REFERENCE。您仍将定义和管理一个或多个分类法。要查找具有特定标记的节点,您不能在标记节点上使用“getReferences()”方法(因为它们不适用于 WEAKREFERENCE 属性),但您可以通过查询轻松完成此操作:
SELECT * FROM [acme:taggable] AS taggable
JOIN [acme:tag] AS tag ON taggable.[acme:tags] = tag.[jcr:uuid]
AND LOCALNAME(tag) = 'tag1'
这种方法确实强制使用一个或多个分类法,使控制标签更容易一些,因为它们必须存在于分类法中才能使用。重命名和删除也更容易。在性能方面,这比 REFERENCE 方法要好,因为 WEAKREFERENCE 属性在大量引用时会表现得更好,无论它们都指向一个节点还是多个节点。
缺点是即使标签仍在使用,您也可以删除它,但包含对该已删除标签的 WEAKREFERENCE 的节点将不再有效。这可以通过应用程序中的一些约定来解决,或者通过简单地使用分类上的元数据来说明特定标签“已弃用”并且不应使用。(IMO,后者实际上是这种方法的一个好处。)
此选项通常会比选项 2 更好地执行和扩展。
选项 4:使用字符串属性
另一种方法是简单地使用 STRING 属性来用要应用的标签的名称来标记每个节点。例如,您可以定义一个定义多值 STRING 属性的 mixin(例如,“acme:taggable”),当您想要标记节点时,只需添加 mixin(如果尚未存在)并添加标记为“acme:tags”STRING 属性上的值(同样,如果它尚未作为值存在)。
这种方法的主要优点是非常简单:您只需在要标记的节点上使用字符串值。要查找使用特定标签(例如,“tag1”)标记的所有节点,只需发出查询:
SELECT *
FROM [acme:taggable] AS taggable
WHERE taggable.[acme:tags] = 'tag1'
标签的管理很容易:没有管理。如果要重命名标签,则可以重命名标签值。如果要删除标签(并从带有标签的节点中删除),则可以通过从“acme:tags”属性中删除值来完成(可能在后台作业中)。
请注意,这允许使用任何标签名称,因此最适用于标签名称根本不受控制的情况。如果您想控制用作标签值的字符串列表,只需在存储库中创建一个分类(如上面的选项 2 和 3 中所述),然后让您的应用程序将值限制为分类中的值。您甚至可以拥有多个分类法,其中一些可能是特定于用户的。但是这种方法没有与选项 2 或 3 完全相同的控制。
此选项将比选项 3 执行得更好(因为查询更简单),但也可以扩展。