SQLite3の提供する集約関数は avg, count, max, min, sum, totalが全てで、どれも数値を引数にとる。sumが与えられた数値の合計を返すように、与えられた文字列を全て連結して返す集約関数(MySQLの GROUP_CONCATがまさしくそれ)がないものかと探していた。それが無いなら無いで SELECT, JOIN, UNION, CASEといった標準的なものを使って、特定の列の値を(行をまたいで)連結したりできないものかと考えたけど、行と行の独立は破れなかった。
話は変わって、これ↓は昨日見つけた目から鱗ものの文書。
http://www.geocities.jp/mickindex/database/idx_database.html
対象読者として次のような項目が挙げられている。
- なぜ"関係"モデルという抽象的な用語を使うのか分からない。"表"モデルでいいじゃない。
- なぜ「= NULL」ではなく「IS NULL」と書かねばならないのか分からない。
- E.F.Codd、C.J.Date、J.Celko、F.Pascal の本を読んだことがない。
- IN述語とEXISTS述語では、IN の方が直観的に分かりやすいから好きだ。
- IN述語、EXISTS述語、LIKE述語・・・「述語」って何だ?
- テーブル設計のときは滅多に NOT NULL制約を付けない。しかもそれが大罪であることの自覚がない。
- SQL で CASE式を使ったことがない。
「INは使ったことあるけど EXISTSは知らない」「何故 = NULLと書いたものが IS NULLと同じ結果を返さないのか分からない」「 CASE?そんなんあった?」「DEFAULT '' は多用するけど、NOT NULLは付けてない。付けるべき理由があるなら知りたいよ」と、冒頭からがっちりハートを鷲掴み。
このサイトの文書がどれも興味深く、有用なのはさておいて、CASEの存在を今まで知らなかったことに少なからずショックを受けた。SQLiteの本家サイトにもちゃんと記述されているというのに。(→Query Language Understood by SQLite: expression)
そこで改めて SQLiteでできることをおさらいしてみたところ発見されたのが create_function。(→C/C++ Interface For SQLite Version 3)
C/C++ APIだから Rubyから使うには dl を使わなあかんのかと思ったら、何と SQLiteといつもセットで利用しているsqlite3-rubyの SQLite3::Databaseオブジェクトにはその名も create_aggregateなんてメソッドが存在するのですねえ。一体今までどこに目を付けていたのかと……。
そんなわけで、無いなら作ってしまえ文字列連結集約関数〜♪。
require 'sqlite3' database = SQLite3::Database.new('hoge.db'); # concatという不定数の引数をとる集約関数を作成。 # 第一引数(val)は連結される文字列。 # 第二引数(sep)は valと valの間に挿入されるセパレータ。 # 第三引数(sortval)は valを連結する前に並び替えたい場合にソートキーとして利用される文字列/数値。(省略されたり NULLの場合はソートしない) # 第四引数(desc)はソートの昇順/降順を切り替える。(省略/NULL=>昇順, その他=>降順) database.create_aggregate(name='concat', arity=-1){ step {|func, val, sep, sortval, desc| func['separator'] = sep.to_s; func['sortdesc'] = !(desc.nil? || desc.null?); func['needsort'] = (func['needsort'] || !(sortval.nil? || sortval.null?)); func['sortvaltype'] ||= (sortval.nil? || sortval.null?) ? nil : {:int=>:to_i, :float=>:to_f, :blob=>:to_blob, :text=>:to_s}[sortval.type]; func['array'] ||= []; func['array'].push([ (sortval.nil? || sortval.null?) ? nil : sortval.send(func['sortvaltype']), val.to_s ]); } finalize {|func| arr = (func['array'] || []); arr = arr.sort_by{|x| x.nil? ? {:to_i=>0, :to_f=>0.0, :to_s=>''}[func['sortvaltype']] : x.first.dup } if(func['needsort']); arr.reverse! if(func['sortdesc']); func.result = arr.map{|x| x.last }.join(func['separator']); } }
というわけで、探し物が見つかったということに満足しつつ、文字列の連結は集約関数でなく ERBスクリプトで行っている現在。
データベースに対するリードオンリーアクセスを保証したりもできるのねん。
CGI経由で渡された SQLも安全に実行できそうじゃね?
#!ruby -T4 eval(ENV['QUERY_STRING'])
と同程度かそれ以上に。
http://www.sqlite.org/capi3ref.html#sqlite3_set_authorizer から引用しておく。
The intent of this routine is to allow applications to safely execute user-entered SQL. An appropriate callback can deny the user-entered SQL access certain operations (ex: anything that changes the database) or to deny access to certain tables or columns within the database.
これらが今 自分が必要としてるものの名前だということに気付きました。
るびま で Railsの記事を読んだときには Railsが結局何なのかが分かりませんでした。
るびま の 3号では複数の O/Rマッパーが紹介されてました。読み飛ばしてました。
MVCって単語はもう何年、目にして、素通りしてきただろう。
15アイテムずつ表示するのは一緒だけど……
以前は「おすすめの理由は」っていうリンクがあるだけだったのに、「〜などを評価されたお客様におすすめします」って、最初から一冊だけだけどおすすめの理由が表示されてる。これはページの移動が減るなあ。
出版日も表示されてる。
マウスオーバーに合わせて「嫌い(☆)」「好きではない(☆☆)」「普通(☆☆☆)」「好き(☆☆☆☆)」「大好き(☆☆☆☆☆)」と表示され、クリックすると評価が送信される。ページ遷移はない。
「持っています」「興味がありません」も従来はハイパーリンクだったものが、ページ遷移無しでアマゾンに送信されるようになっている。
ソースを覗いてみると、Javascriptが使用可能なら
window.location.href="〜"
とやってる。これは普通ページを移動するときに使うけど、そうはならない。からくりは以下のレスポンス。(注: id と名の付くものをそのまま載せるのはまずいかもしれないので正規表現に置換してある)
HTTP/1.x 204 NoContent Date: Thu, 06 Apr 2006 18:42:18 GMT Server: Server x-amz-id-1: [0-9A-Z]{20} x-amz-id-2: [0-9A-Za-z/=]{44} Vary: Accept-Encoding,User-Agent Content-Encoding: gzip Connection: close Content-Type: text/html; charset=Shift_JIS
Firefoxは 204 NoContent を受け取ったものだから新しいページを表示できなくて古いページを表示し続けるというわけ。
Javascriptが使用不可ならフォームを使って、(確認してないけど)スクリプトと同じ内容を POSTしてる。レスポンスも同じ 204 NoContentだろう。
評価などを変更しましたか? ここをクリックするとおすすめ商品の情報が更新されます。
というのがページ下部にある。内容は単なる javascript:location.reload() だけど需要のあるリンクだと思う。表示中のページに加えた評価 を加味した最新のおすすめを表示したいと思うもの。
キーボード派は F5を押せばいいと言うでしょうがキーボード派はお呼びでない様子。
「興味がありません」を例にとるとソースはこう↓。丸々スクリプトで中身を出力してる。(スクリプトOFFなブラウザには <noscript><form>〜</form></noscript>が用意してあるので重複して表示しないように。)
<td style="white-space: nowrap;"> <script language="Javascript" type="text/javascript">amz_js_showNotInterested(asin, notInterested);</script> </td>
amz_js_showNotInterested()が何を返すかというと
function amz_js_showNotInterested(asin, notInterested){ var imageID = "notInterested." + asin; document.write("<img src="+'"'++ checkboxImages[notInterested] ++'"'+""); document.write("onclick="+'"'+"amz_js_sendNotInterested('" + asin + "', 'alt');"+'"'+" "); document.write("onmouseover="+'"'+"amz_js_notInterestedMouseOver('" + asin + "', 'alt');"+'"'+" "); document.write("onmouseout="+'"'+"amz_js_notInterestedMouseOut('" + asin + "', 'alt');"+'"'+" "); document.write("id="+'"'++ imageID ++'"'+" "); document.write("border="+'"'+"0"+'"'+" valign="+'"'+"bottom"+'"'+" />"); }
一つの <img>要素。リンクでないただの画像にフォーカスは移りません。
というわけで、スクリプトが OFFの場合はフォームと画像ボタンが表示されるのでキーボードで操作できるはずだが、スクリプトONの場合はマウス必須。
☆の部分のソースを見ると、☆の画像は一つで、6つの<area>を持つ<map>を適用することで個々の☆のマウスオーバー効果やクリックの処理をしている。評価送信のトリガはハイパーリンクを使ったものではなく <area onClick="〜" />。だから、☆にフォーカスは移るんだけど Enterを押しても反応しない。クリックをエミュレートするキーボードショートカットがあれば何とかなるが。
因みに ☆評価に対応した <noscript> なフォームは用意されてないのでスクリプトオフでは☆自体が表示されないはず。
改行とインデントをいじって HTML要素の内容を省略したソースが以下。
<html> <head> <title>Amazon.co.jp: おすすめ商品</title> <script language="Javascript1.1" type="text/javascript"></script> <style type="text/css"></style> <style type="text/css"></style> <style type="text/css"></style> </head> <body> <html> <head> <title></title> <style type="text/css"></style> <script language="Javascript1.1" type="text/javascript"></script> </head> <body bgcolor="#FFFFFF" link="#003399" alink="#FF9933" vlink="#996633" text="#000000"> …… </body> </html> </body> </html>
よくわからない HTML、ところどころ XHTML風味。何れにしろ Broken みたいなっ。
アマゾンみたいなサイトでは HTMLの理念理想よりブラウザ互換性の方が大事でしょうが、見映えと関係ない細かな部分ではソースの統一をして欲しいな。(<br />なんて <br>でいいでしょ)
これまでは 100件ちょっとで「次のページ」が無くなって打ち止めになったけど、現在 400件目を過ぎたところまで表示してる。ここまでくるとおすすめの精度が悪くて参考にならない。でも自分で止めるところを決められるのは良い。
* Ajaxを XmlHttpRequest, MSXMLオブジェクトを利用した、ページ遷移を伴わないサーバーとの通信(HTTP GET/POST)、(とそれによるページ更新)とするなら
バッテリーを買って家で交換しようと思って帰ろうとしたら、とうとうメーターが点かないくらいにへたってて、セルが回らない(キュ、ル、ルではなくチチチチチ。全く回ってない)のは当然としてキックでもエンジンがかからないので駐車場で交換した。
例によって Home Editionには secpol.msc(ローカルセキュリティポリシー) が存在しないので、そんなんばっかヽ(`Д´)ノ、「アカウント : ローカルアカウントの空のパスワードをコンソールログオンのみに制限する」を無効に設定できない。
かといって、たった一つのタスクのために毎日のログオン手順にキーストロークを一つ(以上)追加するのも嫌だ。
結局タスクのプロパティで「ログオンしている場合にのみ実行する」オプションをチェックすることで空パスワードの問題を回避できた。
ということで、このチェックが ONであろうが OFFであろうが大差なし。
あちらを立てればこちらが立たず。Firefoxは about:configや user.jsで簡単に設定が変更できるので、メモリ容量と相談してお好みでってことですな。
映画の原作本を読み漁ってた中学生の時に一度読んだ本。気に入って『マウント・ドラゴン』と『地底大戦—レリック2』も図書館に入るや借りて読んだ。マイクル・クライトンが好きなら気に入ると思う。
この後プレストン繋がり(リチャード・プレストンとダグラス・プレストンは兄弟)で『ホット・ゾーン』『コブラの眼』、『デーモンズ・アイ』(未読)と読む本が広がっていった。
この時期に見つけたんだった。背表紙がレリックと同じ扶桑社ミステリーの赤色だから。レイモンはエロス&バイオレンスと裏表紙に書いてある様にプレストンと方向性は違うけど「当たり」ではあった。(6歳の自分の娘をレイプして捕まってた男が出所してきて、妻と娘を追いかける。行きがけに 9歳の女の子を拉致して旅のお供に……。そしてダークなエンディング)
何のためのチェックページなのかと。
実際に今日も他の本を買う前にはケータイでチェックしたんだけど、「持っているかもしれない」という疑いが微塵も頭に浮かばなければチェック手段が用意されてても意味ないわな。
いじめられっ子の荻野くんに綺麗な彼女ができた。二人は相思相愛で、思わず「幸せだ」と口にしてしまうほど幸せな高校生活。荻野の悩みや考えに共感できるから、読んでる自分も二人の関係を壊したくないと思っている。
なのに、一巻から六巻まで読んでて緊張の連続。古谷 実はこの幸せな二人に何度も何度も執拗に悪意をぶつけてくる。二人の幸せの何と危うく儚いものか。それでも一線を越えることはなく二人の関係は六巻まで壊れずに続く。
最終話。荻野は社会人になっている。彼女(南雲さん)が全てのように感じていたこともある荻野だが、南雲とはとうに別れており現在は違う女性と交際中。妊娠中だという南雲の姿を想像しつつも現在の彼女に愛してるという荻野に南雲への未練はなく、(最終マイナス一)話まで感じさせていた、南雲さんを失ったらこいつ壊れんじゃないの、ってな危うさももうない。荻野は成長していたのだ。
シガテラと違い、救われないエンドの 安達哲『さくらの唄』を先に読んでしまった人に勧めておく。
マンガでもアニメでもなく、西の善き乙女はDO MY BESTでしょ? (わたしが知らないスゴ本は、きっとあなたが読んでいる)
ツガノガク、三冊目のコミックスが見つかんね。京アニのせいだ。
JavaScript組み込みの Arrayオブジェクトなんて push(), pop(), lengthみたいにプリミティブなメソッドしか備えてないから、例えば map()が使いたい時は毎回
Array.prototype.map = function(f) { ほにゃらら }
みたいなのを最初に書くか、JScript限定でいいなら
function Array.prototype.map(f) { ほにゃらら }
をソースファイルのどこかに書いておくかしていた。
prototype.jsにはビルトインオブジェクトを拡張する便利なメソッド、それも Rubyでなじんだのと同じ名前のメソッドやイテレータの概念が取り入れられてるので、CGIスクリプト(サーバーサイド)で Rubyを使う人間がクライアントサイドで JavaScriptを使うときには手放せなくなりそう。