go+MongoDB实现附近的人

在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


go+MongoDB实现附近的人
https://blog.puresai.com/2021/02/12/goexample10/
作者
puresai
许可协议