xslt sort based on mutually exclusive element - xslt-2.0

I have an xml data where there are 2 date elements <STRT_DT> and <STRT_BY_DT>. These 2 elements are mutually exclusive. I need to sort the XML aggregate with these dates in ascending order by date. Sample Instance:
<EVNT>
<LGL_EVNT>
<STRT_DT>2017-11-01</STRT_DT>
<EVNT_TYP_CD>10</EVNT_TYP_CD>
</LGL_EVNT>
<LGL_EVNT>
<STRT_BY_DT>2017-11-02</STRT_BY_DT>
<EVNT_TYP_CD>10</EVNT_TYP_CD>
</LGL_EVNT>
<LGL_EVNT>
<STRT_DT>2017-10-02</STRT_DT>
<EVNT_TYP_CD>10</EVNT_TYP_CD>
</LGL_EVNT>
<LGL_EVNT>
<STRT_BY_DT>2017-10-03</STRT_BY_DT>
<EVNT_TYP_CD>10</EVNT_TYP_CD>
</LGL_EVNT>
</EVNT>
Expected Output is to get the Legal Event aggregate in ascending order:
<EVNT>
<LGL_EVNT>
<STRT_DT>2017-10-02</STRT_DT>
<EVNT_TYP_CD>10</EVNT_TYP_CD>
</LGL_EVNT>
<LGL_EVNT>
<STRT_BY_DT>2017-10-03</STRT_BY_DT>
<EVNT_TYP_CD>10</EVNT_TYP_CD>
</LGL_EVNT>
<LGL_EVNT>
<STRT_DT>2017-11-01</STRT_DT>
<EVNT_TYP_CD>10</EVNT_TYP_CD>
</LGL_EVNT>
<LGL_EVNT>
<STRT_BY_DT>2017-11-02</STRT_BY_DT>
<EVNT_TYP_CD>10</EVNT_TYP_CD>
</LGL_EVNT>
</EVNT>
I am not able to sort based on these 2 mutually exclusive elements using xslt 2.0. Please help.

Simply use an xsl:sort with a sequence of two dates constructed from the two elements, as they are mutually exclusive there will only one sort key for each element:
<xsl:template match="EVNT">
<xsl:copy>
<xsl:apply-templates select="LGL_EVNT">
<xsl:sort select="xs:date(STRT_DT) , xs:date(STRT_BY_DT)"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
Add the identity transformation template to that (or use XSLT 3 and <xsl:mode on-no-match="shallow-copy"/>).

Related

Neo4j where predicates on multiple attributes

In Neo4j, for the where predicate, can we have constraints on more than one property? For example, suppose that we have a list of pairs: L = [(23, 'San Diego'), (25, 'Palo Alto'), (21, 'Seattle'), ....], then does Cypher support something similar to the following:
Match (a) where (a.age, a.city) in L return a
The age and city combinations need to be in the L list
Neo4j does not accept tuples but map of key, value pairs (or dictionary).
However, this query will be close to what you have described.
WITH [{age:23, city:'San Diego'}, {age:25, city:'Palo Alto'}, {age:21, city:'Seattle'}] as L
MATCH (p:Person) WHERE {age: p.age, city: p.city} in L
RETURN p
Sample result:
╒═══════════════════════════════════════════╕
│"p" │
╞═══════════════════════════════════════════╡
│{"name":"Andy","city":"San Diego","age":23}│
└───────────────────────────────────────────┘
See below:
https://neo4j.com/docs/cypher-manual/current/syntax/values/#composite-types

neo4j order by optional property in descdending order with nulls being at the bottom

I need to sort nodes by createdAt timestamp in descending order. It is an optional parameter. There are old nodes that don't have this parameter. When I try sorting as suggested in neo4j doc:
MATCH (node:MyTag)
WHERE ...
RETURN node
ORDER BY node.createdAt DESC
I get nodes where createAt does not exist before the ones where it exists. How can I get nodes that have createdAt in descending order first and then have the nodes that don't have createAt field?
Cheers
You could add a field to your RETURN
COALESCE ( node.createdAt, 999999999) AS sortField
that would give you a value to sort on.

neo4j matching two different relationships and retrieving count while avoiding cartesian product

So I have database in which I want to retrieve the result that has the relationship "Reviewed" and in this result I want to retrieve entities with relationship "acted_in" and return the movie with highest number of cast members.
This is the code I wrote:
MATCH (a:Person)-[r2:REVIEWED]->(movie:Movie)<-[r:ACTED_IN]-(actors:Person)
RETURN movie.title as Movie_name, count(actors) as no_of_cast
ORDER BY no_of_cast DESC
Limit 1
It returns the correct movie name but the number of cast members is a cartesian product of the persons who reviewed and the persons who acted in.
The result I get after the collect function on actors is
"The Replacements" ["Brooke Langton", "Keanu Reeves", "Orlando Jones", "Gene Hackman", "Brooke Langton", "Keanu Reeves", "Orlando Jones", "Gene Hackman", "Brooke Langton", "Keanu Reeves", "Orlando Jones", "Gene Hackman"]
It repeats the actors the number of times relationship "reviewed" is present in that movie node.
How can I avoid this and get the correct number of cast members which is 4. Thank you.
The reason why you get multiple/duplicate actors is because the same movie is being reviewed by more than one person (reviewers). To remove duplicates, you can use the keyword "DISTINCT".
MATCH (a:Person)-[r2:REVIEWED]->(movie:Movie)<-[r:ACTED_IN]-(actors:Person)
RETURN movie.title as Movie_name, count(distinct actors) as no_of_cast
ORDER BY no_of_cast DESC
Result:
╒═══════════════════╤════════════╕
│"Movie_name" │"no_of_cast"│
╞═══════════════════╪════════════╡
│"The Replacements" │4 │
├───────────────────┼────────────┤
│"Cloud Atlas" │4 │
├───────────────────┼────────────┤
│"The Da Vinci Code"│4 │
├───────────────────┼────────────┤
│"The Birdcage" │3 │
├───────────────────┼────────────┤
│"Unforgiven" │3 │
└───────────────────┴────────────┘
You have some other options for improvement.
You don't really want paths that include the reviewer, that's what causing a cross product (reviewer rows x actor rows). Instead, this is a condition, that the only movies you are interested in are those where someone has reviewed it. You can move that part of the pattern to the WHERE clause instead:
MATCH (movie:Movie)
WHERE ()-[:REVIEWED]->(movie)
WITH movie, size((movie)<-[:ACTED_IN]-()) as no_of_cast
ORDER BY no_of_cast DESC
LIMIT 1
RETURN movie.title as Movie_name, no_of_cast
Note also that instead of doing an aggregation, we're getting the degree of the :ACTED_IN relationship on the node for the number of cast using size(). This is more efficient, since we don't need to perform any expansions or aggregations to get this value.
Also we're waiting until after we've gotten our single top result to project that title property. Property access can be expensive, so it's best to defer it until you have your minimal result set.

Good way to set relation to an edge in Neo4j

For my project I need to create a relation to an edge in Neo4j graph database.
Let's illustrate on example of a flight provider who operates flights from Rome to London (and back) and also from Oslo to Lisbon.
CREATE (l:City{name:"London"})
(r:City{name:"Rome"}),
(li:City{name:"Lisbon"})
(o:City{name:"Oslo"}),
(op:Operator{name:"One"}),
(l)<-[f:FlightRoute{distance:900}]->(r)
How would you link Operator One to London, Rome, Lisbon and Oslo to suggest that this operator connects these cities (l<-->r & li<-->o) but not e.g. r<-->o. Other operators would be doing other cities. So basically I would like to link op to 2 edges.
Queries to perform would be to find all operators doing various lines. Calculating overall distances of operations (assuming <--> has distance parameter) etc.
I can imagine only to create a node between (l) and (r). Is there some other way?
As you suspected, "reification" (i.e., turning a relationship into a node) is probably the best way to handle your situation.
Here is an example of a query that adds the fact that an operator operates a flight from London to Rome:
MATCH (l:City {name:"London"}),
(r:City {name:"Rome"}),
(op:Operator {name:"One"})
CREATE (l)<-[:FROM]-(f:FlightRoute{distance:900})-[:TO]->(r),
(op)-[:OPERATES]->(f);
And a similar query for a flight from Lisbon to Oslo:
MATCH (li:City {name:"Lisbon"}),
(o:City {name:"Oslo"}),
(op:Operator {name:"One"})
CREATE (li)<-[:FROM]-(f:FlightRoute{distance:21})-[:TO]->(o),
(op)-[:OPERATES]->(f);
To find all operators between London and Rome:
MATCH (l:City {name:"London"})<-[:FROM|TO]-(f)-[:FROM|TO]->(r:City {name:"Rome"}),
(op:Operator)-[:OPERATES]->(f)
RETURN DISTINCT op;
To find overall distance for all flights of an operator:
MATCH (op:Operator {name:"One"})-[:OPERATES]->(f)
RETURN SUM(f.distance);
Indexes (or uniqueness constraint) for :City(name) and Operator(name) would help to speed up the above queries.

Is there some inherent filter in Neo4J Cypher?

In the tutorial, there is the following Cypher query:
MATCH (gene:Person)-[:ACTED_IN]->()<-[:ACTED_IN]-(other)
WHERE gene.name="Gene Hackman"
RETURN DISTINCT other;
I generally understand the query. Match all Person nodes that ACTED_IN "any" node, where the gene:Person node has a name property equal to "Gene Hackman".
Then Return a distinct set of "other" nodes that ACTED_IN the same set of "any" nodes as Gene Hackman.
What is returned is a set of nodes, not including the Gene Hackman node.
It seems like the set of nodes being returned would include the Gene Hackman node. There is nothing explicitly filtering out the node containing the name "Gene Hackman" in the Cypher query. So the set of nodes that acted in the same movies as Gene Hackman should include Gene Hackman as well.
I'm assuming there is some inherent rule that is being applied, but the tutorial up to this point hasn't stated what that rule is or I just missed it :^).
Thanks,
Todd
The rule is that relationships in a pattern aren't reused. Once you follow the first :ACTED_IN relationship, you can't follow it again in the same pattern match.
Wes's answer is ok, and helped me too. In addition please note that this rule holds even if you write the query using 2 paths as
MATCH (gene:Person)-[:ACTED_IN]->(m),
(m)<-[:ACTED_IN]-(other)
WHERE gene.name="Gene Hackman"
RETURN DISTINCT other;

Resources