2016-01-18

The site is back

Danilo Chang's Home Page


今天測試,發現可以改回來,所以回復原來的位址了。

我也同步更新了訊息在 comp.lang.tcl 上。

2016-01-12

總之,被我弄到掛掉了

Danilo Chang's Home Page


受到 Google Sites 的影響,我目前無法編輯 tcl-lmdb 網頁,原因不明。我在實驗的過程中沒注意到 Google Sites 的刪除與複製網站功能有次數限制, 所以目前無法還原,就先目前這個網頁吧,如果可以改回來我再還原。


我在 comp.lang.tcl 也發了一篇文章 (Tcl-lmdb v0.3, Tclunqlite and Tcljsonnet),所以暫時就這樣了。


PS.
基本上我還是很介意,因為一個 Google Sites 的網站管理者無法編輯修改自己的網頁是一件很瞎的事情,到底是哪裡出問題了?

PS.
原來的 tcl-lmdb 無法修改內容,只能夠使用附件管理的方式來增加與刪除附件。 經過思考,我找到了一個解法。首先建立一個 test 網頁,而後將舊的 tcl-lmdb 網頁放在 test 網頁下,複製內容到新的 tcl-lmdb 網頁,於是就可以有一個可以修改的 tcl-lmdb 網頁。 我不確定這是不是 Google Sites 的 bug,但是看來 Google 的雲端服務可能沒那麼可靠。

不過我已經在 Github 也放了相關的 source code 內容,接下來如果 domain name 可以恢復,應該就可以解決這件事情了,只是日常使用就會遇到使用障礙,如果連 Google 都是如此,我會對整個雲端產業抱持著質疑的態度。

2016-01-11

Convert Feedly.opml to SQLite3 database

目標是把 export 出來的 feedly.opml 檔案的資料放到 SQLite3 database 檔案。我寫完以後,才覺得…… 我好像多此一舉了。

#!/usr/bin/tclsh
package require tdom
package require tdbc::sqlite3

# prepare our sqlite database
tdbc::sqlite3::connection create db "feedly.db" 

set statement [db prepare {drop table if exists mylist}]
$statement execute
$statement close

set statement [db prepare {create table mylist (title TEXT, xmlUrl TEXT, htmlUrl TEXT, PRIMARY KEY (title))}]
$statement execute
$statement close

# setup feedly opml file path
set filename "/home/danilo/Personal/feedly.opml"
# parse XML data
set doc [dom parse [tDOM::xmlReadFile $filename "UTF-8"]]
set root [$doc documentElement]
set nodeList [$root selectNodes /opml/body/outline/outline]

set title ""
set xmlUrl ""
set htmlUrl ""
set statement [db prepare {insert into mylist values(:title, :xmlUrl, :htmlUrl)}]

foreach node $nodeList { 
    if {[catch {set title [$node getAttribute title]}] != 0} {
        set title ""
    }
    
    if {[catch {set xmlUrl [$node getAttribute xmlUrl]}] != 0} {
        set xmlUrl ""
    }
    
    if {[catch {set htmlUrl [$node getAttribute htmlUrl]}] != 0} {
        set htmlUrl ""
    }    
    
    $statement execute    
}

$statement close
db close

第二個則是讀出來確定有轉存成功:

#!/usr/bin/tclsh
package require tdbc::sqlite3

tdbc::sqlite3::connection create db "feedly.db" 

set statement [db prepare {SELECT * FROM mylist}]

$statement foreach row {
    puts "RSS title: [dict get $row title]"
    puts "RSS xmlUrl:  [dict get $row xmlUrl]"
    puts "RSS htmlUrl:  [dict get $row htmlUrl]"
}

$statement close
db close

2016-01-08

SQLite 與 PostgreSQL 釋出新版本

昨天我發現 SQLite 釋出了新的版本,3.10.0,所以已經下載了新的版本並且 build 出來使用簡單的 Tcl Script 測試。

然後接下來我就發現 PostgreSQL 也釋出了新版,9.5.0 版。我下載並且安裝以後,也使用一些簡單的 Tcl Script 進行測試,接下來需要閱讀文件來對 PostgreSQL 改版的項目進行更多的理解。

也就是新年一開始,open source relational database 三巨頭中的二個就釋出新版本了,很有趣的發展。

2016-01-02

Update couchdbtcl and solr4tcl

couchdbtcl -

在寫 solr4tcl 的時候,我才注意到,我沒有在 couchdbtcl 中處理 http::geturl 連線失敗的情況(其實也只是加 catch,避免直接吐出來錯誤訊息),所以加進來處理的部份。


solr4tcl -

在進行更多測試的時候,我發現 ping method 雖然拿到 ok,但是我填的 URL 不是正確的,所以更新了 URL。然後 ping method 的 method 是 HEAD,看起來不用設定什麼 Accept header,我也更正了這部份。

2015-12-29

solr4tcl v0.1: A lightweight Tcl client interface to Apache Solr

Homepage


solr4tcl

About

A lightweight Tcl client interface to Apache Solr. The library consists of a single Tcl Module file.

solr4tcl is using Tcl built-in package http to send request to Aapache Solr.

This library requires package tdom.

Interface

The library has 1 TclOO class, Solr_Request.

Provide below things:
  • A simple search interface
  • A simple interface to add, delete and update documents to the index (using XML Formatted Index Updates)
  • Uploading file data by using Apache Tika

一些說明


經過閱讀 Apache Solr 的文件以後,使用 Tcl http package 送出 request 並且得到結果的記錄。應該是 work 的,起碼沒有出現網頁找不到或者是錯誤回應。

更新 2016/01/02:
雖然都是傳回 OK,但是 ping method 填的參數不是完全正確,我試著更新一份正確的。

2015-12-28

Apache Solr test code

solr4tcl-0.1.tm

# solr4tcl --
#

package require Tcl 8.6
package require TclOO
package require http
package require tdom

package provide solr4tcl 0.1


#
# Solr_Request class: handle send request to Apache Solr
#
oo::class create Solr_Request {
    variable server
    variable path
    variable solr_writer

    constructor {SERVER} {
        set server $SERVER
        set path ""

        set solr_writer "xml"
    }

    destructor {
    }

    method setDocumentPath {PATH} {
        set path $PATH
    }

    #
    # support type: xml, json and cvs
    #
    method setSolrWriter {WRITER} {
        set solr_writer $WRITER
    }

    method send_request {url method {headers ""} {data ""}} {
        variable tok

        if {[string length $data] < 1} {
            if {[catch {set tok [http::geturl $url -method $method \
                -headers $headers]}]} {
                return "error"
            }
        } else {
            if {[catch {set tok [http::geturl $url -method $method \
                -headers $headers -query $data]}]} {
                return "error"
            }
        }

        if {[string compare -nocase $method "HEAD"] == 1} {
            set res [http::data $tok]
        } else {
            set res [http::status $tok]
        }

        http::cleanup $tok
        return $res
    }

    #
    # Call the /admin/ping servlet
    #
    method ping {} {
        set myurl "$server/solr"

        if {[string length $path] < 1} {
            append myurl "/admin/ping"
        } else {
            append myurl "/$path/admin/ping"
        }

        set headerl ""
        set res [my send_request $myurl HEAD $headerl]
        return $res
    }

    #
    # Simple Search interface
    # params is a list, give this funcition name-value pair parameter
    #
    method search {query {offset 0} {limit 10} {params ""}} {
        set myurl "$server/solr"

        if {[string length $path] < 1} {
            append myurl "/select"
        } else {
            append myurl "/$path/select"
        }

        lappend params q $query
        lappend params wt $solr_writer
        lappend params start $offset
        lappend params rows $limit
        set querystring [http::formatQuery {*}$params]

        #
        # The return data format is defined by wt, $solr_writer setting.
        #
        set headerl [list Content-Type "application/x-www-form-urlencoded; charset=UTF-8"]
        set res [my send_request $myurl POST $headerl $querystring]

        return $res
    }

    #
    # parameters - a list include key-value pair
    #
    method add {parameters {OVERWRITE true} {BOOST "1.0"} {COMMIT true}} {
        # Try to build our XML document
        set doc [dom createDocument add]

        set root [$doc documentElement]
        $root setAttribute overwrite $OVERWRITE

        set docnode [$doc createElement doc]
        $docnode setAttribute boost $BOOST
        $root appendChild $docnode

        foreach {key value} $parameters {
            set node [$doc createElement field]
            $node setAttribute name $key
            $node appendChild [$doc createTextNode $value]
            $docnode appendChild $node
        }

        set myaddString [$root asXML]
        set myurl "$server/solr"

        set params [list commit $COMMIT]
        set querystring [http::formatQuery {*}$params]

        if {[string length $path] < 1} {
            append myurl "/update?$querystring"
        } else {
            append myurl "/$path/update?$querystring"
        }

        set headerl [list Content-Type "text/xml; charset=UTF-8"]
        set res [my send_request $myurl POST $headerl $myaddString]

        return $res
    }

    #
    # xmldata - xml data string want to add
    #
    method addData {xmldata {COMMIT true}} {
        set myurl "$server/solr"

        set params [list commit $COMMIT]
        set querystring [http::formatQuery {*}$params]

        if {[string length $path] < 1} {
            append myurl "/update?$querystring"
        } else {
            append myurl "/$path/update?$querystring"
        }

        set headerl [list Content-Type "text/xml; charset=UTF-8"]
        set res [my send_request $myurl POST $headerl $xmldata]

        return $res
    }

    #
    # The <commit>  operation writes all documents loaded since the last
    # commit to one or more segment files on the disk
    #
    method commit {{WAITSEARCHER true} {EXPUNGEDELETES false}} {
        set mycommitString "<commit waitSearcher=\"$WAITSEARCHER\" expungeDeletes=\"$EXPUNGEDELETES\"/>"
        set myurl "$server/solr"

        if {[string length $path] < 1} {
            append myurl "/update"
        } else {
            append myurl "/$path/update"
        }

        set headerl [list Content-Type "text/xml; charset=UTF-8"]
        set res [my send_request $myurl POST $headerl $mycommitString]

        return $res
    }

    #
    # The <optimize> operation requests Solr to merge internal data structures
    # in order to improve search performance.
    #
    method optimize {{WAITSEARCHER true} {MAXSegments 1}} {
        set myoptimizeString "<optimize waitSearcher=\"$WAITSEARCHER\" maxSegments=\"$MAXSegments\"/>"
        set myurl "$server/solr"

        if {[string length $path] < 1} {
            append myurl "/update"
        } else {
            append myurl "/$path/update"
        }

        set headerl [list Content-Type "text/xml; charset=UTF-8"]
        set res [my send_request $myurl POST $headerl $myoptimizeString]

        return $res
    }

    #
    #  "Delete by ID" deletes the document with the specified ID
    #
    method deleteById {ID {COMMIT true}} {
        set mydeleteString "<delete><id>$ID</id></delete>"
        set myurl "$server/solr"

        set params [list commit $COMMIT]
        set querystring [http::formatQuery {*}$params]

        if {[string length $path] < 1} {
            append myurl "/update?$querystring"
        } else {
            append myurl "/$path/update?$querystring"
        }

        set headerl [list Content-Type "text/xml; charset=UTF-8"]
        set res [my send_request $myurl POST $headerl $mydeleteString]

        return $res
    }

    #
    #  "Delete by Query" deletes all documents matching a specified query
    #
    method deleteByQuery {QUERY {COMMIT true}} {
        set mydeleteString "<delete><query>$QUERY</query></delete>"
        set myurl "$server/solr"

        set params [list commit $COMMIT]
        set querystring [http::formatQuery {*}$params]

        if {[string length $path] < 1} {
            append myurl "/update?$querystring"
        } else {
            append myurl "/$path/update?$querystring"
        }

        set headerl [list Content-Type "text/xml; charset=UTF-8"]
        set res [my send_request $myurl POST $headerl $mydeleteString]

        return $res
    }

    #
    #  Uploading Data by using Apache Tika
    #
    method upload {fileContent {FILENAME ""} {COMMIT true} {ExtractOnly false} {params ""}} {
        set myurl "$server/solr"

        lappend params commit $COMMIT extractOnly $ExtractOnly

        if {[string length $FILENAME] > 1} {
            lappend params "resource.name" $FILENAME
        }

        set querystring [http::formatQuery {*}$params]

        if {[string length $path] < 1} {
            append myurl "/update/extract?$querystring"
        } else {
            append myurl "/$path/update/extract?$querystring"
        }

        set headerl [list Content-Type "text/xml; charset=UTF-8"]
        set res [my send_request $myurl POST $headerl $fileContent]

        return $res
    }
}



Test code:

#!/usr/bin/tclsh

package require solr4tcl

set solrresquest [Solr_Request new "http://localhost:8983"]
$solrresquest setDocumentPath gettingstarted

# support xml, json or csv
$solrresquest setSolrWriter xml

set res [$solrresquest ping]
if {[string compare -nocase $res "ok"]!=0} {
    puts "Apache Solr server returns not OK, close."
    exit
}

set res [$solrresquest search "foundation"]
puts "Search result:"
puts $res

set parameters [list authors "Patrick Eagar" subject "Sports" dd "796.35" \
                     isdn "0002166313" yearpub "1982" publisher "Collins"]
set res [$solrresquest add $parameters true]
puts $res

set res [$solrresquest commit]
puts $res

set res [$solrresquest optimize]
puts $res

set res [$solrresquest deleteById "0002166313"]
puts $res

set res [$solrresquest deleteByQuery "publisher:Collins"]
puts $res

#set size [file size "./solr-word.pdf"] 
#set fd [open "./solr-word.pdf" {RDWR BINARY}]  
#fconfigure $fd -blocking 1 -encoding binary -translation binary 
#set data [read $fd $size]  
#close $fd  
#set res [$solrresquest upload $data "solr-word.pdf"]
#puts $res

set res [$solrresquest search "pdf"]
puts "Search result:"
puts $res



Apache Solr 沒有像 Apache CouchDB 一樣,有將 REST API 做一個整理表,所以我只是對 search 的部份做一個簡單的 search method,然後確定有正確的回應回來(而不是無法連線,或者是傳回來一個網頁不存在的 404 錯誤網頁,如果有執行 Solr 的 Solr Quick Start)。

更新 2015/12/29:
加入上傳檔案和 index 處理的部份

更新 2016/01/02:
更新 method ping 的參數

2015-12-26

PostgreSQL extension: hstore

PostgreSQL: hstore


厲害的地方在於,不只可以放一個 key-value pair,而是可以在一個 column 上放一個以上的 key-value pair。

在 Windows 平台上測試最近安裝的  PostgreSQL 9.4.5 是否有支援 hstore:

package require tdbc::postgres
tdbc::postgres::connection create db -user postgres -password postgres -port 5432

set statement [db prepare {
     CREATE EXTENSION hstore
}]

$statement foreach row {
 puts $row
}

$statement close
db close


得到 extension "hstore" already exists 的答案。

更新:列出目前系統上的 extension name:

package require tdbc::postgres
tdbc::postgres::connection create db -user postgres -password postgres -port 5432

set statement [db prepare {
     select extname from pg_extension
}]

$statement foreach row {
 puts $row
}

$statement close
db close

2015-12-24

SSDB 與 retcl

SSDB

Features

  • An alternative to Redis, 100x Redis
  • LevelDB client-server support, written in C/C++
  • Redis API compatible, Redis clients are supported
  • Designed to store collection data, such as list, hash, zset...
  • Client API supports including C++, PHP, Python, Java, Go
  • Persistent queue service
  • Replication(master-slave), load balance

retcl: Tcl client library for Redis


看完了 SSDB 寫的 features,所以我可以拿一個 Redis 的 client 來連線並且測試囉?閱讀 SSDB 的文件,SSDB 的 port 是 8888。

retcl create r 127.0.0.1 8888

接下來使用 retcl 的範例進行一些測試,真的可以直接使用(除了 Publish / Subscribe and callbacks, Handling errors 我沒試,另外 retcl  Commands pipelining 的範例要先 set i 0,總之要先設定 i 的值),SSDB 有些 command 回來的反應和說明不太一致(例如 info 只傳回 OK),不過大體上 SSDB 確實是 Redis API compatible,也就是如果 Redis client 寫的彈性一點,理論上就可以無痛的拿來 SSDB 這邊使用。

PS. 不要嘗試在 Windows 平台使用 SSDB,雖然在網路上可以找到 binary files,但是跟作者網站上講的一樣,不建議在 Windows 平台上使用。

couchdbtcl: rename oauth-1.0.tm

couchdbtcl


我沒注意到 tcllib 中已經有 package 名稱是 oauth,雖然我測試的時候可以正確執行,不過為了避免套件名稱衝突,所以 oauth-1.0.tm 被我改名為 myoauth-1.0.tm,提供的套件從 oauth 變成 myoauth。