2017-05-16 76 views
5

Używając Sequelize i zapytań geoprzestrzennych, jeśli chcę znaleźć "n" najbliższe punkty do określonej lokalizacji, jak powinno wyglądać zapytanie Sequelize?Sequelize zapytanie geoprzestrzenne: znajdź "n" najbliższe punkty do lokalizacji

Załóżmy Mam modelu, który wygląda mniej więcej tak:

sequelize.define('Point', {geo: DataTypes.GEOMETRY('POINT')}); 

Teraz powiedzmy, że wejście 100 losowo wybranych punktów w db przez coś podobnego:

db.Point.create({geo: {type: 'Point', coordinates: [randomLat, randomLng]}}); 

Wyobraź mamy lat i lng zmienne do zdefiniowania lokalizacji, a my chcemy znaleźć 10 najbliższych punktów do niej. Po uruchomieniu tego zapytania pojawia się błąd:

const location = sequelize.literal(`ST_GeomFromText('POINT(${lat} ${lng})', 4326)`); 

db.Point.findAll({ 
    attributes: [['distance', sequelize.fn('ST_Distance', sequelize.col('Point'), location)]], 
    order: 'distance', 
    limit: 10 
}); 

// -> TypeError: s.replace is not a function 

Każdy pomysł, jaki jest problem/jak go naprawić?

Thx!

+0

Nie jestem pewien, ale wygląda na to, że masz dodatkowe nawiasy kwadratowe wokół atrybutów –

+0

jakieś wiadomości na ten temat? –

Odpowiedz

1

Kiedy otaczają sequelize.fn ze wspornikami, należy również zawierać ciąg jako alias:

[sequelize.fn('ST_Distance_Sphere', sequelize.literal('geolocation'), location), 'ALIASNAME'] 

Ponadto, spróbuj zmienić ST_Distance do ST_Distance_Sphere. A więc:

const location = sequelize.literal(`ST_GeomFromText('POINT(${lat} ${lng})', 4326)`); 

    User.findAll({ 
     attributes: [[sequelize.fn('ST_Distance_Sphere', sequelize.literal('geolocation'), location),'distance']], 
     order: 'distance', 
     limit: 10, 
     logging: console.log 
    }) 
    .then(function(instance){ 
     console.log(instance); 
    }) 

To faktycznie działa dla mnie. obs: upewnij się, że podstawisz "Użytkownik" do modelu, w którym masz typ danych geometrii.

Aktualizacja: Jeśli nadal nie można zamówić za pomocą order: 'distance', może powinieneś zadeklarować ją w var i używać order: distance bez cudzysłowów, tak:

var lat = parseFloat(json.lat); 
    var lng = parseFloat(json.lng); 
    var attributes = Object.keys(User.attributes); 

    var location = sequelize.literal(`ST_GeomFromText('POINT(${lat} ${lng})')`); 
    var distance = sequelize.fn('ST_Distance_Sphere', sequelize.literal('geolocation'), location); 
    attributes.push([distance,'distance']); 

    var query = { 
     attributes: attributes, 
     order: distance, 
     include: {model: Address, as: 'address'}, 
     where: sequelize.where(distance, {$lte: maxDistance}), 
     logging: console.log 
    } 
+0

To zapytanie daje mi następujący błąd: 'Błąd: Order musi być typem tablicy lub wystąpieniem prawidłowej metody sequelize." Próbowałem zmienić 'order' na' ['distance'] 'zamiast' 'distance'' i teraz otrzymuję: 'SequelizeDatabaseError: column User.distance nie istnieje'. – Pensierinmusica

+0

czy w atrybutach umieściłeś ciąg "odległość"? odległość bez cudzysłowów również powinna działać: ... 'order: distance' ... – Edudjr

0

Budowanie off @ Edudjr za odpowiedź to jest to, co zrobiłem, aby zmusić go do pracy w moim projekcie:

const location = sequelize.literal(`ST_GeomFromText('POINT(${ startLongitude } ${ startLatitude })')`) 
const distance = sequelize.fn('ST_Distance_Sphere', sequelize.col('location'), location) 

const inRadius = await Position.findAll({ 
    order: distance, 
    where: sequelize.where(distance, { $lte: radius }), 
    logging: console.log 
}) 

gdzie pozycja jest zdefiniowana jako:

sequelize.define('Position', { 
    location: DataTypes.GEOMETRY('POINT') 
}) 

Note that Point requires the coordinates in the format of (longitude latitude)

https://gis.stackexchange.com/questions/209008/incorrect-arguments-to-st-distance-sphere-in-special-cases

https://dba.stackexchange.com/questions/33410/whats-the-difference-between-pointx-y-and-geomfromtextpointx-y

0

MySQL może dać błąd tej funkcji ST_Distance_Sphere nie istnieje. W takim przypadku możesz użyć tego alternatywnego rozwiązania:

Informacje o punkcie zaczepienia oddzielnie, jako wartości po przecinku szerokości i długości geograficznej.Zakładamy, trzeba mieć model, który wygląda mniej więcej tak:

sequelize.define('Point', {latitude: DataTypes.DECIMAL(11,2)}, 
          {longitude: DataTypes.DECIMAL(11,2)}); 

Imagine mamy lat i lng zmiennych zdefiniować lokalizację, a chcemy znaleźć 10 najbliższych punktów do niego:

db.Point.findAll({ 
    attributes: [[sequelize.fn('POW',sequelize.fn('ABS',sequelize.literal("latitude-"+lat)),2),'x1'], 
       [sequelize.fn('POW',sequelize.fn('ABS',sequelize.literal("longitude-"+lng)),2),'x2']], 
    order: sequelize.fn('SQRT', sequelize.literal('x1+x2')), 
    limit: 10 
}); 

Aktualizacja:

Z Haversine Formula, odległość jest dokładniejsza:

db.Point.findAll({ 
    attributes: [[sequelize.literal("6371 * acos(cos(radians("+lat+")) * cos(radians(latitude)) * cos(radians("+lng+") - radians(longitude)) + sin(radians("+lat+")) * sin(radians(latitude)))"),'distance']], 
    order: sequelize.col('distance'), 
    limit: 10 
});