Go言語 XMLを変換してDB内を検索し、一致した情報をXML形式で返す

##はじめに
自分はプログラミング歴5ヶ月の超初心者で、自分と同じような超初心者と自分の備忘録のために書いております。
もし、気になる点やもっといい方法があれば指摘していただけると幸いです。

##やること
今回は、[Go言語でAPIを使ってXML形式のリクエストをDBに保存する](https://qiita.com/hhio67/items/38d7b51c0d19f67910ef)の続きをやる。
DBに保存するだけじゃ物足りないので、保存したデータ内を別のキーワードでDB内を検索する機能と検索結果をXML形式で出力するところもやってみようと思った。（まだ物足りない）

##実装する機能
前回はXMLのデータをUnmarshalして保存するだけだったから今回は

・キーワードでリクエストを送信してXMLを受け取り、DB内を検索
・検索したキーワードとヒットしたキーワード別のヒット件数を出力
・一致したデータをXML形式に変換して出力
・ヒットしなかったらその旨を伝えてプログラムを終了する

を実装する。

##やってみる
今回も引き続き[国会議事録検索システム](https://kokkai.ndl.go.jp/api.html)を使っていく。

###DB
検索したキーワードも保存した方がいいかなと思ってテーブル構造をちょっと変えた。
例によってid以外は~~面倒なので~~文字列にしてある。

```
+---------+--------------+------+-----+---------+----------------+
| Field   | Type         | Null | Key | Default | Extra          |
+---------+--------------+------+-----+---------+----------------+
| id      | int          | NO   | MUL | NULL    | auto_increment |
| issueid | varchar(255) | YES  |     | NULL    |                |
| house   | varchar(5)   | YES  |     | NULL    |                |
| date    | varchar(255) | YES  |     | NULL    |                |
| keyword | varchar(255) | YES  |     | NULL    |                |
+---------+--------------+------+-----+---------+----------------+
```

あと、検索対象が必要なので「コロナ」に加えて

・ワクチン
・菅義偉（人名を出すのは大丈夫なのか？）
・緊急事態宣言

での検索結果もDBに保存しておいた。

###リクエスト
ここは前回と同じ処理です。キーワードを変更するだけ。
`HttpGet`については
[Go言語でAPIを使ってXML形式のリクエストをDBに保存する](https://qiita.com/hhio67/items/38d7b51c0d19f67910ef)
を参照してください。

```Go
word := "ああ"
eWord := url.QueryEscape(word)
data := HttpGet("https://kokkai.ndl.go.jp/api/meeting_list?any=" + eWord)
```

###返すデータ
今回はヒットしたアイテムをDBに保存したときのキーワードも返したいので、取得した時とは別の構造体を定義する。

```Go
type Return struct {
	ID string `xml:"recordData>meetingRecord>issueID"`
	House string `xml:"recordData>meetingRecord>nameOfHouse"`
	Date string `xml:"recordData>meetingRecord>date"`
	Hitword string `xml:"recordData>meetingRecord>hitword"`
}

type ReturnList struct {
	XMLName xml.Name `xml:"data"`
	Recs []Return `xml:"records>record"`
}
```

###検索する
DB接続、レコード取得部分等は省略。
今回はヒット件数、キーワード別のヒット件数も返すのでそれらを格納する変数と後でXMLに変換して出力するデータを`mDataList`にReturnList型で定義

```Go
// ヒット件数
hit := 0
// ヒットしたレコードの検索時キーワード
keywords := make([]string, 0)
// 返すデータ
mDataList := ReturnList{}
```

検索して、ヒットすれば`hit`に1を加算し、そのデータのID,House,DateとヒットしたレコードのキーワードをReturn型のスライスである`mDataList.Recs`にappendする。
キーワード別の件数表示のため`keywords`にもappendする。
`result`にはDBから取得したデータが前回内容のRecordList型で入っている。

```Go
for _, rec := range result.Recs {
	for _, scan := range scanned {
		if rec.ID == scan.issueID {
			hit++
			mData := Return{
				ID: rec.ID,
				House: rec.House,
				Date: rec.Date,
				Keyword: scan.Hitword,
			}
			mDataList.Recs = append(mDataList.Recs, mData)
			keywords = append(keywords, scan.Keyword)
		}
	}
}
```
###結果を出力する
ヒットしなかった場合の出力
`os.Exit()`は`defer`を実行しないらしく`panic`も`Goexit`もなんか違う気がするのですが、いい方法があれば教えていただきたいです。

```Go
if hit == 0 {
	fmt.Println("ヒットしませんでした。")
	fmt.Printf("検索したキーワード: %v\n", word)
	os.Exit(1)
}
```
ヒットした場合の出力
mapを範囲ループするとGoでは順番が毎回異なってしまい、今回は順番が変わっても影響はないがなんとなく気持ち悪いので順番を固定しておく。

```Go
// どのキーワードで何件ヒットしたか
resultWords := make(map[string]int)
for _, w := range keywords {
	resultWords[w]++
}
// キーワード別件数の順序を保持するための処理
keys := make([]string, 0)
for i, _ := range resultWords {
	keys = append(keys, i)
}
sort.Slice(keys, func(i, j int) bool { return keys[i] > keys[j] })

// 結果をXML形式に変換
buf, err := xml.MarshalIndent(mDataList, "", " ")
if err != nil {
	log.Fatal(err)
}

// 出力
fmt.Printf("%v件が見つかりました。\n検索したキーワード: %v\n", hit, word)
fmt.Println("検索結果")
for _, w := range keys {
	fmt.Printf("%v: %v件\n", w, resultWords[w])
}
fmt.Println("\n以下、XML形式で結果をお知らせします。\n")
fmt.Println(string(buf))
```

##実行結果

```
 go run .

5件が見つかりました
検索したキーワード: ああ

検索結果
菅義偉: 2件
ワクチン: 2件
コロナ: 1件

以下、XML形式で結果をお知らせします。

<data>
 <records>
  <record>
   <recordData>
    <meetingRecord>
     <issueID>120115254X02020200529</issueID>
     <nameOfHouse>参議院</nameOfHouse>
     <date>2020-05-29</date>
     <keyword>コロナ</keyword>
    </meetingRecord>
   </recordData>
  </record>
  <record>
   <recordData>
    <meetingRecord>
     <issueID>120105254X01420200402</issueID>
     <nameOfHouse>衆議院</nameOfHouse>
     <date>2020-04-02</date>
     <keyword>ワクチン</keyword>
    </meetingRecord>
   </recordData>
  </record>
  <record>
   <recordData>
    <meetingRecord>
     <issueID>120105254X00720200227</issueID>
     <nameOfHouse>衆議院</nameOfHouse>
     <date>2020-02-27</date>
     <keyword>菅義偉</keyword>
    </meetingRecord>
   </recordData>
  </record>
  <record>
   <recordData>
    <meetingRecord>
     <issueID>120015254X00320191009</issueID>
     <nameOfHouse>参議院</nameOfHouse>
     <date>2019-10-09</date>
     <keyword>ワクチン</keyword>
    </meetingRecord>
   </recordData>
  </record>
  <record>
   <recordData>
    <meetingRecord>
     <issueID>119805254X03120190621</issueID>
     <nameOfHouse>衆議院</nameOfHouse>
     <date>2019-06-21</date>
     <keyword>菅義偉</keyword>
    </meetingRecord>
   </recordData>
  </record>
 </records>
</data>
```

##終わりに
読んでいただきありがとうございました。
もっと深い知識で記事を書けるようになりたいです。