I'm using TypeDB 2.2.0 and I'm getting the following error in Workbase with a query that used to work in previous versions. How do I do "not equal" between two concepts? (see query below)
Error: 13 INTERNAL: [QRY16] Invalid Query Pattern: The pattern '{{ $t2 != $t; $t2 isa transaction;
$_0 = "Peter"; $_0 isa name;
$t isa transaction;
$v has $_0; $v isa person;
$1 ($t, $v);
$2 ($t, $pu);
$3 ($pu, $t2); }}' can never be satisfied the current schema, specifically due to '[{ $t2 != $t; $t2 isa transaction;
$_0 = "Peter"; $_0 isa name;
$t isa transaction;
$v has $_0; $v isa person;
$1 ($t, $v);
$2 ($t, $pu);
$3 ($pu, $t2); }]'. Please check server logs for the stack trace.
match
$v isa person, has name "Peter";
$t isa transaction;
$1 ($t, $v);
$2 ($t, $pu);
$3 ($pu, $t2);
$t2 isa transaction;
$t2 != $t;
offset 0; limit 10;
You want not { $t2 is $t; };
$a is $b declares that $a and $b are exactly the same concept, so not { $t2 is $t; }; says that they are not the same concept. Both forms work for comparing variables that could be entities, relations or attributes.
There are also value comparators that are only used to compare two variables that denote attributes and compare them by value only:
$a = $b
$a != $b
$a > $b
$a < $b
$a >= $b
$a <= $b
Related
In XQuery 3.1 I am constructing an HTML table. In one <td> element I'm outputting a series of <a ref="">.
So, currently this simple for:
<td>
{
for $b in collection($globalvar:URIdata)/tei:TEI/tei:text//tei:seg[#type="dep_event"
and #corresp = $a/data(#corresp)
and #xml:id != $a/data(#xml:id)]
order by $b/data(#xml:id)
return {$b/data(#xml:id)}
}
</td>
Outputs this:
<td>
MS609-0006-8
MS609-0419-5
MS609-0613-4
</td>
But I'd like it to output the list of <a href=""> separated by commas:
<td>
MS609-0006-8,
MS609-0419-5,
MS609-0613-4
</td>
EDIT: below works...but does not output the results in desired order and I cannot get order by $b/data(#xml:id) to work with it due to an "cardinality" problem (that did not pop up in the original).
let $coll := collection($globalvar:URIdata)/tei:TEI/tei:text//tei:seg[#type="dep_event"
and #corresp = $a/data(#corresp)
and #xml:id != $a/data(#xml:id)]
let $last := count($coll)
for $b at $position in $coll
return ({$b/data(#xml:id)},
if ($position ne $last) then ', ' else '')
Many thanks in advance for any advice.
I am not sure whether there is a common idiom in XQuery to do that but I think using
for $b in collection($globalvar:URIdata)/tei:TEI/tei:text//tei:seg[#type="dep_event"
and #corresp = $a/data(#corresp)
and #xml:id != $a/data(#xml:id)]
order by $b/data(#xml:id)
count $p
let $a := {$b/data(#xml:id)}
return
if ($p > 1)
then (',', $a)
else $a
is a possible way, much aline the old XSLT approach to have <xsl:for-each select="$nodes"><xsl:if test="position() > 1">,</xsl:if><xsl:copy-of select="."/></xsl:for-each>. Closer to that you could also try
(
for $b in collection($globalvar:URIdata)/tei:TEI/tei:text//tei:seg[#type="dep_event"
and #corresp = $a/data(#corresp)
and #xml:id != $a/data(#xml:id)]
order by $b/data(#xml:id)
return {$b/data(#xml:id)}
) ! (if (position() > 1) then (',', .) else .)
That could also be written as
(
for $b in collection($globalvar:URIdata)/tei:TEI/tei:text//tei:seg[#type="dep_event"
and #corresp = $a/data(#corresp)
and #xml:id != $a/data(#xml:id)]
order by $b/data(#xml:id)
return {$b/data(#xml:id)}
) ! (if (position() > 1) then ',' else (), .)
a bit closer to your second attempt.
I'm having some difficulty with making a function I've written recursive. I need to be able to turn this xml:
<entry ref="22">
<headword>abaishen</headword>
<part_of_speech> v. </part_of_speech>
<variant>abeishen</variant>
<variant>abaissen</variant>
<variant>abeisen</variant>
<variant>abashen</variant>
<variant>abasshen</variant>
<variant>abassen</variant>
<variant>abeeshen</variant>
<variant>abesen</variant>
<variant>abessen</variant>
<variant>abaished</variant>
<variant>-et</variant>
<variant>-it</variant>
<variant>abaisht</variant>
<variant>abaist</variant>
<variant>abasht</variant>
<variant>abast</variant>
</entry>
Into this XML -- essentially replacing the ending of any entry that begins with an "-" with the stem of the last complete entry:
<entry ref="22">
<headword>abaishen</headword>
<variant>abeishen</variant>
<variant>abaissen</variant>
<variant>abeisen</variant>
<variant>abashen</variant>
<variant>abasshen</variant>
<variant>abassen</variant>
<variant>abeeshen</variant>
<variant>abesen</variant>
<variant>abessen</variant>
<variant>abaished</variant>
<variant>abaishet</variant>
<variant>abaishit</variant>
<variant>abaisht</variant>
<variant>abaist</variant>
<variant>abasht</variant>
<variant>abast</variant>
<part_of_speech> v. </part_of_speech>
</entry>
The issue I'm running into is that second entry, the -it one, returns "abaishet" with the code I currently have:
declare function local:hyphen-replace($f) {
let $j :=
if (substring($f/text(), 1, 1) = "-") then
let $ending := substring-after($f/text(),"-")
let $ending-length := string-length($ending)
let $previous := $f/preceding-sibling::*[1]
let $previous-length := string-length($previous)
return
if (substring($previous/text(), 1, 1) = "-") then
local:hyphen-replace($previous)
else
element {name($f)} {concat(substring($previous,1,($previous-length - $ending-length)),$ending)}
else
$f
return $j
};
declare function local:verbCheck($nodes as node()*) as node()* {
let $d := $nodes/part_of_speech
let $s := functx:siblings($d)
let $p := for $node in $nodes
return
let $d := $node/part_of_speech
let $s := functx:siblings($d)
return
if ($d/text() = " v. ") then
for $f in $s
let $j :=
local:hyphen-replace($f)
return ($j)
else
<empty/>
return
($p,$d)
};
<list>
{
let $collection := concat($collection, '?select=*.xml')
let $q := collection($collection)
let $v := local:buildNodes($q)
let $entries :=
for $n in $v
return <entry ref="{$n/#ref}">{local:verbCheck($n)}</entry>
return local:remove-empty-elements($entries)
}
</list>
It's obvious to me that my problem is with this piece of code in local:hypen-replace:
if (substring($previous/text(), 1, 1) = "-") then
local:hyphen-replace($previous)
because it's calling to the immediately previous item and replacing the "-it" node with it's information. But I don't know how to rewrite it to make it work recursively properly. Any suggestions would be appreciated. Thank you.
anyone help please
I have a function that outputs a list of names to an array. I've parsed the list into a string that shows exactly what I want.
I have a second list of names that I want to compare to the first list and print out the details if they are different.
Example: If string = CCleaner 5.5 then prog1 is out of date. I want it to show this
#Include <Date.au3>
#Include <MsgBoxConstants.au3>
Local $prog1 = "CCleaner 5.29"
Local $prog2 = "Defraggler 2.21"
Local $string
Local $aList = _UninstallList("", "","Publisher|InstallLocation|DisplayVersion")
for $1 = 0 to ubound($aList) - 1
$string &= $aList[$1][2] & " " & $aList[$1][6] & #CRLF
Next
msgbox(0, "", $string)
Several years ago, I wrote a function to compare lists or arrays. I think this can help you.
;==================================================================================================
; Function Name: _GetIntersection($Set1, $Set2 [, $GetAll=0 [, $Delim=Default]])
; Description:: Detect from 2 sets
; - Intersection (elements are contains in both sets)
; - Difference 1 (elements are contains only in $Set1)
; - Difference 2 (elements are contains only in $Set2)
; Parameter(s): $Set1 set 1 (1D-array or delimited string)
; $Set2 set 2 (1D-array or delimited string)
; optional: $GetAll 0 - only one occurence of every different element are shown (Default)
; 1 - all elements of differences are shown
; optional: $Delim Delimiter for strings (Default use the separator character set by Opt("GUIDataSeparatorChar") )
; Return Value(s): Succes 2D-array [i][0]=Intersection
; [i][1]=Difference 1
; [i][2]=Difference 2
; Failure -1 #error set, that was given as array, is'nt 1D-array
; Note: Comparison is case-sensitiv! - i.e. Number 9 is different to string '9'!
; Author(s): BugFix (AutoIt#bug-fix.info)
;==================================================================================================
Func _GetIntersection(ByRef $Set1, ByRef $Set2, $GetAll=0, $Delim=Default)
Local $o1 = ObjCreate("System.Collections.ArrayList")
Local $o2 = ObjCreate("System.Collections.ArrayList")
Local $oUnion = ObjCreate("System.Collections.ArrayList")
Local $oDiff1 = ObjCreate("System.Collections.ArrayList")
Local $oDiff2 = ObjCreate("System.Collections.ArrayList")
Local $tmp, $i
If $GetAll <> 1 Then $GetAll = 0
If $Delim = Default Then $Delim = Opt("GUIDataSeparatorChar")
If Not IsArray($Set1) Then
If Not StringInStr($Set1, $Delim) Then
$o1.Add($Set1)
Else
$tmp = StringSplit($Set1, $Delim)
For $i = 1 To UBound($tmp) -1
$o1.Add($tmp[$i])
Next
EndIf
Else
If UBound($Set1, 0) > 1 Then Return SetError(1,0,-1)
For $i = 0 To UBound($Set1) -1
$o1.Add($Set1[$i])
Next
EndIf
If Not IsArray($Set2) Then
If Not StringInStr($Set2, $Delim) Then
$o2.Add($Set2)
Else
$tmp = StringSplit($Set2, $Delim)
For $i = 1 To UBound($tmp) -1
$o2.Add($tmp[$i])
Next
EndIf
Else
If UBound($Set2, 0) > 1 Then Return SetError(1,0,-1)
For $i = 0 To UBound($Set2) -1
$o2.Add($Set2[$i])
Next
EndIf
For $tmp In $o1
If $o2.Contains($tmp) And Not $oUnion.Contains($tmp) Then $oUnion.Add($tmp)
Next
For $tmp In $o2
If $o1.Contains($tmp) And Not $oUnion.Contains($tmp) Then $oUnion.Add($tmp)
Next
For $tmp In $o1
If $GetAll Then
If Not $oUnion.Contains($tmp) Then $oDiff1.Add($tmp)
Else
If Not $oUnion.Contains($tmp) And Not $oDiff1.Contains($tmp) Then $oDiff1.Add($tmp)
EndIf
Next
For $tmp In $o2
If $GetAll Then
If Not $oUnion.Contains($tmp) Then $oDiff2.Add($tmp)
Else
If Not $oUnion.Contains($tmp) And Not $oDiff2.Contains($tmp) Then $oDiff2.Add($tmp)
EndIf
Next
Local $UBound[3] = [$oDiff1.Count,$oDiff2.Count,$oUnion.Count], $max = 1
For $i = 0 To UBound($UBound) -1
If $UBound[$i] > $max Then $max = $UBound[$i]
Next
Local $aOut[$max][3]
If $oUnion.Count > 0 Then
$i = 0
For $tmp In $oUnion
$aOut[$i][0] = $tmp
$i += 1
Next
EndIf
If $oDiff1.Count > 0 Then
$i = 0
For $tmp In $oDiff1
$aOut[$i][1] = $tmp
$i += 1
Next
EndIf
If $oDiff2.Count > 0 Then
$i = 0
For $tmp In $oDiff2
$aOut[$i][2] = $tmp
$i += 1
Next
EndIf
Return $aOut
EndFunc ;==>_GetIntersection
I'm trying to format decimals in XQuery. The decimals are currency, so the format should be ,###.##.
For example:
5573652.23 should be 5,573,652.23
and
352769 should be 352,769 (or 352,769.00 if it's easier/cleaner)
Right now I'm using this function from http://www.xqueryhacker.com/2009/09/format-number-in-xquery/, but I can't use decimals with it:
declare function local:format-int($i as xs:int) as xs:string
{
let $input :=
if ($i lt 0) then fn:substring(fn:string($i), 2)
else fn:string($i)
let $rev := fn:reverse(fn:string-to-codepoints(fn:string($input)))
let $comma := fn:string-to-codepoints(',')
let $chars :=
for $c at $i in $rev
return (
$c,
if ($i mod 3 eq 0 and fn:not($i eq count($rev)))
then $comma else ()
)
return fn:concat(
if ($i lt 0) then '-' else (),
fn:codepoints-to-string(fn:reverse($chars))
)
};
I'm using Saxon 9HE for my processor.
Any help would be greatly appreciated.
----- UPDATE -----
Based on Dimitre's answer, I modified the function to save the decimal portion and add it to the end of the return string.
New Function
declare function local:format-dec($i as xs:decimal) as xs:string
{
let $input := tokenize(string(abs($i)),'\.')[1]
let $dec := substring(tokenize(string($i),'\.')[2],1,2)
let $rev := reverse(string-to-codepoints(string($input)))
let $comma := string-to-codepoints(',')
let $chars :=
for $c at $i in $rev
return (
$c,
if ($i mod 3 eq 0 and not($i eq count($rev)))
then $comma else ()
)
return concat(if ($i lt 0) then '-' else (),
codepoints-to-string(reverse($chars)),
if ($dec != '') then concat('.',$dec) else ()
)
};
Use:
let $n := 5573652.23
return
concat(local:format-int(xs:int(floor($n))),
'.',
substring(string($n - floor($n)), 3)
)
This produces exactly the wanted, correct result:
5,573,652.23
This doesn't work for you?:
format-number(5573652.23,",###.##")
You can play with this here. I am pretty sure that saxon supports this function.
Edit: This function is not supported in saxon (see comments below).
With XQuery 3.0 and Saxon-HE 9.7 Parser you can do the following:
declare decimal-format local:de decimal-separator = "," grouping-separator = ".";
declare decimal-format local:en decimal-separator = "." grouping-separator = ",";
let $numbers := (1234.567, 789, 1234567.765)
for $i in $numbers
return (
format-number($i,"#.###,##","local:de"),
format-number($i,"#,###.##","local:en")
)
The output is:
<?xml version="1.0" encoding="UTF-8"?>1.234,57 1,234.57 789,0 789.0 1.234.567,76
1,234,567.76
Assume we have:
$a = #(1, #(2, #(3)))
I would like to flatten $a to get #(1, 2, 3).
I have found one solution:
#($a | % {$_}).count
But maybe there is a more elegant way?
Piping is the correct way to flatten nested structures, so I'm not sure what would be more "elegant". Yes, the syntax is a bit line-noisy looking, but frankly quite serviceable.
2020 Edit
The recommended syntax these days is to expand % to ForEach-Object. A bit more verbose but definitely more readable:
#($a | ForEach-Object {$_}).count
Same code, just wrapped in function:
function Flatten($a)
{
,#($a | % {$_})
}
Testing:
function AssertLength($expectedLength, $arr)
{
if($ExpectedLength -eq $arr.length)
{
Write-Host "OK"
}
else
{
Write-Host "FAILURE"
}
}
# Tests
AssertLength 0 (Flatten #())
AssertLength 1 (Flatten 1)
AssertLength 1 (Flatten #(1))
AssertLength 2 (Flatten #(1, 2))
AssertLength 2 (Flatten #(1, #(2)))
AssertLength 3 (Flatten #(1, #(2, #(3))))
This problem is probably most elegantly resolved with the .ForEach() array method introduced in Powershell v4.0. Performance-wise it has the advantage of not needing to construct a pipeline, so in some cases it might perform better.
> $a.ForEach({$_}).Count
3
If you already have a pipeline, the easiest way to flatten an array is to pipe it through Write-Output:
> $b = $a | Write-Output
> $b.Count
3
There are examples of nested arrays where piping to ForEach-Object simply can't handle them.
For example, given our nested array:
$toUnroll = #(#(0,1),#(2,3),#(#(4,#(5,6)),#(7,8),9),10)
If we attempt to pipe to ForEach-Object, the result would be:
PS /> $toUnroll | ForEach-Object { $_ }
0
1
2
3
4
Length : 2
LongLength : 2
Rank : 1
SyncRoot : {5, 6}
IsReadOnly : False
IsFixedSize : True
IsSynchronized : False
Count : 2
7
8
9
10
Write-Output is also not able to handle the unrolling:
$toUnroll | Write-Output | ForEach-Object GetType
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Int32 System.ValueType
True True Int32 System.ValueType
True True Int32 System.ValueType
True True Int32 System.ValueType
True True Int32 System.ValueType
True True Object[] System.Array
True True Int32 System.ValueType
True True Int32 System.ValueType
True True Int32 System.ValueType
True True Int32 System.ValueType
Below we can see some examples on how we can handle the flattening of these nested arrays including a one-liner anonymous function.
Recursive Function
This technique unrolls our array in order.
function RecursiveUnroll {
[cmdletbinding()]
param(
[parameter(Mandatory, ValueFromPipeline)]
[object[]] $Unroll
)
process {
foreach($item in $Unroll) {
if($item -is [object[]]) {
RecursiveUnroll -Unroll $item
continue
}
$item
}
}
}
RecursiveUnroll -Unroll $toUnroll
# Results in an array from 0 to 10
One-liner anonymous function:
The logic for this script block is the exact same as the function demonstrated above.
$toUnroll | & { process { if($_ -is [object[]]) { return $_ | & $MyInvocation.MyCommand.ScriptBlock }; $_ }}
Recursive Class Method (can be static or instance)
Same as the recursive function example, we can expect the array to keep it's order. We can add that this technique should be faster than the function since parameter binding is much faster for methods.
class Unroller {
[object[]] $Array
Unroller() { }
Unroller([object[]] $Array) {
$this.Array = $Array
}
static [object] Unroll([object[]] $Array) {
$result = foreach($item in $Array) {
if($item -is [object[]]) {
[Unroller]::Unroll($item)
continue
}
$item
}
return $result
}
[object] Unroll () {
return [Unroller]::Unroll($this.Array)
}
}
# Instantiating and using using the instance method of our class:
$instance = [Unroller] $toUnroll
$instance.Unroll()
# Results in an array from 0 to 10
# Using the static method of our class, no need to instantiate:
[Unroller]::Unroll($toUnroll)
# Results in an array from 0 to 10
Queue
This technique should be the fastest one, the downside is that we cannot expect an ordered array.
$queue = [Collections.Queue]::new()
$queue.Enqueue($toUnroll)
while($queue.Count) {
foreach($item in $queue.Dequeue()) {
if($item -is [object[]]) {
$queue.Enqueue($item)
continue
}
$item
}
}
# Using our given nested array as an example we can expect
# a flattened array with the following order:
# 10, 0, 1, 2, 3, 9, 4, 7, 8, 5, 6
Stack
Lastly, using a Stack we can ensure that the order is preserved, this technique is also very efficient.
$stack = [Collections.Stack]::new()
$stack.Push($toUnroll)
$result = while($stack.Count) {
foreach($item in $stack.Pop()) {
if($item -is [object[]]) {
[array]::Reverse($item)
$stack.Push($item)
continue
}
$item
}
}
[array]::Reverse($result)
$result
You can use .NET's String.Join method.
[String]::Join("",$array)