将Postgresql数组直接读入Golang Slice

时间:2015-05-27 17:18:46

标签: postgresql go

我有一个查询,它返回一行,其中包含一个包含字符串数组的列(character varying[]):

{http://wp.me/p62MJv-Jc,http://tyrant.click/1LGBoD6}

有没有简单的方法可以直接将其读入Golang切片? E.g。

var arr []string

for rows.Next() {
    rows.Scan(&arr)
    fmt.Println(len(arr))
}

产地:

0

4 个答案:

答案 0 :(得分:10)

我认为这应该可以胜任。在sql中使用 array_to_json 。然后解组 json-string到golang切片

sql-> select array_to_json(arr) from ....

var arrStr string
var arr []string

for rows.Next() {
    rows.Scan(&arrStr)
    json.Unmarshal([]byte(arrStr), &arr)
    fmt.Println(len(arr))
}

答案 1 :(得分:7)

正如Victor在原始帖子的评论中提到的那样,this帖子通过对pg.Array()的解释很好地回答了这个问题。

直接从链接中获取:

  

要将Postgres数组值读入Go切片,请使用:

func getTags(db *sql.DB, title string) (tags []string) {
    // the select query, returning 1 column of array type
    sel := "SELECT tags FROM posts WHERE title=$1"

    // wrap the output parameter in pq.Array for receiving into it
    if err := db.QueryRow(sel, title).Scan(pq.Array(&tags)); err != nil {
        log.Fatal(err)
    }

    return
}

我也已经在自己的项目中使用了此功能,因此可以确认它是否有效。

答案 2 :(得分:2)

目前,没有直接的方法可以使用lib/pq库将PostgreSQL数组加载到Go切片中。可能在某个时候,但是对于图书馆本身是否应该透明地处理这样的事情存在争议。

但是,一个选项是将结果加载到字符串(看起来像{item1,item2,"comma,item"})中,然后使用正则表达式将该字符串拆分为字符串切片,如下面的代码所示(部分采用)来自安德鲁·哈里斯的this Gist

import (
    "regexp"
    "strings"
)

var (
    // unquoted array values must not contain: (" , \ { } whitespace NULL)
    // and must be at least one char
    unquotedChar  = `[^",\\{}\s(NULL)]`
    unquotedValue = fmt.Sprintf("(%s)+", unquotedChar)

    // quoted array values are surrounded by double quotes, can be any
    // character except " or \, which must be backslash escaped:
    quotedChar  = `[^"\\]|\\"|\\\\`
    quotedValue = fmt.Sprintf("\"(%s)*\"", quotedChar)

    // an array value may be either quoted or unquoted:
    arrayValue = fmt.Sprintf("(?P<value>(%s|%s))", unquotedValue, quotedValue)

    // Array values are separated with a comma IF there is more than one value:
    arrayExp = regexp.MustCompile(fmt.Sprintf("((%s)(,)?)", arrayValue))
)

// Parse the output string from the array type.
// Regex used: (((?P<value>(([^",\\{}\s(NULL)])+|"([^"\\]|\\"|\\\\)*")))(,)?)
func pgArrayToSlice(array string) []string {
    var valueIndex int
    results := make([]string, 0)
    matches := arrayExp.FindAllStringSubmatch(array, -1)
    for _, match := range matches {
        s := match[valueIndex]
        // the string _might_ be wrapped in quotes, so trim them:
        s = strings.Trim(s, "\"")
        results = append(results, s)
    }
    return results
}

以下是它的使用方法:

rows, err := db.Query("SELECT link FROM links")
if err != nil {
    panic(err)
}
var tmp string
for rows.Next() {
    rows.Scan(&tmp)
    links := pgArrayToSlice(tmp)
    fmt.Println(len(links), links)
}

在数据库中包含以下内容:

# \d links
    Table "public.links"
 Column |  Type  | Modifiers 
--------+--------+-----------
 link   | text[] | 

# select * from links;
             link             
------------------------------
 {this,that}
 {another,thing}

 {}
 {"test,123","one,two,three"}
(5 rows)

这是上面的Go代码输出的内容:

2 []string{"this,", "that"}
2 []string{"another,", "thing"}
2 []string{"another,", "thing"}
0 []string{}
2 []string{"test,123\",", "one,two,three"}

答案 3 :(得分:1)

我已经看到了这个代码的各种变体,但是对于某些测试集它并不适合我。

这里我写的东西处理了我抛出的所有测试值(测试用例如下)。它的速度也提高了约80%。

scanTests := []struct {
  in   string
  out  []string
}{
  {"{one,two}", []string{"one", "two"}},
  {`{"one, sdf",two}`, []string{"one, sdf", "two"}},
  {`{"\"one\"",two}`, []string{`"one"`, "two"}},
  {`{"\\one\\",two}`, []string{`\one\`, "two"}},
  {`{"{one}",two}`, []string{`{one}`, "two"}},
  {`{"one two"}`, []string{`one two`}},
  {`{"one,two"}`, []string{`one,two`}},
  {`{abcdef:83bf98cc-fec9-4e77-b4cf-99f9fb6655fa-0NH:zxcvzxc:wers:vxdfw-asdf-asdf}`, []string{"abcdef:83bf98cc-fec9-4e77-b4cf-99f9fb6655fa-0NH:zxcvzxc:wers:vxdfw-asdf-asdf"}},
  {`{"",two}`, []string{"","two"}},
  {`{" ","NULL"}`, []string{" ",""}},
}

以下是测试用例:

{{1}}