“基于表达的顺序”

时间:2014-04-01 08:19:31

标签: java xpath jcr jcr-sql2

给出一个比较器:

public int compareTo(Person p1, Person p2)
{
    String val1 = StringUtils.isEmpty(p1.nickName) ? p1.name : p1.nickName;
    String val2 = StringUtils.isBlank(p2.nickName) ? p2.name : p2.nickName;

    return val1.compareTo(val2);
}

我希望使用xpath查询(或SQL2,如果它超出xpath的功能)使用“order by”获得相同的结果。有可能吗?

鉴于数据:

No. |  Name    | Nickname |
 1  |  Adam    | Hornet   |
 2. |  Adam    |          |
 3. |  Jack    | Legend   |

排序数据:

No. | name | nickname
2.  | Adam | 
1.  | Adam | Hornet
3.  | Jack | Legend

2 个答案:

答案 0 :(得分:0)

您可以在JCR-SQL2和XPath中执行此操作。

标准JCR-SQL2

假设您有一个名为Person且包含namenickname字段的节点类型,那么在JCR-SQL2中实现接近所需结果的一种方法是:

SELECT p.name, p.nickname FROM Person AS p ORDER BY p.name ASC, p.nickname ASC

但是我们可以更清楚地写出这个查询,因为只有一个选择器(即一个“表”),我们可以省去可选的ASC

SELECT name, nickname FROM Person ORDER BY name, nickname

现在,严格来说,从此查询中获得的结果将取决于实现,这是因为您在问题中描述了nickname字段可能如何为null,而JCR 2.0规范未指定如何排序处理NULL值。例如,它们应该出现在非空值之前还是之后?实现可以选择。

还有另一个棘手的问题。请注意,我假设Person节点类型具有name字段,而不是假设您将该人的名称存储在节点名称中。那是因为在JCR-SQL2中,您必须使用名为NODENAMENODELOCALNAME的特殊函数来访问节点的名称。您可以直接在排序中使用它,但遗憾的是,在标准JCR-SQL2中未定义查询结果中的节点名称。

因此,使用标准JCR-SQL2的以下查询将起作用:

SELECT nickname FROM Person ORDER BY NODELOCALNAME, nickname

但您必须从结果中获取的javax.jcr.Node中获取节点的名称,而不是结果集中的某个值。严格来说,这既不昂贵也不困难;它只是改变了应用程序使用查询结果的方式。

ModeShape中的JCR-SQL2

一个JCR实现ModeShape扩展了标准JCR-SQL2语法,其数量为additional features。其中一个扩展是引入几个伪列,包括:

  • jcr:name是节点名称的替身
  • jcr:path是节点路径的替身
  • mode:localName是节点名称
  • 的本地部分的替代
  • mode:depth评估节点的深度
  • jcr:uuid评估javax.jcr.Node.getUUID()
  • 的结果

因此,您可以在排序和SELECT子句中使用jcr:name伪列:

SELECT jcr:name, nickname FROM Person ORDER BY jcr:name, nickname

ModeShape 3.x中提供了这些选项。但社区目前正在开发ModeShape 4.0(目前在Alpha中),并且还添加了一个扩展名:您可以指定排序中是否nulls should come first or last。以下是早期查询的变体,显示了如何使用它:

SELECT name, nickname FROM Person ORDER BY name NULLS FIRST, nickname NULLS FIRST

如果你是Person节点类型定义确保name属性是必需的(永远不为null),则不需要指定NULLS FIRST(或{{1}在NULLS LAST列排序。

除了name而不是jcr:name

之外,这是相同的查询
name

请注意,我们不需要在SELECT jcr:name, nickname FROM Person ORDER BY jcr:name, nickname NULLS FIRST 列上指定NULLS FIRSTNULLS LAST,因为每个节点都有一个名称,因此jcr:name永远不会为空。

标准JCR 1.0 XPath

如果您正在使用仍支持XPath的JCR实现(技术上已指定为JSR-170或JCR 1.0的一部分,并且在JSR-283或JCR 2.0中已弃用),您还可以使用XPath:

jcr:name

由于JCR 1.0确实定义了//element(*, Person) order by @name ascending, @nickname ascending 伪列,因此您可以非常轻松地使用节点名称:

jcr:name

还有一件事:ModeShape 3.x或更高版本将用JCR-SQL2,XPath和JCR-SQL编写的查询解析到同一个抽象语法树中,然后执行它们。因此,最后一个JCR-SQL2查询和最后一个XPath查询将在ModeShape中以相同方式处理。

答案 1 :(得分:0)

以下是比较器中如何使用任意代码执行此操作的草图。获取执行普通查询时获得的NodeIterator

import javax.jcr.query.Query;
import javax.jcr.query.QueryManager;
import javax.jcr.query.QueryResult;
import javax.jcr.query.NodeIterator;
import org.apache.jackrabbit.commons.iterator.NodeIteratorAdapter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

. . .

Query query = queryManager.createQuery( /* query without any ordering specs */ );
QueryResult queryResult = query.execute();
NodeIterator nodeIterator = queryResult.getNodes();

并将其传递给执行排序并返回另一个NodeIterator的方法:

NodeIterator sortedNodes = sortMyWay(nodeIterator);

. . .

private NodeIterator sortMyWay(NodeIterator input) {
    ArrayList<Node> results = new ArrayList<Node>();
    while (input.hasNext()) {
        results.add(input.nextNode());
    }
    Collections.sort(results, new Comparator<Node>(){
        @Override
        public int compare(Node node1, Node node2) {
            String nickName1 = (node1.hasProperty("nickName") ? node1.getProperty("nickName").getString() : "");
            String nickName2 = (node2.hasProperty("nickName") ? node2.getProperty("nickName").getString() : "");
            return nickName1.compareTo(nickName2);
        }
    });
    return new NodeIteratorAdapter(results);
}

照常从sortedNodes.nextNode()检索您的搜索结果。

根据需要向上述方法添加异常处理,并根据需要向name属性添加默认值。