简体   繁体   English

Golang GORM 使用 Lat 和 Lng 查询某个位置周围的位置

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM