xQuery group output by category - xpath

I have another little problem with xQuery/xPath. For my homework i need to output the product range by branch and product category, where the output should only contain those branches (filiale), that have products (produkt and prodInSortiment) of every category (kategorie).
For the XML data please see the URL provided in the code.
While this gives me the right amount of products for each branch, i need to group it further by product category:
declare context item := doc("https://etutor.dke.uni-linz.ac.at/etutor/XML?id=1");
let $filialen := //filiale,
$sortiment := //prodInsortiment,
$produkte := //produkt,
$kategorien := distinct-values(//produkt/kategorie)
for $f in $filialen
let $s := $f//prodInSortiment
let $scat := distinct-values($produkte[#ean = $s/ean]/kategorie)
let $filsort := $produkte[#ean = $s/ean]/#ean
let $catcount := count($scat)
where $catcount = count(distinct-values($kategorien))
return
<filiale filialeNr="{$f/#filNr}">
{for $fs in $filsort return <ean>{$fs}</ean>
}
</filiale>
This is my approach of grouping it further, but it returns every product for every category(kategorie) in every branch(filiale):
declare context item := doc("https://etutor.dke.uni-linz.ac.at/etutor/XML?id=1");
let $filialen := //filiale,
$sortiment := //prodInsortiment,
$produkte := //produkt,
$kategorien := distinct-values(//produkt/kategorie)
for $f in $filialen
let $s := $f//prodInSortiment
let $scat := distinct-values($produkte[#ean = $s/ean]/kategorie)
let $filsort := $produkte[#ean = $s/ean]/#ean
let $catcount := count($scat)
where $catcount = count(distinct-values($kategorien))
return
<filiale filialeNr="{$f/#filNr}">
{for $cat in $scat return
<prodGruppe val = "{$cat}">
{for $fs in $filsort return <ean>{$fs}</ean>
}
</prodGruppe>
}
</filiale>
The result should look like this (this is the correct output for filiale "1"):
<filiale filialeNr="1">
<prodGruppe val="Pflege">
<ean>0-666-4567-2-22</ean>
<ean>0-777-4997-2-43</ean>
<ean>0-456-4887-3-22</ean>
<ean>0-55-48567-16-2</ean>
</prodGruppe>
<prodGruppe val="Ersatz">
<ean>1-626-7767-2-99</ean>
<ean>1-256-7700-2-00</ean>
<ean>1-333-7788-2-31</ean>
<ean>2-446-7240-9-15</ean>
<ean>9-396-7510-9-00</ean>
</prodGruppe>
<prodGruppe val="Audio">
<ean>7-2881-760-3-70</ean>
<ean>5-2671-955-5-55</ean>
<ean>1-4444-652-8-88</ean>
<ean>3-1111-654-3-99</ean>
</prodGruppe>
<prodGruppe val="Sonstiges">
<ean>6-581-1766-3-45</ean>
<ean>6-231-4777-3-15</ean>
<ean>4-1161-730-3-88</ean>
<ean>0-4381-880-7-00</ean>
<ean>5-6661-000-0-00</ean>
</prodGruppe>
Since there is an upcoming mid-term exam, I'm not just asking for a solution, I'm also interested in possible simpler ways to achieve the correct output.
Thanks in advance for any helpful information!

Assuming you have XQuery 3's group-by clause (https://www.w3.org/TR/xquery-31/#id-group-by) you can rewrite the last return to
return
<filiale filialeNr="{$f/#filNr}">
{
for $prod in $s
group by $cat := $produkte[#ean = $prod/ean]/kategorie
return
<prodGruppe val = "{$cat}">
{
$prod/ean
}
</prodGruppe>
}
</filiale>
I think.

Related

In golang how to loop through string interface

I have an interface that has some strings. I want to print each item one by one. For example, consider the following interface.Here I want to print sud first then man
var x interface{} = []string{"sud", "man"}
You can use something like this:
var x interface{} = []string{"sud", "man"}
res, ok := x.([]string)
if !ok {
fmt.Errorf("error")
}
for i := range res {
fmt.Println(res[i])
}

Failed to update rows in "jinzhu/gorm" pkg

I need to update value of fields in multiple rows.
I'm querying to get some of the database rows, but it doesn't work.
DB.Where("is_send = ?", "0").Find(&artists)
for _, artist := range artists {
if condition {
artist.IsSend = 1
... (more updatee)
DB.Save(&artist)
}
}
Change how you range it, by referring the below example:
for _, elem := range elems {
elem = new_val // Won't work, because elem is a copy of
// the value from elems
}
for i := range elems {
elems[i] = new_val // Works, because elems[i] deferences
// the pointer to the actual value in elems
}
Read: Gotchas
Also, if you're not modifying all fields, rather than using Save you can use Update as well. Refer: GORM CRUD's Interface UPDATE

Use SubQuery in Select

Is there any way to use the SubQuery function in Select?? I have seen it as part of Where clause, but I need on select.
I am solving this temporary doing this:
func GetUserProviders(userID int) ([]userprovider, error) {
providers := []userprovider{}
query := `SELECT (count(users_providers.user_id) > 0)
FROM users_providers
WHERE users_providers.user_id = '` + strconv.Itoa(userID) + `' AND users_providers.provider_id=providers.id`
rows, err := db.DB.Table("providers").
Select("providers.id, providers.name, (" + query + ") as checked").Rows()
if err == nil {
for rows.Next() {
var provider = userprovider{}
db.DB.ScanRows(rows, &provider)
providers = append(providers, provider)
}
}
return providers, err
}
But I would prefer, if possible, to use the function of the ORM instead concatenating strings.
In this case there is no danger, but for other cases, it would be great if there was any function to tranform
// SQL expression
type expr struct {
expr string
args []interface{}
}
into a sanitized String.
Thanks in advance.
Ok... I found the solution:
q := db.DB.Table("users_providers").
Select("(count(users_providers.user_id) > 0)").
Where("users_providers.user_id = ? AND users_providers.provider_id=providers.id", userID).
SubQuery()
rows, err := db.DB.Table("providers").
Select("providers.id, providers.name, ? as checked", q).
Rows()
The Select function accepts 2 arguments: one for the query and the other one for the args, working in the same way than the Where.
Thanks any way :)

Fill an object in Golang

How can I fill the todos-Object with a for-loop?
type Row struct {
Name string
Completed bool
Due time.Time
Rcount string
}
type Rows []Row
todos := Rows{
Row{Name: "Write presentation"},
Row{Name: "Host meetup"},
}
The question is a little hard to follow but try starting out with something following this pattern (error handling omitted for brevity):
rows, _ := db.Query(string, args...)
var Rows []Row
for rows.Next() {
var r Row
rows.Scan(&r.Name, &r.Completed, &r.Due, &r.Rcount)
Rows = append(Rows, r)
}
If you can clarify the question perhaps we can provide better answers
I think you are looking for the builtin function append
Note that it is normally used in combination with an assignment, because it may have to allocate additional memory. A zero value slice works just fine, no need to call make.
steps := []string{"write program", "???", "profit"}
var rows []Row
for _, tasks := range steps {
rows = append(rows, Row{Name: tasks})
}
If you want to loop over a sqlite3 query result, your loop will look different, but the x = append(x, ...) pattern will stay the same
If you know in advance how big your slice is going to be, explicit initialization with make will be more efficient.
var rows = make([]Row, len(steps))
for i, tasks := range steps {
rows[i] = Row{Name: tasks}
}

Why can't I key in a string in Golang map?

I'm writing a function in go to remove duplicate characters in a string. Here is my approach. When I run the following test, why do I get this error? I'm new to Go and used to more dynamic languages like Ruby/Python.
panic: assignment to entry in nil map [recovered]
panic: assignment to entry in nil map
source.go
func removeDuplicate(s string) string {
var m map[string]int
var c_string []string = strings.Split(s, "")
for i :=0; i < len(c_string); i++ {
m[c_string[i]] = 0
}
for i :=0; i < len(c_string); i++ {
m[c_string[i]] = m[c_string[i]] + 1
}
var (
result string = ""
)
for i :=0; i < len(c_string); i++ {
if m[c_string[i]] < 1 {
result = result + c_string[i]
}
}
return result
}
source_test.go
func TestRemoveDuplicateChars(t *testing.T) {
got := removeDuplicateChars("abbcde")
if got != "abcde" {
t.Fatalf("removeDuplicateChars fails")
}
}
Because you haven't actually initilize/allocated m, you've only declared it. Make this; var m map[string]int into m := map[string]int{}.
Which does initilization and assignment both in the same statement. You could also add another line m = make(map[string]int) which would prevent the error though I personally prefer the compacted syntax.
fyi your code is barfing on this line; m[c_string[i]] = 0, the error message should make sense when combining that with the information above.

Resources