V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
balabalaguguji
V2EX  ›  Go 编程语言

看到 Go 与 MongoDB 的交互方式,我想放弃 Go 了

  •  
  •   balabalaguguji · 2021-10-24 11:48:43 +08:00 · 14482 次点击
    这是一个创建于 1132 天前的主题,其中的信息可能已经有所发展或是发生改变。

    之前习惯了 python/js 这种语法,感觉很自然很方便。

    今天看了下 MongoDB 官方的 Go 接口,哎呀,那交互方式,真的是痛苦。

    例如查询用户为 1 的用户:{userid: 1},在 Go 里面你还得包裹为 bson.D{{"userid", 1}}

    返回的结果是一个索引,要 Decode 下,Docode 还需要传递一个结构体过去。

    还得传递一个 context (还没看为啥要这么做,其他语言不用)

    感觉一点也不方便,代码很多不美观不优雅,习惯了 js/python 这种比较简单直观的语法,难以接受呀。

    感觉 Google 最近出的东西,语法都那么特立独行的,还有一个是死亡嵌套 Flutter ,嵌套到怀疑人生。

    官方教程: https://www.mongodb.com/blog/post/mongodb-go-driver-tutorial

    filter := bson.D{{"name", "Ash"}}
    
    update := bson.D{
        {"$inc", bson.D{
            {"age", 1},
        }},
    }
    
    updateResult, err := collection.UpdateOne(context.TODO(), filter, update)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("Matched %v documents and updated %v documents.\n", updateResult.MatchedCount, updateResult.ModifiedCount)
    
    99 条回复    2021-11-12 16:41:40 +08:00
    EPr2hh6LADQWqRVH
        1
    EPr2hh6LADQWqRVH  
       2021-10-24 11:54:16 +08:00
    主要是因为 Go 里你可以自己安排内存,而这个库不想替你管理内存。

    其实别的语言也一样,当一个组件不想建堆内存的时候,代码都一样拧巴。
    EPr2hh6LADQWqRVH
        2
    EPr2hh6LADQWqRVH  
       2021-10-24 11:57:44 +08:00
    哦我收回我的话,这不是主要原因。

    主要原因就是 Go 描述动态对象太拧巴了
    Rache1
        3
    Rache1  
       2021-10-24 12:00:59 +08:00
    封装一下,应该还行
    codehz
        4
    codehz  
       2021-10-24 12:02:39 +08:00 via Android
    context 这个可以洗一下,它可以用于在请求撤销的时候撤回发出去的其他请求,避免了资源的浪费,在 go 的主要应用场景--中间件方面意义重大
    JS 里也有类似的东西,叫 AbortSignal
    (虽然 context 功能上更多一点,这里只考虑它的这个功能(
    cheng6563
        5
    cheng6563  
       2021-10-24 12:43:29 +08:00
    静态语音就是这样的,对接第三方的时候都是先定义模型,再用这个模型的实例去做交互。
    通常用泛型会简化一点这种操作,但是 go 。。。
    balabalaguguji
        6
    balabalaguguji  
    OP
       2021-10-24 13:07:09 +08:00
    @Rache1 #3 好像不好封装,Go 里面没有支持任意类型的 Map
    balabalaguguji
        7
    balabalaguguji  
    OP
       2021-10-24 13:08:10 +08:00
    @codehz #4 这个特性感觉我都用不到,但是每次都得写,感觉就有点强制多余了
    balabalaguguji
        8
    balabalaguguji  
    OP
       2021-10-24 13:09:13 +08:00
    @cheng6563 #5 嗯,静态语言应该都是要先定义模型,太不灵活了。
    XTTX
        9
    XTTX  
       2021-10-24 13:33:26 +08:00
    官方 doc 就劝退了, 那你还是千万别用 go+mongoDB 了
    XTTX
        10
    XTTX  
       2021-10-24 13:34:26 +08:00   ❤️ 2
    //FindReadingListArticleAction finds if articleAction exists by checking articleId and ownerID
    func (aam *articleActionDB) FindUserArticleActionArticle(ownerID primitive.ObjectID) (*[]Article, error) {
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()

    client, err := mongo.Connect(ctx, options.Client().ApplyURI(aam.connectionString))
    if err != nil {
    return nil, err
    }
    defer client.Disconnect(ctx)
    collection := client.Database(aam.dataBaseName).Collection(aam.collectionName)
    matchStage := bson.D{{"$match", bson.M{"ownerID": ownerID}}}
    projectStage := bson.D{{"$project", bson.M{"articleID": 1}}}

    type articleID struct {
    ArticleID primitive.ObjectID `bson:"articleID"`
    }

    var articleIDs []articleID

    cursor, err := collection.Aggregate(ctx, mongo.Pipeline{matchStage, projectStage})
    if err != nil {
    fmt.Println(err)
    return nil, err
    }
    if err = cursor.All(ctx, &articleIDs); err != nil {
    return nil, err
    }
    articleClient, err := mongo.Connect(ctx, options.Client().ApplyURI(aam.connectionString))
    if err != nil {
    return nil, err
    }
    defer articleClient.Disconnect(ctx)
    articleCollection := client.Database("crawler").Collection("article")
    var articles []Article
    var article Article
    for i := 0; i < len(articleIDs); i++ {
    err := articleCollection.FindOne(ctx, bson.M{"_id": articleIDs[i].ArticleID}).Decode(&article)
    if err != nil {
    fmt.Println(err)
    }
    articles = append(articles, article)
    }
    return &articles, nil
    }

    我 6 个月前写的,我现在完全看不懂当初写了什么寂寞
    XTTX
        11
    XTTX  
       2021-10-24 13:39:57 +08:00
    ````
    //FindFinishedListArticleAction finds if articleAction exists by checking articleId and ownerID
    func (aam *articleActionDB) FindFinishedListArticleAction(ownerID primitive.ObjectID) (*[]Article, error) {
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()

    client, err := mongo.Connect(ctx, options.Client().ApplyURI(aam.connectionString))
    if err != nil {
    return nil, err
    }
    defer client.Disconnect(ctx)
    collection := client.Database(aam.dataBaseName).Collection(aam.collectionName)
    matchStage := bson.D{{"$match", bson.M{"ownerID": ownerID}}}
    matchStage1 := bson.D{{"$match",
    bson.M{"finisheddate": bson.M{"$exists": true}}}}
    projectStage := bson.D{{"$project", bson.M{"articleID": 1}}}

    type articleID struct {
    ArticleID primitive.ObjectID `bson:"articleID"`
    }

    var articleIDs []articleID

    cursor, err := collection.Aggregate(ctx, mongo.Pipeline{matchStage, matchStage1, projectStage})
    if err != nil {
    fmt.Println(err)
    return nil, err
    }
    if err = cursor.All(ctx, &articleIDs); err != nil {
    return nil, err
    }
    articleClient, err := mongo.Connect(ctx, options.Client().ApplyURI(aam.connectionString))
    if err != nil {
    return nil, err
    }
    defer articleClient.Disconnect(ctx)
    articleCollection := client.Database("crawler").Collection("article")
    var articles []Article
    var article Article
    for i := 0; i < len(articleIDs); i++ {
    err := articleCollection.FindOne(ctx, bson.M{"_id": articleIDs[i].ArticleID}).Decode(&article)
    if err != nil {
    fmt.Println(err)
    }
    articles = append(articles, article)
    }
    return &articles, nil
    }
    ````

    这种东西写的难受, 看得更难受。
    pengtdyd
        12
    pengtdyd  
       2021-10-24 13:41:20 +08:00   ❤️ 7
    说这话的人显然没有写过 rust
    wxlwsy
        13
    wxlwsy  
       2021-10-24 14:07:33 +08:00
    你应该没写过原生 JDBC 代码,否则也不会这么说了。
    darksword21
        14
    darksword21  
       2021-10-24 14:15:13 +08:00 via iPhone
    那是相当难受了,特别层级很多的话每层都要检查 err
    Kisesy
        15
    Kisesy  
       2021-10-24 14:20:09 +08:00
    mongo 官方库就是不好用,有点底层,当然我没用过不好评价
    他这设计有些不对,各种操作必须要传 ctx 这也太那啥了,本来 Go 的操作就繁琐
    完全可以写成 collection.WithCtx(context.TODO()).UpdateOne(filter, update),想用 ctx 的时候才调用
    要是 Go 能支持默认参数就好了,能省很多事
    XTTX
        16
    XTTX  
       2021-10-24 14:47:46 +08:00
    @Kisesy 我猜当初设计的时候,mongoDB 就是要做 data warehouse,所以必须读 ctx withTimeout 。mongoDB 的市场营销是所有 tech 里做得最好的, 让所有人都觉得其他人都在用 mongoDB 。 官网的文档那叫一个省事,复杂一点的查询方式根本不介绍。 当初我找查询的语法,真的找到吐血。
    balabalaguguji
        17
    balabalaguguji  
    OP
       2021-10-24 14:54:47 +08:00
    @XTTX #10 写个复杂点的 aggregate 感觉会代码量巨大,巨难理解
    balabalaguguji
        18
    balabalaguguji  
    OP
       2021-10-24 14:55:27 +08:00
    @pengtdyd #12 没写过,还有比这更蛋疼的
    balabalaguguji
        19
    balabalaguguji  
    OP
       2021-10-24 14:56:00 +08:00
    @wxlwsy #13 写过,真没这个难受
    Weny
        20
    Weny  
       2021-10-24 15:09:54 +08:00
    简单的 filter 可以自己封装一下,几百行代码的事情。
    ```GO
    import (
    "context"
    "fmt"
    "strings"
    )

    func initQuery() *Query {
    q := Query{
    operation: "",
    options: nil,
    conditions: make(M),
    field: "",
    }
    return &q
    }

    func NewQuery(fn ...func(query *Query)) *Query {
    q := Query{
    operation: "",
    options: &Options{
    skip: 0,
    projection: make(M),
    },
    conditions: make(M),
    field: "",
    }
    if len(fn) > 0 {
    for _, f := range fn {
    f(&q)
    }
    }
    return &q
    }

    func SetConn(c Connection) func(query *Query) {
    return func(query *Query) {
    query.connection = c
    }
    }

    func NewQueryWitConn(c Connection) *Query {
    return NewQuery(SetConn(c))
    }

    type Query struct {
    connection Connection
    operation string
    options *Options
    conditions M
    field string
    model interface{}
    }

    func (q *Query) GetConditions() M {
    return q.conditions
    }

    func (q *Query) GetSelect() M {
    return q.options.projection
    }

    func mappingStringToFieldSets(value Input, projection bool) Input {
    out := -1
    if projection {
    out = 0
    }
    obj := make(M)
    switch value.(type) {
    case string:
    strArray := strings.Fields(strings.TrimSpace(value.(string)))
    for _, v := range strArray {
    if v[0] == '-' {
    v = v[1:]
    obj[v] = out
    } else {
    obj[v] = 1
    }
    }
    case M:
    obj = value.(M)
    }
    return obj
    }

    func (q *Query) Sort(value interface{}) *Query {
    q.options.sort = mappingStringToFieldSets(value, false).(M)
    return q
    }

    func (q *Query) Select(value interface{}) *Query {
    q.options.projection = mappingStringToFieldSets(value, true).(M)
    return q
    }

    func (q *Query) Skip(value int32) *Query {
    q.options.skip = value
    return q
    }

    func (q *Query) Where(args ...interface{}) *Query {
    //q.field = field
    switch len(args) {
    // first args is string
    case 1:
    if field, ok := args[0].(string); ok {
    q.Set(field)
    }
    // Where("version",1) where version is equals q
    case 2:
    if field, ok := args[0].(string); ok {
    q.Set(field).Eq(args[1])
    }
    // Where("version",">=",1) gte 1
    case 3:
    if field, ok := args[0].(string); ok {
    q.Set(field)
    }
    if operators, ok := args[1].(string); ok {
    q.bindCondition(
    chain(
    getFlagWrapperFromString(operators),
    inputBuilder,
    )(args[2]),
    )
    }
    }
    return q
    }

    func (q *Query) Set(field string) *Query {
    q.field = field
    return q
    }

    func (q *Query) Eq(input interface{}) *Query {
    q.
    ensureField("Eq").
    bindCondition(
    chain(
    inputLogger,
    inputBuilder,
    )(input),
    ).
    resetField()
    return q
    }

    func (q *Query) Equals(input interface{}) *Query {
    q.
    ensureField("Equals").
    bindCondition(
    chain(inputBuilder)(input),
    ).
    resetField()
    return q
    }

    func (q *Query) AutoBindConditions(flag string, condition interface{}) *Query {
    if q.hasField() {
    q.bindCondition(
    chain(
    inputBuilder,
    )(condition),
    ).resetField()
    } else {
    q.bindCondition(
    chain(
    inputWrapper(flag),
    inputBuilder,
    )(condition),
    ).resetField()
    }
    return q
    }

    /*
    e.g. query.Or([]M{{"name": "weny"}, {"age": "20"}})
    */
    func (q *Query) Or(value interface{}) *Query {
    flag := "$or"
    return q.AutoBindConditions(flag, value)
    }

    /*
    e.g. query.Nor([]M{{"name": "weny"}, {"age": "20"}})
    */
    func (q *Query) Nor(value interface{}) *Query {
    flag := "$nor"
    return q.AutoBindConditions(flag, value)
    }

    func (q *Query) And(value interface{}) *Query {
    flag := "$and"
    return q.AutoBindConditions(flag, value)
    }

    func (q *Query) Not(value interface{}) *Query {
    flag := "$not"
    return q.AutoBindConditions(flag, value)
    }

    func (q *Query) Gt(value interface{}) *Query {
    flag := "$gt"
    return q.AutoBindConditions(flag, value)
    }

    func (q *Query) Gte(value interface{}) *Query {
    flag := "$gte"
    return q.AutoBindConditions(flag, value)
    }

    func (q *Query) Lt(value interface{}) *Query {
    flag := "$lt"
    return q.AutoBindConditions(flag, value)
    }

    func (q *Query) Lte(value interface{}) *Query {
    flag := "$lte"
    return q.AutoBindConditions(flag, value)
    }

    func (q *Query) Ne(value interface{}) *Query {
    flag := "$ne"
    return q.AutoBindConditions(flag, value)
    }

    func (q *Query) In(value interface{}) *Query {
    flag := "$in"
    return q.AutoBindConditions(flag, value)
    }

    func (q *Query) Nin(value interface{}) *Query {
    flag := "$nin"
    return q.AutoBindConditions(flag, value)
    }

    func (q *Query) All(value interface{}) *Query {
    flag := "$all"
    return q.AutoBindConditions(flag, value)
    }

    func (q *Query) Regex(value interface{}) *Query {
    flag := "$regex"
    return q.AutoBindConditions(flag, value)
    }

    func (q *Query) Size(value interface{}) *Query {
    flag := "$size"
    return q.AutoBindConditions(flag, value)
    }

    func (q *Query) MaxDistance(value interface{}) *Query {
    flag := "$maxDistance"
    return q.AutoBindConditions(flag, value)
    }

    func (q *Query) MinDistance(value interface{}) *Query {
    flag := "$minDistance"
    return q.AutoBindConditions(flag, value)
    }

    func (q *Query) Mod(value interface{}) *Query {
    flag := "$mod"
    return q.AutoBindConditions(flag, value)
    }

    //TODO: geometry

    func (q *Query) Exists(value bool) *Query {
    flag := "$exists"
    return q.AutoBindConditions(flag, value)
    }

    func (q *Query) ElemMatch(value interface{}) *Query {
    flag := "$elemMatch"
    return q.AutoBindConditions(flag, value)
    }

    func (q *Query) bindCondition(value Input) *Query {
    q.conditions[q.field] = value
    return q
    }

    func (q *Query) resetField() *Query {
    q.field = ""
    return q
    }

    func (q *Query) setField(field string) *Query {
    q.field = field
    return q
    }
    func (q *Query) hasField() bool {
    if q.field == "" {
    return false
    }
    return true
    }

    func (q *Query) ensureField(method string) *Query {
    if q.field == "" {
    panic(method + " must be used after Where() ")
    }
    return q
    }

    func inputLogger(input Input) Input {
    go func() {
    fmt.Print(input)
    }()
    return input
    }

    func inputWrapper(flag string) InputEndpoint {
    return func(input Input) Input {
    return M{flag: input}
    }
    }
    func getFlagWrapperFromString(arg string) InputEndpoint {
    switch arg {
    case "<":
    return inputWrapper("$lt")
    case ">":
    return inputWrapper("$gt")
    case "=":
    return inputWrapper("$eq")
    case ">=":
    return inputWrapper("$gte")
    case "<=":
    return inputWrapper("$lte")
    default:
    return inputWrapper("$eq")
    }
    }

    func inputBuilder(input Input) Input {
    var res interface{}
    switch input.(type) {
    case func(q *Query):
    query := NewQuery()
    input.(func(q *Query))(query)
    res = query.conditions
    break
    case func(q *Query) *Query:
    res = input.(func(q *Query) *Query)(initQuery()).conditions
    break
    case interface{}:
    res = input
    break
    }
    return res
    }
    ```
    linhongye
        21
    linhongye  
       2021-10-24 15:14:47 +08:00
    我们就是 Go+mongo, 舒服得很...
    搞点插件熟练一下...
    balabalaguguji
        22
    balabalaguguji  
    OP
       2021-10-24 15:24:56 +08:00
    @linhongye #21 怎么会舒服,具体点怎么搞
    balabalaguguji
        23
    balabalaguguji  
    OP
       2021-10-24 15:30:20 +08:00
    @Weny #20 感觉还是很复杂麻烦?
    用你这个封装好的代码写一个查询用户 ID=1 的怎么查?
    复杂点的 aggregate 怎么写,如下:
    pipline = [
    {'$match': {'taskId': {'$in': taskIds}}},
    {'$lookup': {"from": "groupLogs", "localField": "groupId", "foreignField": "_id", "as": "groupData"}},
    {'$match': {'groupData': {'$size': 1}}},
    {'$group': {'_id': '$taskId', 'count': {'$sum': '$totalCount'}}},
    ]
    40EaE5uJO3Xt1VVa
        24
    40EaE5uJO3Xt1VVa  
       2021-10-24 15:32:08 +08:00
    @linhongye 怎么搞的请教一下
    jim9606
        25
    jim9606  
       2021-10-24 16:18:28 +08:00
    你说的问题其实是强类型语言的问题,如果你对程序鲁棒性要求要一些,要对所有输入做格式和类型校验,那不可能优雅的。
    go 里面任意类型的 map 是 map[string]interface{},不过访问这玩意的代码写起来也很烦就是了。
    clf
        26
    clf  
       2021-10-24 16:26:09 +08:00 via Android
    java 的 mongo template 其实也是构建一个 Document 丢进去查询,mongo 官方基本就提供了连接和调用查询的基本方式,更多的还是由 orm 框架实现。
    learningman
        27
    learningman  
       2021-10-24 16:26:14 +08:00
    go 处理 json 就是不爽啊,想爽用 js
    holulu
        28
    holulu  
       2021-10-24 16:37:51 +08:00
    强类型都有这种问题,用过的语言多了,不同范畴的代码都写过了,就会看淡了。语言就是个工具,没必要看得那么重,大不了就换。
    xnotepad
        29
    xnotepad  
       2021-10-24 16:52:05 +08:00
    这应该是所有静态语言都有的问题。
    Weny
        30
    Weny  
       2021-10-24 17:16:00 +08:00
    @balabalaguguji 其实本质上就是一个 Map 的 builder

    第一个问题 :queryBuilder.Where("user_id",id)

    aggregate 我觉得再封装一个 pipeline builder ,再扩展一下已有的 queryBuilder 比如
    pipelineBuilder(
    queryBuilder.Match("taskId",func(q){return q.in(taskIds)}),
    queryBuilder.Lookup(func(q){return q.Form("")...})
    ...
    )
    gengchun
        31
    gengchun  
       2021-10-24 17:25:48 +08:00
    要静态语言,要性能就是这个结果。

    可以找一些 orm ,我记得有的不需要传 context ;当然 orm 又会有性能问题,你对 orm 的一些选择也未必会满意。
    xuanbg
        32
    xuanbg  
       2021-10-24 17:48:12 +08:00
    自己再封装一下啊
    TypeError
        33
    TypeError  
       2021-10-24 18:17:44 +08:00
    qmgo
    xsen
        34
    xsen  
       2021-10-24 19:20:54 +08:00
    这跟语言有什么关系,这只是 mongodb 的 sdk 没有封装好而已
    都有参考的理想的用法,那自己再封装一层不就是可以

    对于 flutter 嵌套这样的,对原有做 pc 客户端的(如 c++)的就很是友好
    只是一个思维习惯的问题
    zxCoder
        35
    zxCoder  
       2021-10-24 19:33:47 +08:00
    感觉 mongodb 还是适合 js/ts
    sunmoon1983
        36
    sunmoon1983  
       2021-10-24 19:36:27 +08:00
    七牛的 qmgo
    fireleaves
        37
    fireleaves  
       2021-10-24 19:50:27 +08:00
    @balabalaguguji 试试这样一个 pipline 转 bson 接口呢?

    ```golang
    // MongoPipeline gets aggregation pipeline from a string
    func MongoPipeline(str string) mongo.Pipeline {
    var pipeline = []bson.D{}
    str = strings.TrimSpace(str)
    if strings.Index(str, "[") != 0 {
    var doc bson.D
    bson.UnmarshalExtJSON([]byte(str), false, &doc)
    pipeline = append(pipeline, doc)
    } else {
    bson.UnmarshalExtJSON([]byte(str), false, &pipeline)
    }
    return pipeline
    }
    ```

    这样调用

    ```golang
    pipline = `pipline = [
    {'$match': {'taskId': {'$in': taskIds}}},
    {'$lookup': {"from": "groupLogs", "localField": "groupId", "foreignField": "_id", "as": "groupData"}},
    {'$match': {'groupData': {'$size': 1}}},
    {'$group': {'_id': '$taskId', 'count': {'$sum': '$totalCount'}}},
    ]`
    opts := options.Aggregate()
    if res, err = collection.Aggregate(ctx, MongoPipeline(pipeline), opts); err != nil {
    t.Fatal(err)
    }
    ```

    参考自: https://github.com/simagix/mongo-go-examples
    Fitz
        38
    Fitz  
       2021-10-24 20:54:56 +08:00
    静态语言不都这样吗 无非是怎么封装,context 是为了并发控制, 这个没什么好喷的, 你只是没遇到需要它的场景
    balabalaguguji
        39
    balabalaguguji  
    OP
       2021-10-24 20:58:44 +08:00
    @jim9606 #25 是这么回事,强类型的都得这么麻烦。
    balabalaguguji
        40
    balabalaguguji  
    OP
       2021-10-24 21:01:18 +08:00
    @clf #26 看了下 Java 的,构建一个 Document 感觉比 Go 这种方式好很多,好接受多了
    balabalaguguji
        41
    balabalaguguji  
    OP
       2021-10-24 21:02:00 +08:00
    @learningman #27 所以人生短暂,准备用 nodejs 吧,写起来舒服。
    balabalaguguji
        42
    balabalaguguji  
    OP
       2021-10-24 21:04:25 +08:00
    @holulu #28 嗯,强类型确实没啥办法,不过 Java 的比 Go 的好很多,应该是有泛型的原因?(没怎么用过 JAVA)。
    balabalaguguji
        43
    balabalaguguji  
    OP
       2021-10-24 21:07:56 +08:00
    @xnotepad #29 嗯,静态语言是得这样,不过 Java 的也舒服很多。
    @gengchun #31 性能就必须是静态语言吗?这个不是很了解
    balabalaguguji
        44
    balabalaguguji  
    OP
       2021-10-24 21:11:19 +08:00
    @sunmoon1983 #36 看了下,也是一堆包裹
    YUyu101
        45
    YUyu101  
       2021-10-24 21:20:04 +08:00   ❤️ 1
    确实不舒服,工作写了半年 go ,从没在工作用过的 nodejs 用起来无比丝滑,真是由俭入奢易,要是有一门语言把两者的优点合起来就好了。
    ob
        46
    ob  
       2021-10-24 21:22:40 +08:00
    @YUyu101 数据库改个字段名或增删个字段,涉及到的 json ,结构体处理,都要改好几遍。
    zjsxwc
        47
    zjsxwc  
       2021-10-24 22:24:15 +08:00   ❤️ 2
    rust 有宏有泛型,比 go 好多了
    https://github.com/mongodb/mongo-rust-driver#example-usage
    XTTX
        48
    XTTX  
       2021-10-24 23:39:31 +08:00
    JS 写成 typescript ,要写各式各样的 type\interface\generics, 可不必 Go 写 struct 方便多少。
    XTTX
        49
    XTTX  
       2021-10-24 23:45:50 +08:00
    不清楚为什么贴主一定要用 mongoDB 。Go 写 psql query 挺方便的,写多了都是 boilerplate, 改改就差不多了
    gengchun
        50
    gengchun  
       2021-10-25 06:55:27 +08:00
    @balabalaguguji 认真说起来的话。据说,动态里好像 erlang, groovy 这些强类型性能还可以,不过我都没有用过。我用过的里面,性能可以的确实都是静态的。主要是第一句看 python/js ,这个加上 golang 默认讨论的就是动态 vs. 静态,没仔细想就发了。

    你的问题本身确实是 golang 的类型系统问题,但是 json 的这个的处理麻烦。并不是简单的需要静态检查,或者类型强弱导致的。如果只是强类型的话,但是有语法糖的话,其实也不会导致这样写。

    不过语言设计太高深,还是打住,不然没完没了了。

    总结一下,Golang 出于某个我不知道的原因,就是要让语言的用户,自己在业务代码层面上实现 hash 字典的处理。这么多年了,我估计默认的语法或者内置库,是不会有更方便的实现了。这个地方用 golang 不是自己封装,就是指着 orm ,也没有别的办法了。
    wellsc
        51
    wellsc  
       2021-10-25 09:17:13 +08:00
    @avastms #1 你在说什么。。
    vipppppp
        52
    vipppppp  
       2021-10-25 09:23:21 +08:00
    MongoDB 官方的 Go 接口 真的,一言难尽。。。
    p1gd0g
        53
    p1gd0g  
       2021-10-25 09:24:37 +08:00
    传 context 很正常吧。现在不传,以后有一天要用到再换函数?
    airplayxcom
        54
    airplayxcom  
       2021-10-25 09:35:05 +08:00 via iPhone
    动态语言害人不浅
    lizuoqiang
        55
    lizuoqiang  
       2021-10-25 09:35:15 +08:00
    go 的 orm 真的一言难尽
    pkoukk
        56
    pkoukk  
       2021-10-25 09:55:02 +08:00
    动态语言害人不浅+1
    别用 mongo 了,性能太差了,pg 吧
    x940727
        57
    x940727  
       2021-10-25 10:04:42 +08:00
    @holulu 不敢苟同,语言确实是工具,但是决定一个语言生命力的,语法不是关键性因素,语言附带的生态才是关键性因素。如果你换一个语言,就等于生态全部都需要重新去了解熟悉,这个沉没成本远比你想的要高的多,基本上没有老手会选择放弃自己钻研五六年的语言去换一个全新的语言,最多换一个相关性比较高的语言。我认为张嘴语言就是工具可以随便换的,不是超级大神,就是菜鸟……
    qW7bo2FbzbC0
        58
    qW7bo2FbzbC0  
       2021-10-25 10:14:00 +08:00
    @Kisesy #15 mongodb 静态类型驱动本身就很捉急,更别提那种复杂 aggregate 操作转述时多么费力了。应该也可以输入 json str 自动转成 query document 的
    rayw0ng
        59
    rayw0ng  
       2021-10-25 10:20:04 +08:00
    @pengtdyd Rust 官方例子,读起来很容易啊
    use mongodb::{
    bson::doc,
    sync::Client,
    };
    use serde::{Deserialize, Serialize};

    #[derive(Debug, Serialize, Deserialize)]
    struct Book {
    title: String,
    author: String,
    }
    let client = Client::with_uri_str("mongodb://localhost:27017")?;
    let database = client.database("mydb");
    let collection = database.collection::<Book>("books");

    let docs = vec![
    Book {
    title: "1984".to_string(),
    author: "George Orwell".to_string(),
    },
    Book {
    title: "Animal Farm".to_string(),
    author: "George Orwell".to_string(),
    },
    Book {
    title: "The Great Gatsby".to_string(),
    author: "F. Scott Fitzgerald".to_string(),
    },
    ];

    // Insert some books into the "mydb.books" collection.
    collection.insert_many(docs, None)?;

    let cursor = collection.find(doc! { "author": "George Orwell" }, None)?;
    for result in cursor {
    println!("title: {}", result?.title);
    }
    lancelock
        60
    lancelock  
       2021-10-25 10:22:42 +08:00   ❤️ 6
    go 写业务本来就是残废。之前帮亲戚写了一个东西后就被我放弃了。但架不住跟风吹的多啊,中国人的权威崇拜深入骨髓,看着两个创世人的履历,和大公司的背景就不能自已了。凡是觉得不好用的,那一定是自己的问题,凡人不能领会大神的思维。动辄大道至简,你不懂 go 的哲学,可惜你的哲学也不是那么坚挺啊,这不就加上了泛型

    关键 go 吹你吹就吹吧,还爱给你安利:来用 go 吧,你会打开新世界的大门。一副没有见识的样子,真实无语
    wowbaby
        61
    wowbaby  
       2021-10-25 10:24:46 +08:00   ❤️ 1
    我一般看到 .H .D .M 这的命名就没有了兴趣
    keepeye
        62
    keepeye  
       2021-10-25 10:32:22 +08:00
    这是什么操作,mongodb 写的库垃圾,让 go 背锅?让一个静态语言跟脚本语言去比较所谓的 “优雅”也是醉了
    xz410236056
        63
    xz410236056  
       2021-10-25 10:49:33 +08:00
    “嗯,静态语言应该都是要先定义模型,太不灵活了。”

    针对这句话问一下,动态语言不定义模型,后面接手的人知道你这个字典里有什么值吗?能直接点属性用吗?还是要写一堆字符串去取,万一再拼错了
    shockerli
        64
    shockerli  
       2021-10-25 11:55:38 +08:00
    我用过,MongoDB 官方的 Go SDK 确实拉胯,但这不能算到 Go 语言身上
    ila
        65
    ila  
       2021-10-25 12:10:10 +08:00 via Android
    @lancelock 用 go 的,开发快,编译快,单文件运行。
    需要这些的自然接受的了 go
    ila
        66
    ila  
       2021-10-25 12:11:03 +08:00 via Android
    @xz410236056 由得他去吹。
    python 一样要定义模型再 crud.
    balabalaguguji
        67
    balabalaguguji  
    OP
       2021-10-25 12:42:05 +08:00
    @shockerli #64 那能找到一个更优雅的库?
    chtcrack
        68
    chtcrack  
       2021-10-25 12:45:36 +08:00
    真的要性能不如用 c 或者 c++,感觉用 go 怪怪的.https://docs.mongodb.com/drivers/cxx/
    c88155745
        69
    c88155745  
       2021-10-25 13:47:13 +08:00
    MongoDB 有 go struct 约束 还是可以用的 ,反而弱类型语言容易类型出错 不同人整出各种类型
    cyrivlclth
        70
    cyrivlclth  
       2021-10-25 13:47:25 +08:00
    可以用 entgo 写一套模板,生成操作 MongoDB 库的代码。。。我之前刚准备弄这个,但是忙忘了
    thevita
        71
    thevita  
       2021-10-25 13:48:31 +08:00
    1. 如果觉得用 js python 直接读更舒服完全可以用 js.
    2. 没受过动态类型荼毒的,必然难以理解强类型系统的作用
    3. 强类型与否, 看自己的 use case 吧这个不好说,有的场景就是用 js 写着就很爽,矫枉过正还是不好,被毒打多了就逐渐知道怎么平衡了
    4. mongo 本来就是个文档数据库,半结构化,天然跟 js ,python 之类的会亲和一点
    5. 应该能通过合理的约定+时代的封装 /抽象 来缓解,就是谨慎点用 mongo ,模式管理更规范一点(当然,并不是说你不规范的意思,只是说到这里了)
    6. 写 go 踩到自己的地方多了去了,老想给自己一拳。
    thevita
        72
    thevita  
       2021-10-25 13:53:43 +08:00
    @thevita 对于我来说,这些硬编码的 "name", "age" 都是不能忍的,不过这就有点矫枉过正了
    XTTX
        73
    XTTX  
       2021-10-25 14:36:48 +08:00
    从 mongoDB 和 Go 的不匹配到讨伐 Golang ,这出戏我见过了。每个语言都有自己存在的理由。用不用得习惯,适不适合自己的使用场景,得看自己。js 为了类型检查,搞出了 typescript. 都用习惯了就好了,没有完美的语言和工具
    thtznet
        74
    thtznet  
       2021-10-25 14:40:04 +08:00
    C# 欢迎你,不极端,兼容各类语言的优点,集大成者。
    lancelock
        75
    lancelock  
       2021-10-25 14:42:21 +08:00
    @ila go 开发快?讲笑话吗?唯一的优势就是编译了。不过其他技术栈上个 cicd 也就操心一次,后续也都一样了
    skiy
        76
    skiy  
       2021-10-25 15:09:28 +08:00
    可以等等试试泛型出来之后?
    holulu
        77
    holulu  
       2021-10-25 15:20:42 +08:00
    @x940727 祝您早日摆脱生态束缚,实现语言自由,成为想换就换的超级大神。:)
    zhuangzhuang1988
        78
    zhuangzhuang1988  
       2021-10-25 15:22:55 +08:00
    @thtznet 能听就好了
    x940727
        79
    x940727  
       2021-10-25 15:41:31 +08:00
    @holulu 如果写小工具的话,那无所谓生态问题,毕竟现在大部分语言的标准库都不差,我平时也会用 Python 和 Go 写一些数据处理、系统小脚本之类的,但是如果是写复杂点的项目,生态的重要性就凸显出来了。
    zjyl1994
        80
    zjyl1994  
       2021-10-25 15:42:56 +08:00
    我也觉得交互很傻,所以我放弃了 mongo 。个人觉得 mongo 没香到哪里去
    qq1340691923
        81
    qq1340691923  
       2021-10-25 15:44:09 +08:00
    我刚开始也是用的官方库,怎么写怎么繁琐,后来换成了 https://github.com/go-mgo/mgo ,瞬间开发效率都高了好多
    gowk
        82
    gowk  
       2021-10-25 15:59:14 +08:00
    @lancelock 目前业务密集型应用就别用 Go 了,给自己找麻烦,好好用 Java 挺香。但是我挺期待 Go 泛型的大规模应用,再加上完备的库和生态,让写业务也成为一种乐趣。
    qq1340691923
        83
    qq1340691923  
       2021-10-25 16:07:54 +08:00
    golang 官方的 es 库用起来也没有第三方的库清爽
    securityCoding
        84
    securityCoding  
       2021-10-25 16:10:27 +08:00
    @gowk 没泛型写业务是真的操蛋
    ila
        85
    ila  
       2021-10-25 16:21:24 +08:00 via Android
    @lancelock 你是说哪个点不快?可以举个例子了。
    solos
        86
    solos  
       2021-10-25 17:48:18 +08:00
    你该放弃的是 mongodb
    holulu
        87
    holulu  
       2021-10-25 18:14:21 +08:00
    @x940727 现在微服务的优势就可以让你结合不同工具的优势来打造复杂的项目,减少要在同一个项目中使用统一的技术框架的麻烦。
    x940727
        88
    x940727  
       2021-10-25 18:21:34 +08:00
    @holulu 微服务的副作用是非常大的,上分布式系统的前提就是能不上分布式就别上,如果我就是业务复杂但是用户量不大呢?软件工作没有银弹,微服务也不是架构银弹。
    xiaotianhu
        89
    xiaotianhu  
       2021-10-25 19:06:22 +08:00
    @x940727 不敢苟同,语言确实是工具,但是决定一个语言生命力的,语法不是关键性因素,语言附带的生态才是关键性因素。如果你换一个语言,就等于生态全部都需要重新去了解熟悉,这个沉没成本远比你想的要高的多,基本上没有老手会选择放弃自己钻研五六年的语言去换一个全新的语言,最多换一个相关性比较高的语言。我认为张嘴语言就是工具可以随便换的,不是超级大神,就是菜鸟……

    大神都是不用干活搞科研那种,研究点编译原理 runtime 什么的,才不要解析神马 JSON ,太 low
    x940727
        90
    x940727  
       2021-10-25 19:13:18 +08:00
    @xiaotianhu 那也不一定啊,就拿软件领域来说,搞科研的很多东西落不了地,你看 MIT 的莫里斯教授对于 Hadoop 就赞扬有加,因为这玩意是落了地的,验证了可行性并且大规模使用的。大部分理工科人还是不喜欢嘴炮侠的吧。
    Z1on
        91
    Z1on  
       2021-10-25 20:25:19 +08:00
    @XTTX mongo.Connect()在程序启动的时候,放在 init 里面调用一次就好了,把 client 声明成全局变量。client 是并发安全的,会自己维护一个链接池。
    https://github.com/mongodb/mongo-go-driver/blob/v1.7.3/mongo/client.go#L51
    gengchun
        92
    gengchun  
       2021-10-25 23:38:47 +08:00
    @lancelock 这个是工业化,和权威没有关系。Golang 的目标和之前 Java 一样,就是希望高中毕业培训两个月,给个最低工资就能上手写代码。

    真能单枪匹马完成项目,想用 lisp 都没有人管。
    holulu
        93
    holulu  
       2021-10-26 07:07:54 +08:00
    @x940727 不是说微服务就能解决所有问题,只是提供一个思路或方向。任何技术都有副作用,大不大看能否承受,重要的是权衡利弊。软件工程就是风险管理,永远不可能为要实现的业务找到最合适的技术方案,只有在不同方案之间妥协,寻找一个适应当时业务发展的平衡点。了解的技术越多,离找到合适的平衡点就越近。
    tiedan
        94
    tiedan  
       2021-10-26 09:13:42 +08:00
    可能我用习惯了,我觉得 go 下操作 mongo 挺方便的😂
    wildlife
        95
    wildlife  
       2021-10-27 09:47:20 +08:00
    balabalaguguji
        96
    balabalaguguji  
    OP
       2021-10-27 10:39:37 +08:00
    @wildlife #95 瞬间感觉 mongodb 如此复杂
    8355
        97
    8355  
       2021-10-27 14:39:31 +08:00
    用 php 啊 一把嗦 不要考虑这些 拥抱世界上最好的语言 狗头
    darknoll
        98
    darknoll  
       2021-10-28 17:06:29 +08:00
    不能拿静态语言和动态语言比,你要觉得 go 麻烦,那 C++看都不能看了啊
    maotao456
        99
    maotao456  
       2021-11-12 16:41:40 +08:00
    @8355 这可不太好,在 php 里面用 mongodb 很容易被数据类型搞死。123, "123"
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2151 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 00:43 · PVG 08:43 · LAX 16:43 · JFK 19:43
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.