在O2O与社交场景中,搜索附近的人、附近的商家是很常见的场景。那么我们如何实现呢?

接触的方法有:

  • 坐标+球体距离计算公式
  • 基于Redis的geo
  • 基于MongoDB的geohash

前面的demo已经有接触Redis,这里我们就用mongoDB来实现一下。

我们就直接使用官方的实现好了:

go.mongodb.org/mongo-driver/mongo

连接

opt := options.Client().ApplyURI("mongodb://root:211111@localhost:27017")

// Connect to MongoDB
client, err := mongo.Connect(context.TODO(), opt)
if err != nil {
log.Fatal(err)
}

// Check the connection
err = client.Ping(context.TODO(), nil)
if err != nil {
log.Fatal(err)
}

fmt.Println("Connected to MongoDB!")

我们可以设置更多:

opt.SetLocalThreshold(3 * time.Second)     //只使用与mongo操作耗时小于3秒的
opt.SetMaxConnIdleTime(5 * time.Second) //指定连接可以保持空闲的最大毫秒数
opt.SetMaxPoolSize(200) //使用最大的连接数
opt.SetReadConcern(readconcern.Majority()) //指定查询应返回实例的最新数据确认为,已写入副本集中的大多数成员

model

// 坐标
type Location struct {
Type string `json:"type" bson:"type"`
Coordinates []float64 `json:"coordinates" bson:"coordinates"`
}

// 每个点
type Point struct {
Name string `json:"name"`
Age int `json:"age"`
City string `json:"city"`
Location Location `json:"location"`
}

数据写入

我们可以先插入数据

func(mgo *mgo) Start() {
collection := mgo.client.Database(DBName).Collection(CollectionName)

// 设置索引 2dsphere, 很重要
collection.Indexes().CreateOne(context.TODO(), mongo.IndexModel{
Keys: bson.M{Key: "2dsphere"},
})

a := Point{"王二", 18, "杭州", Location{"Point", []float64{120.185614,30.300738}}}
b := Point{"张三", 25, "杭州", Location{"Point", []float64{120.094778,30.310217}}}
c := Point{"小晴", 35, "绍兴", Location{"Point", []float64{120.603847,30.054237}}}
d := Point{"李四", 34, "杭州", Location{"Point", []float64{120.110893,30.207849}}}
e := Point{"小明", 24, "北京", Location{"Point", []float64{116.435721,39.914031}}}
f := Point{"吴六", 25, "杭州", Location{"Point", []float64{120.126443,30.33084}}}
h := Point{"于一", 23, "杭州", Location{"Point", []float64{120.28132,30.184083}}}
j := Point{"小七", 14, "杭州", Location{"Point", []float64{119.73926,30.247639}}}

// 单条插入
insertResult, err := collection.InsertOne(context.TODO(), a)
if err != nil {
log.Fatal(err)
}
fmt.Println("Inserted a single document: ", insertResult.InsertedID)

ps := []interface{}{b, c, d, e, f, h, j}

// 批量插入
insertManyResult, err := collection.InsertMany(context.TODO(), ps)
if err != nil {
log.Fatal(err)
}
fmt.Println("Inserted multiple documents: ", insertManyResult.InsertedIDs)
}

注意,设置索引 2dsphere, 很重要!!!

img存储的数据格式

我们查找:

func (mgo *mgo) Near() {
collection := mgo.client.Database(DBName).Collection(CollectionName)
// 查找120.110893,30.2078490坐标附近15000米的人
cur, err := collection.Find(context.TODO(), bson.D{
{Key, bson.D{
{"$near", bson.D{
{
"$geometry", Location{
"Point",
[]float64{120.110893,30.2078490},
},
},
{"$maxDistance", 15000}, // 单位米
}},
}},
})

if err != nil {
fmt.Println(err)
return
}
var results []Point

for cur.Next(context.TODO()) {
var elem Point
err := cur.Decode(&elem)
fmt.Println(elem)
fmt.Println(cur)
if err != nil {
fmt.Println("Could not decode Point")
return
}

results = append(results, elem)
}
fmt.Println("查找到", len(results))
}

我们不妨运行一下:

img

能成功查找出了附近的人。

那么,如何计算距离呢?

可以经纬度计算,也可以直接mongoDB聚合查询,可以自行思索,参考代码点击见github