[英]Golang GORM query locations around a position with Lat and Lng
I'm trying to make a specific request with GORM .我正在尝试使用GORM提出具体要求。
Here are the tables I use:这是我使用的表格:
+-------------------+
| Tables |
+-------------------+
| locations |
| shops |
| shops_tags |
| tags |
+-------------------+
Locations table位置表
+------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| shop_id | bigint(20) | YES | | NULL | |
| lat | decimal(10,8) | YES | | NULL | |
| lng | decimal(11,8) | YES | | NULL | |
+------------+------------------+------+-----+---------+----------------+
Location model位置模型
type Location struct {
gorm.Model
ShopID int64
Shop Shop
Lat float64 `gorm:"type:decimal(10,8)"`
Lng float64 `gorm:"type:decimal(11,8)"`
}
Shops table店铺表
+-------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| name | varchar(180) | YES | UNI | NULL | |
| active | tinyint(1) | YES | | 1 | |
+-------------+------------------+------+-----+---------+----------------+
Shop model店铺模型
type Shop struct {
gorm.Model
Name string `json:"name" gorm:"type:varchar(180);unique_index"`
Active int `json:"active" gorm:"type:tinyint(1);default:1"`
Tags []*Tag `json:"tags" gorm:"many2many:shops_tags;"`
Locations []Location `json:"locations" gorm:"locations"`
}
Tag model标签模型
type Tag struct {
gorm.Model
Name string `json:"name" gorm:"type:varchar(180)"`
Shops []*Shop `json:"shops" gorm:"many2many:shops_tags;"`
}
Handler to return query in JSON以 JSON 格式返回查询的处理程序
func GetShops(c echo.Context) error {
db := db.DbManager()
// Get POST data (name lat and lng)
type geo struct {
Lat string `json:"lat" form:"lat" query:"lat"`
Lng string `json:"lng" form:"lng" query:"lng"`
}
// Bind request body
g := new(geo)
if err := c.Bind(g); err != nil {
return c.JSON(http.StatusForbidden, err)
}
shops := []model.Shop{}
// Totally disordered attempt of a query with `go-gorm` to display the list of nearby shops, sorted by the distance between us.
db.Preload("Locations", func(db *gorm.DB) *gorm.DB {
// How here use g.Lat and g.Lng for this request
return db.Order("locations.id DESC").Limit(1)
}).Preload("Tag").Find(&shops)
// Json
return c.JSON(http.StatusOK, echo.Map{
"shops": &shops,
})
}
So we have Stores , which have Locations , we want to display Stores within 15km of our Location .所以我们有Stores ,它有Locations ,我们想在我们Location的 15 公里范围内显示Stores 。 Only the last location is useful, that's why, we limited the results to 1 in decreasing order.只有最后一个位置是有用的,这就是为什么我们将结果限制为 1(按降序排列)。
Edit编辑
Thanks to @Rick James for suggesting me to rephrase my question, here is the query in MYSQL
, how to adapt it to go-gorm
:感谢@Rick James建议我改写我的问题,这里是MYSQL
中的查询,如何使其适应go-gorm
:
SELECT
shops.id,
shops.name, (
(
6371.04 * ACOS(
(
(
COS(
(
(
PI() / 2
) - RADIANS(
(90 - locations.lat)
)
)
) * COS(
PI() / 2 - RADIANS(90 - -33.73788500)
) * COS(
(
RADIANS(locations.lng) - RADIANS('151.23526000')
)
)
) + (
SIN(
(
(
PI() / 2
) - RADIANS(
(90 - locations.lat)
)
)
) * SIN(
(
(
PI() / 2
) - RADIANS(90 - -33.73788500)
)
)
)
)
)
)
) AS 'distance',
locations.id AS 'location_id',
locations.shop_id,
locations.lat,
locations.lng,
locations.created_at
FROM
shops
INNER JOIN locations ON(
locations.created_at >= '2017-12-13'
AND(
(
6371.04 * ACOS(
(
(
COS(
(
(
PI() / 2
) - RADIANS(
(90 - locations.lat)
)
)
) * COS(
PI() / 2 - RADIANS(90 - -33.73788500)
) * COS(
(
RADIANS(locations.lng) - RADIANS('151.23526000')
)
)
) + (
SIN(
(
(
PI() / 2
) - RADIANS(
(90 - locations.lat)
)
)
) * SIN(
(
(
PI() / 2
) - RADIANS(90 - -33.73788500)
)
)
)
)
)
)
) < '500'
AND shops.id = (locations.shop_id)
)
WHERE
shops.active = 1
GROUP BY
shops.id
ORDER BY
distance ASC
LIMIT
100
Looking at the query I would definitely hand craft this one and not rely on any ORM to execute the query.查看查询,我肯定会手工制作这个查询,而不依赖任何 ORM 来执行查询。 Refer to using the Exec method on a gorm database instance ref .请参阅在 gorm 数据库实例ref上使用 Exec 方法。
But... To use an ORM like gorm, using gorm's SQL-Builder is a possibility.但是...要使用像 gorm 这样的 ORM,可以使用 gorm 的 SQL-Builder。 Here is an example that should be of guidance.这是一个应该具有指导意义的示例。 Please note that this is for SQLite Database, and not for MYSQL so the functions in use do not exist.请注意,这是针对 SQLite 数据库的,而不是针对 MYSQL 的,因此正在使用的函数不存在。 We are mainly concerned with building the query.我们主要关心构建查询。
package main
import (
"fmt"
"time"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
)
const (
layoutISO = "2006-01-02"
)
type Shop struct {
gorm.Model
Name string `json:"name" gorm:"type:varchar(180);unique_index"`
Active int `json:"active" gorm:"type:tinyint(1);default:1"`
Tags []Tag `json:"tags" gorm:"many2many:shops_tags;"`
Locations []Location `json:"locations" gorm:"locations"`
}
type Tag struct {
gorm.Model
Name string `json:"name" gorm:"type:varchar(180)"`
Shops []Shop `json:"shops" gorm:"many2many:shops_tags;"`
}
type Location struct {
gorm.Model
ShopID int64
Shop Shop
Lat float64 `gorm:"type:decimal(10,8)"`
Lng float64 `gorm:"type:decimal(11,8)"`
}
// Get POST data (name lat and lng)
type geo struct {
Lat float64 `json:"lat" form:"lat" query:"lat"`
Lng float64 `json:"lng" form:"lng" query:"lng"`
}
var distance_calculation = `
(
(
6371.04 * ACOS(((COS(((PI() / 2) - RADIANS((90 - locations.lat)))) *
COS(PI() / 2 - RADIANS(90 - ?)) *
COS((RADIANS(locations.lng) - RADIANS(?))))
+ (SIN(((PI() / 2) - RADIANS((90 - locations.lat)))) *
SIN(((PI() / 2) - RADIANS(90 - ?))))))
)
)`
func main() {
db, err := gorm.Open("sqlite3", "test.db")
if err != nil {
panic("failed to connect database")
}
defer db.Close()
// Enable Logger, show detailed log
db.LogMode(true)
// Migrate the schema
db.AutoMigrate(&Shop{})
db.AutoMigrate(&Tag{})
db.AutoMigrate(&Location{})
g := new(geo)
g.Lat = -33.73788500
g.Lng = 151.23526000
type Result struct {
ID uint
Name string
Distance int
LocationID uint
ShopID uint
Lat float64
Lng float64
CreatedAt time.Time
}
date, _ := time.Parse(layoutISO, "2017-12-13")
var t []Result
err = db.Table("Shops").
Select("shops.id, shops.name, locations.id AS 'location_id', locations.shop_id, locations.lat, locations.lng, locations.created_at, "+
distance_calculation+" as distance\n", g.Lat, g.Lng, g.Lat).
Joins("inner join locations on shops.id = locations.shop_id\n").
Where(&Shop{Active: 1}).
Where("locations.created_at >= CAST(strftime('%s', ?) AS INT) * 1000", date).
Where(distance_calculation + " < ?", g.Lat, g.Lng, g.Lat, 500).
Group("\nshops.id\n").
Order("locations.id DESC\n").
Limit(100).Scan(&t).Error
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", t)
}
Query Built with SQL Builder使用 SQL Builder 构建的查询
(C:/Users/fakename/source/stackoverflow/go/geo-go-gorm/main.go:97)
[2019-10-31 15:36:37] [1.00ms] SELECT shops.id, shops.name, locations.id AS 'location_id', locations.shop_id, locations.lat, locations.lng, locations.created_at,
(
(
6371.04 * ACOS(((COS(((PI() / 2) - RADIANS((90 - locations.lat)))) *
COS(PI() / 2 - RADIANS(90 - -33.737885)) *
COS((RADIANS(locations.lng) - RADIANS(151.23526))))
+ (SIN(((PI() / 2) - RADIANS((90 - locations.lat)))) *
SIN(((PI() / 2) - RADIANS(90 - -33.737885))))))
)
) as distance
FROM "Shops" inner join locations on shops.id = locations.shop_id
WHERE ("shops"."active" = 1) AND (locations.created_at >= CAST(strftime('%s', '2017-12-13 00:00:00') AS INT) * 1000) AND (
(
(
6371.04 * ACOS(((COS(((PI() / 2) - RADIANS((90 - locations.lat)))) *
COS(PI() / 2 - RADIANS(90 - -33.737885)) *
COS((RADIANS(locations.lng) - RADIANS(151.23526))))
+ (SIN(((PI() / 2) - RADIANS((90 - locations.lat)))) *
SIN(((PI() / 2) - RADIANS(90 - -33.737885))))))
)
) < 500
) GROUP BY shops.id
ORDER BY locations.id DESC
LIMIT 100
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.