xQuery: test for two specific children with 'and' - conditional-statements

Assume I have the following snippet
<persName>
<forename>Gaius</forename>
<surname>Iulius</surname>
<addName>Caesar</addName>
</persName>
I need a result like [surname], [forename] where the comma should only be present if necessary.
In XSLT, I'd simply use
<xsl:value-of select="surname" />
<xsl:if test="surname and forename"><xsl:text>, </xsl:text></xsl:if>
<xsl:value-of select="forename" />
Now I naively thought I could transfer this to XQuery – and failed.
I was slightly puzzled that
if ($node/forename) then "1" else "0"
if ($node/surname) then "1" else "0"
if ($node/surname and $node/forename) then "1" else "0"
will give 1, 1, 0, respectively.
I worked around this by counting the number of children in one of these cases but I'm still puzzled why this is the way it is.
Tanks for any input!
Edit: here's the code I've been using:
declare function habe:shortName($node) {
<span>{
if ($node/tei:name) then $node/tei:name
else
let $tr := if ($node/tei:surname and count($node/tei:forename)>0) then ", " else ""
return concat($node/tei:surname, $tr, $node/tei:forename)
}</span>
};
which, when given the above snippet, returned IuliusGaius.
I then tried the three tests above and got the stated result.
I'm doing this on eXist – maybe their implementation is faulty?
Edit: Thanks to #MichaelKay and #joewiz for hinting at misleading typos!

The following code returns the expected results, (1, 1, 1), using eXide on http://exist-db.org/exist/apps/eXide/index.html:
xquery version "3.0";
let $node :=
<persName>
<forename>Gaius</forename>
<surname>Iulius</surname>
<addName>Caesar</addName>
</persName>
return
(
if ($node/forename) then "1" else "0",
if ($node/surname) then "1" else "0",
if ($node/surname and $node/forename) then "1" else "0"
)

One solution to your original problem (adding a ', ' if both forename and surname exist) is to use string-join($strs as xs:string*, $joiner as xs:string) instead of concat($string as xs:string[...]):
let $name :=
<persName>
<forename>Gaius</forename>
<surname>Iulius</surname>
<addName>Caesar</addName>
</persName>
return string-join(($name/surname, $name/forename), ', ')
This returns Iulius, Gaius.
You can also check for presence of nodes directly in boolean expressions:
let $name :=
<persName>
<forename>Gaius</forename>
<surname>Iulius</surname>
<addName>Caesar</addName>
</persName>
return (
$name/surname and $name/forename,
$name/surname and $name/foo
)
This returns true false.

Related

PostgreSQL verify an empty array on json

I have the following row on select
jsonData
[]
[{"descricao":"falha na porta"}, {"descricao":"falha no ip"}]
[]
I have to Identify empty jsons, then manually add a value to it (eg row 1 and 3 ), I tried the following :
case when jsonData is null then cast('[{"descricao":"no error"}]' AS json) else jsonData end as opts
But the "is null" verification fails for this type of data (array of json), how to identify '[]' values in this case?
Note: I only have select permission on this db
You can use json_array_length()
when json_array_length(jsondata) = 0 then ...
Casting the json to text before comparison worked for this case :
" case when jsondata::text = '[]' "
Try this condition:
jsondata = JSON '[]'

Fix "dynamic" query without throwing me the given error?

UPDATE dbo.Einkauf_Web_Upload
SET
${
updatedUpload.Menge !== null
? `Anzahl = ${`${updatedUpload.Menge}`},`
: null
},
${
updatedUpload.ENummer !== null
? `ENummer = ${`'${updatedUpload.ENummer}'`}`
: null
}
WHERE ...
This query is supposed to differentiate between updated values of the object updatedUpload which, initially, has all of its values set to null. If the value is not altered therefor not updated, the query must not affect the particular column. In its current state, the query throws this error:
Incorrect syntax near the keyword 'null'
And I know why; if you do not alter the Menge attribute, the query looks like this:
UPDATE dbo.Einkauf_Web_Upload
SET null, ENummer = "abc"
WHERE ..
Is there a workaround to this? I am using NodeJs as my backend and thought of trying to make the column references dynamic via a mapped array which contains only the altered columns of updatedUpload.
Will appreciate any help!
SET null, ... is invalid SQL syntax, you should skip the null at all. Furthermore, remember not to do the query if there is nothing to update as SET WHERE ... is also invalid syntax.
I would suggest something like:
let updates = [
updatedUpload.Menge !== null ? `Anzahl = ${`${updatedUpload.Menge}`}` : null,
updatedUpload.ENummer !== null ? `ENummer = ${`'${updatedUpload.ENummer}'`}` : null,
// ... add here
]
// Filter out null updates
updates = updates.filter(u => !!u);
// Do query only if updates are avaliable
if (updates.length > 0) {
const sql = `UPDATE dbo.Einkauf_Web_Upload SET ${updates.join(', ')} WHERE ...`;
// ... execute
}

Remove html tags from a column

I have the a column in my table which stores a paragraph like below :
<p>I like it.</p>this is my job.<main>current.</main>
I want to remove the tags <p>, </p>, and and all tags between < and >.
So my expected output will be like below :
I like it. this is my job. current.
please try this
DECLARE #txt NVARCHAR(MAX) = '<p>I like it.</p>this is my job.<main>current.</main>'
SELECT x.value('.', 'NVARCHAR(MAX)') FROM ( SELECT x =
CAST(REPLACE(REPLACE(#txt, '>', '/>'), '</', '<') AS XML) ) r
this will help to remove all tags
UPDATE: Samir's answer is better than mine as it can deal with html-crap
(as long as there is no < or > as normal content :-)
You can try this:
If your string is valid XML (meaning XHTML) you might go this route:
DECLARE #yourString NVARCHAR(100)=N'<p>I like it.</p>this is my job.<main>current.</main>';
SELECT CAST(#yourString AS XML).value('.','nvarchar(max)');
returns
I like it.this is my job.current.
Using . as the XPath will return the whole content as is...
Any invalid XML (very likely with simple html) will break this...
You can use giant REPLACE() :
SELECT REPLACE(REPLACE(REPLACE(REPLACE(col, '<p>', ''), '</p>', ''), '<main>, ''), '</main>', '')
If you are working the latest SQL version then this will be easy to write using TRANSLATE() :
SELECT TRANSLATE(col, '<p></p><main></main>', '')
If u want remove tags when select, you can do a normal SELECT and clear string:
SELECT column FROM my_table;
$value = $row["column"];
$value_replaced = str_replace('<p>', '', $value);
$value = $value_replaced;
$value_replaced = str_replace('</p>', '', $value);
$value = $value_replaced;
$value_replaced = str_replace('<main>', '', $value);
$value = $value_replaced;
$value_replaced = str_replace('</main>', '', $value);

Laravel/SQL: return rows where column Equals NOT 'something" and NULL

LARAVEL 5.4 (but probably it's a more general SQL question)
Hello! I have a table with a structure:
Suppose it's my model 'Table'.
I want a query which:
uses (receives) variables :
$id of array ['id', 'string', integer]
where string is '<' or '>'
$status_not_bad = bool;
(if true - include all rows where 'status' !== 'bad' AND 'status' IS NULL);
for example, we are given:
$id = ['id', '>', 0];
$status_not_bad = true;
Table::thisquery() ... ->get();
"get rows where status is not bad and id > 0" returns rows 1 and 3.
but if we given:
$id = ['id', '<', 3];
$status_not_bad = true;
Table::thisquery() ... ->get();
"get rows where status is not bad and id < 3" returns row 1
(it should be same query which return those results using those variables).
I've never used laravel before so my syntax could be off, but I know you want to use MySQL's ifNull function for this:
Table::where([['id', '>', 0], ['number', '>', 2])->whereRaw("IfNull(status, 'blah') <> 'bad'")->get();
Just chain your ->where() clauses. Maybe also consider wrapping them to prevent conflicting with any additional:
$results = Table::where("id", ">" 0)
->orWhere("number", ">", 2)
->orWhereNull("status")
->get();
Or, wrapped:
$results = Table::where(function($query){
$query->where("id", ">" 0)
->orWhere("number", ">", 2)
->orWhereNull("status");
})->get();
If you want to see what the actual query you're executing is, replace ->get() with ->toSql(), and use dd($result);:
$results = Table::where(...)->toSql();
dd($results);
Edit: Sounds like we need multiple wrapping queries, due to multiple conditions:
$results = Table::where(function($query){
$query->where("status", "!=", "bad")
->where("id", "<", 3); -- "status is not bad and id < 3"
})->orWhere(function($query){
$query->where("status", "!=", "bad")
->where("id", ">", 0); -- "status is not bad and id > 0"
})->get();
That should handle paired conditions as noted in your comment.

Grails: "where" query with optional associations

I'm trying to run a "where" query to find a domain model object that has no association with another domain model object or if it does, that domain model object has a specific property value. Here's my code:
query = Model.where({
other == null || other.something == value
})
def list = query.list()
However, the resulting list only contains objects that match the second part of the OR statement. It contains no results that match the "other == null" part. My guess is that since it's checking a value in the associated object its forcing it to only check entries that actually have this associated object. If that is the case, how do I go about creating this query and actually having it work correctly?
You have to use a LEFT JOIN in order to look for null associations. By default Grails uses inner join which will not be joined for null results. Using withCriteria as below you should get the expected results:
import org.hibernate.criterion.CriteriaSpecification
def results = Model.withCriteria {
other(CriteriaSpecification.LEFT_JOIN){
or{
isNull 'id'
eq 'something', value
}
}
}
UPDATE
I know aliasing is not possible in DetachedCritieria where one would try to specify the join as in createCriteria/withCriteria. There is an existing defect regarding adding the functionality to DetachedCriteria. Just adding the work around for where query as mentioned in defect.
Model.where {
other {
id == null || something == value
}
}.withPopulatedQuery(null, null){ query ->
query.#criteria.subcriteriaList[0].joinType = CriteriaSpecification.LEFT_JOIN
query.list()
}
I would rather use withCriteria instead of the above hack.
this might work:
query = Model.where({
isNull( other ) || other.something == value
})
If that wouldn't work, try something like:
other.id == null || other.something == value
UPDATE:
or with good'ol criteria query:
list = Pack.withCriteria{
or{
isNull 'other'
other{ eq 'something', value }
}
}