之前习惯了 python/js 这种语法,感觉很自然很方便。
今天看了下 MongoDB 官方的 Go 接口,哎呀,那交互方式,真的是痛苦。
例如查询用户为 1 的用户:{userid: 1},在 Go 里面你还得包裹为 bson.D{{"userid", 1}}
返回的结果是一个索引,要 Decode 下,Docode 还需要传递一个结构体过去。
还得传递一个 context (还没看为啥要这么做,其他语言不用)
感觉一点也不方便,代码很多不美观不优雅,习惯了 js/python 这种比较简单直观的语法,难以接受呀。
感觉 Google 最近出的东西,语法都那么特立独行的,还有一个是死亡嵌套 Flutter ,嵌套到怀疑人生。
官方教程: 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)

主要是因为 Go 里你可以自己安排内存,而这个库不想替你管理内存。

其实别的语言也一样,当一个组件不想建堆内存的时候,代码都一样拧巴。

哦我收回我的话,这不是主要原因。

主要原因就是 Go 描述动态对象太拧巴了

封装一下,应该还行

context 这个可以洗一下,它可以用于在请求撤销的时候撤回发出去的其他请求,避免了资源的浪费,在 go 的主要应用场景--中间件方面意义重大
JS 里也有类似的东西,叫 AbortSignal
(虽然 context 功能上更多一点,这里只考虑它的这个功能(

静态语音就是这样的,对接第三方的时候都是先定义模型,再用这个模型的实例去做交互。
通常用泛型会简化一点这种操作,但是 go 。。。

#3 好像不好封装,Go 里面没有支持任意类型的 Map

#4 这个特性感觉我都用不到,但是每次都得写,感觉就有点强制多余了

#5 嗯,静态语言应该都是要先定义模型,太不灵活了。

官方 doc 就劝退了, 那你还是千万别用 go+mongoDB 了

//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 个月前写的,我现在完全看不懂当初写了什么寂寞

//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
}

这种东西写的难受, 看得更难受。

说这话的人显然没有写过 rust

你应该没写过原生 JDBC 代码,否则也不会这么说了。

那是相当难受了,特别层级很多的话每层都要检查 err

mongo 官方库就是不好用,有点底层,当然我没用过不好评价
他这设计有些不对,各种操作必须要传 ctx 这也太那啥了,本来 Go 的操作就繁琐
完全可以写成 collection.WithCtx(context.TODO()).UpdateOne(filter, update),想用 ctx 的时候才调用
要是 Go 能支持默认参数就好了,能省很多事

我猜当初设计的时候,mongoDB 就是要做 data warehouse,所以必须读 ctx withTimeout 。mongoDB 的市场营销是所有 tech 里做得最好的, 让所有人都觉得其他人都在用 mongoDB 。 官网的文档那叫一个省事,复杂一点的查询方式根本不介绍。 当初我找查询的语法,真的找到吐血。

#10 写个复杂点的 aggregate 感觉会代码量巨大,巨难理解

#12 没写过,还有比这更蛋疼的

#13 写过,真没这个难受

简单的 filter 可以自己封装一下,几百行代码的事情。

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
}

我们就是 Go+mongo, 舒服得很...
搞点插件熟练一下...

#21 怎么会舒服,具体点怎么搞

#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'}}},
]

怎么搞的请教一下

你说的问题其实是强类型语言的问题,如果你对程序鲁棒性要求要一些,要对所有输入做格式和类型校验,那不可能优雅的。
go 里面任意类型的 map 是 map[string]interface{},不过访问这玩意的代码写起来也很烦就是了。

java 的 mongo template 其实也是构建一个 Document 丢进去查询,mongo 官方基本就提供了连接和调用查询的基本方式,更多的还是由 orm 框架实现。

go 处理 json 就是不爽啊,想爽用 js

强类型都有这种问题,用过的语言多了,不同范畴的代码都写过了,就会看淡了。语言就是个工具,没必要看得那么重,大不了就换。

这应该是所有静态语言都有的问题。

其实本质上就是一个 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("")...})
...
)

要静态语言,要性能就是这个结果。

可以找一些 orm ,我记得有的不需要传 context ;当然 orm 又会有性能问题,你对 orm 的一些选择也未必会满意。

自己再封装一下啊

qmgo

这跟语言有什么关系,这只是 mongodb 的 sdk 没有封装好而已
都有参考的理想的用法,那自己再封装一层不就是可以

对于 flutter 嵌套这样的,对原有做 pc 客户端的(如 c++)的就很是友好
只是一个思维习惯的问题

感觉 mongodb 还是适合 js/ts

七牛的 qmgo

试试这样一个 pipline 转 bson 接口呢?

// 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
}

这样调用

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)
}

参考自: github.com/simagix/mongo-go-examples

静态语言不都这样吗 无非是怎么封装,context 是为了并发控制, 这个没什么好喷的, 你只是没遇到需要它的场景

#25 是这么回事,强类型的都得这么麻烦。

#26 看了下 Java 的,构建一个 Document 感觉比 Go 这种方式好很多,好接受多了

#27 所以人生短暂,准备用 nodejs 吧,写起来舒服。

#28 嗯,强类型确实没啥办法,不过 Java 的比 Go 的好很多,应该是有泛型的原因?(没怎么用过 JAVA)。

#29 嗯,静态语言是得这样,不过 Java 的也舒服很多。
#31 性能就必须是静态语言吗?这个不是很了解

#36 看了下,也是一堆包裹

确实不舒服,工作写了半年 go ,从没在工作用过的 nodejs 用起来无比丝滑,真是由俭入奢易,要是有一门语言把两者的优点合起来就好了。

数据库改个字段名或增删个字段,涉及到的 json ,结构体处理,都要改好几遍。

rust 有宏有泛型,比 go 好多了
github.com/mongodb/mongo-rust-driver#example-usage

JS 写成 typescript ,要写各式各样的 type\interface\generics, 可不必 Go 写 struct 方便多少。

不清楚为什么贴主一定要用 mongoDB 。Go 写 psql query 挺方便的,写多了都是 boilerplate, 改改就差不多了

认真说起来的话。据说,动态里好像 erlang, groovy 这些强类型性能还可以,不过我都没有用过。我用过的里面,性能可以的确实都是静态的。主要是第一句看 python/js ,这个加上 golang 默认讨论的就是动态 vs. 静态,没仔细想就发了。

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

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

总结一下,Golang 出于某个我不知道的原因,就是要让语言的用户,自己在业务代码层面上实现 hash 字典的处理。这么多年了,我估计默认的语法或者内置库,是不会有更方便的实现了。这个地方用 golang 不是自己封装,就是指着 orm ,也没有别的办法了。

#1 你在说什么。。

MongoDB 官方的 Go 接口 真的,一言难尽。。。

传 context 很正常吧。现在不传,以后有一天要用到再换函数?

动态语言害人不浅

go 的 orm 真的一言难尽

动态语言害人不浅+1
别用 mongo 了,性能太差了,pg 吧

不敢苟同,语言确实是工具,但是决定一个语言生命力的,语法不是关键性因素,语言附带的生态才是关键性因素。如果你换一个语言,就等于生态全部都需要重新去了解熟悉,这个沉没成本远比你想的要高的多,基本上没有老手会选择放弃自己钻研五六年的语言去换一个全新的语言,最多换一个相关性比较高的语言。我认为张嘴语言就是工具可以随便换的,不是超级大神,就是菜鸟……

#15 mongodb 静态类型驱动本身就很捉急,更别提那种复杂 aggregate 操作转述时多么费力了。应该也可以输入 json str 自动转成 query document 的

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::("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);
}

go 写业务本来就是残废。之前帮亲戚写了一个东西后就被我放弃了。但架不住跟风吹的多啊,中国人的权威崇拜深入骨髓,看着两个创世人的履历,和大公司的背景就不能自已了。凡是觉得不好用的,那一定是自己的问题,凡人不能领会大神的思维。动辄大道至简,你不懂 go 的哲学,可惜你的哲学也不是那么坚挺啊,这不就加上了泛型

关键 go 吹你吹就吹吧,还爱给你安利:来用 go 吧,你会打开新世界的大门。一副没有见识的样子,真实无语

我一般看到 .H .D .M 这的命名就没有了兴趣

这是什么操作,mongodb 写的库垃圾,让 go 背锅?让一个静态语言跟脚本语言去比较所谓的 “优雅”也是醉了

“嗯,静态语言应该都是要先定义模型,太不灵活了。”
针对这句话问一下,动态语言不定义模型,后面接手的人知道你这个字典里有什么值吗?能直接点属性用吗?还是要写一堆字符串去取,万一再拼错了

我用过,MongoDB 官方的 Go SDK 确实拉胯,但这不能算到 Go 语言身上

用 go 的,开发快,编译快,单文件运行。
需要这些的自然接受的了 go

由得他去吹。
python 一样要定义模型再 crud.

#64 那能找到一个更优雅的库?

真的要性能不如用 c 或者 c++,感觉用 go 怪怪的. docs.mongodb.com/drivers/cxx/

MongoDB 有 go struct 约束 还是可以用的 ,反而弱类型语言容易类型出错 不同人整出各种类型

可以用 entgo 写一套模板,生成操作 MongoDB 库的代码。。。我之前刚准备弄这个,但是忙忘了

  1. 如果觉得用 js python 直接读更舒服完全可以用 js.
  2. 没受过动态类型荼毒的,必然难以理解强类型系统的作用
  3. 强类型与否, 看自己的 use case 吧这个不好说,有的场景就是用 js 写着就很爽,矫枉过正还是不好,被毒打多了就逐渐知道怎么平衡了
  4. mongo 本来就是个文档数据库,半结构化,天然跟 js ,python 之类的会亲和一点
  5. 应该能通过合理的约定+时代的封装 /抽象 来缓解,就是谨慎点用 mongo ,模式管理更规范一点(当然,并不是说你不规范的意思,只是说到这里了)
  6. 写 go 踩到自己的地方多了去了,老想给自己一拳。

    对于我来说,这些硬编码的 "name", "age" 都是不能忍的,不过这就有点矫枉过正了

从 mongoDB 和 Go 的不匹配到讨伐 Golang ,这出戏我见过了。每个语言都有自己存在的理由。用不用得习惯,适不适合自己的使用场景,得看自己。js 为了类型检查,搞出了 typescript. 都用习惯了就好了,没有完美的语言和工具

C# 欢迎你,不极端,兼容各类语言的优点,集大成者。

go 开发快?讲笑话吗?唯一的优势就是编译了。不过其他技术栈上个 cicd 也就操心一次,后续也都一样了

可以等等试试泛型出来之后?

祝您早日摆脱生态束缚,实现语言自由,成为想换就换的超级大神。:)

能听就好了

如果写小工具的话,那无所谓生态问题,毕竟现在大部分语言的标准库都不差,我平时也会用 Python 和 Go 写一些数据处理、系统小脚本之类的,但是如果是写复杂点的项目,生态的重要性就凸显出来了。

我也觉得交互很傻,所以我放弃了 mongo 。个人觉得 mongo 没香到哪里去

我刚开始也是用的官方库,怎么写怎么繁琐,后来换成了 github.com/go-mgo/mgo ,瞬间开发效率都高了好多

目前业务密集型应用就别用 Go 了,给自己找麻烦,好好用 Java 挺香。但是我挺期待 Go 泛型的大规模应用,再加上完备的库和生态,让写业务也成为一种乐趣。

golang 官方的 es 库用起来也没有第三方的库清爽

没泛型写业务是真的操蛋

你是说哪个点不快?可以举个例子了。

你该放弃的是 mongodb

现在微服务的优势就可以让你结合不同工具的优势来打造复杂的项目,减少要在同一个项目中使用统一的技术框架的麻烦。

微服务的副作用是非常大的,上分布式系统的前提就是能不上分布式就别上,如果我就是业务复杂但是用户量不大呢?软件工作没有银弹,微服务也不是架构银弹。

不敢苟同,语言确实是工具,但是决定一个语言生命力的,语法不是关键性因素,语言附带的生态才是关键性因素。如果你换一个语言,就等于生态全部都需要重新去了解熟悉,这个沉没成本远比你想的要高的多,基本上没有老手会选择放弃自己钻研五六年的语言去换一个全新的语言,最多换一个相关性比较高的语言。我认为张嘴语言就是工具可以随便换的,不是超级大神,就是菜鸟……

大神都是不用干活搞科研那种,研究点编译原理 runtime 什么的,才不要解析神马 JSON ,太 low

那也不一定啊,就拿软件领域来说,搞科研的很多东西落不了地,你看 MIT 的莫里斯教授对于 Hadoop 就赞扬有加,因为这玩意是落了地的,验证了可行性并且大规模使用的。大部分理工科人还是不喜欢嘴炮侠的吧。

mongo.Connect()在程序启动的时候,放在 init 里面调用一次就好了,把 client 声明成全局变量。client 是并发安全的,会自己维护一个链接池。
github.com/mongodb/mongo-go-driver/blob/v1.7.3/mongo/client.go#L51

这个是工业化,和权威没有关系。Golang 的目标和之前 Java 一样,就是希望高中毕业培训两个月,给个最低工资就能上手写代码。

真能单枪匹马完成项目,想用 lisp 都没有人管。

不是说微服务就能解决所有问题,只是提供一个思路或方向。任何技术都有副作用,大不大看能否承受,重要的是权衡利弊。软件工程就是风险管理,永远不可能为要实现的业务找到最合适的技术方案,只有在不同方案之间妥协,寻找一个适应当时业务发展的平衡点。了解的技术越多,离找到合适的平衡点就越近。

可能我用习惯了,我觉得 go 下操作 mongo 挺方便的😂

再贴一段专门构造聚合查询的代码 🤣 lanseyujie.com/post/chain-construction-of-mongodb-aggregation-pipeline.html

#95 瞬间感觉 mongodb 如此复杂

用 php 啊 一把嗦 不要考虑这些 拥抱世界上最好的语言 狗头