Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

h3 #989

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft

h3 #989

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/since/accounts.ex
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,7 @@ defmodule Since.Accounts do
def get_location_gender_premium_hidden?(user_id) do
Profile
|> where(user_id: ^user_id)
# TODO
|> select([p], {p.location, p.gender, p.premium, p.hidden?})
|> Repo.one!()
end
Expand Down
16 changes: 5 additions & 11 deletions lib/since/chats.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ defmodule Since.Chats do

import Ecto.{Query, Changeset}
alias Ecto.Multi
import Geo.PostGIS

require Logger
require Since.Geo

alias Since.{Repo, Bot}
alias Since.Chats.{Chat, Message}
Expand Down Expand Up @@ -35,15 +35,6 @@ defmodule Since.Chats do
Phoenix.PubSub.broadcast_from(@pubsub, self(), pubsub_user_topic(user_id), message)
end

defmacrop distance_km(location1, location2) do
quote do
fragment(
"round(? / 1000)::int",
st_distance_in_meters(unquote(location1), unquote(location2))
)
end
end

def list_chats(user_id, location) do
Chat
|> where([c], c.user_id_1 == ^user_id or c.user_id_2 == ^user_id)
Expand All @@ -63,10 +54,13 @@ defmodule Since.Chats do

mates = Map.keys(mate_chats)

%Geo.Point{coordinates: {user_lon, user_lat}, srid: 4326} = location
location_h3 = :h3.from_geo({user_lat, user_lon}, 7)

profiles =
FeedProfile
|> where([p], p.user_id in ^mates)
|> select([p], %{p | distance: distance_km(^location, p.location)})
|> select([p], %{p | distance: Since.Geo.distance_km(^location_h3, p.h3)})
|> Repo.all()
|> Map.new(fn profile -> {profile.user_id, profile} end)

Expand Down
54 changes: 30 additions & 24 deletions lib/since/feeds.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ defmodule Since.Feeds do

import Ecto.{Query, Changeset}
alias Ecto.Multi
import Geo.PostGIS

require Logger
require Since.Geo

alias Since.{Repo, Bot}

Expand Down Expand Up @@ -43,15 +43,6 @@ defmodule Since.Feeds do
Phoenix.PubSub.broadcast(@pubsub, pubsub_user_topic(user_id), message)
end

defmacrop distance_km(location1, location2) do
quote do
fragment(
"round(? / 1000)::int",
st_distance_in_meters(unquote(location1), unquote(location2))
)
end
end

### Feed

@feed_fetch_count 10
Expand Down Expand Up @@ -188,14 +179,18 @@ defmodule Since.Feeds do
end

defp first_time_user_feed(user_id, location, gender, feed_filter, count) do
location_h3 = Since.Geo.point_to_h3(location)

feed_profiles_q(user_id, gender, feed_filter.genders)
|> where([p], p.times_liked >= ^@quality_likes_count_treshold)
|> where([p], fragment("jsonb_array_length(?) > 2", p.story))
|> order_by(fragment("location <-> ?::geometry", ^location))
|> order_by([p],
asc_nulls_last: fragment("h3_grid_distance(?, ?)", ^location_h3, p.h3)
)
|> maybe_apply_age_filters(feed_filter)
|> maybe_apply_distance_filter(location, feed_filter.distance)
|> maybe_apply_distance_filter(feed_filter.distance)
|> limit(^count)
|> select([p], %{p | distance: distance_km(^location, p.location)})
|> select([p], %{p | distance: Since.Geo.distance_km(^location_h3, p.h3)})
|> Repo.all()
end

Expand All @@ -208,25 +203,31 @@ defmodule Since.Feeds do
feed_filter,
count
) do
%Geo.Point{coordinates: {search_lon, search_lat}, srid: 4326} = location
location_h3 = :h3.from_geo({search_lat, search_lon}, 7)

feed_profiles_q(user_id, gender, feed_filter.genders)
|> where([p], p.user_id not in ^calculated_feed_ids)
|> where([p], p.user_id not in subquery(feeded_ids_q))
|> not_seen_profiles_q(user_id)
|> order_by(fragment("location <-> ?::geometry", ^location))
|> order_by(fragment("h3_grid_distance(?, ?)", ^location_h3, p.h3))
|> maybe_apply_age_filters(feed_filter)
|> maybe_apply_distance_filter(location, feed_filter.distance)
|> maybe_apply_distance_filter(feed_filter.distance)
|> limit(^count)
|> select([p], %{p | distance: distance_km(^location, p.location)})
|> select([p], %{p | distance: Since.Geo.distance_km(^location_h3, p.h3)})
|> Repo.all()
end

defp preload_feed_profiles(profile_ids, user_id, location, gender, feed_filter) do
%Geo.Point{coordinates: {search_lon, search_lat}, srid: 4326} = location
location_h3 = :h3.from_geo({search_lat, search_lon}, 7)

feed_profiles_q(user_id, gender, feed_filter.genders)
|> where([p], p.user_id in ^profile_ids)
|> maybe_apply_age_filters(feed_filter)
|> maybe_apply_distance_filter(location, feed_filter.distance)
|> maybe_apply_distance_filter(feed_filter.distance)
|> limit(^@feed_fetch_count)
|> select([p], %{p | distance: distance_km(^location, p.location)})
|> select([p], %{p | distance: Since.Geo.distance_km(^location_3, p.h3)})
|> Repo.all()
end

Expand All @@ -246,15 +247,18 @@ defmodule Since.Feeds do
feeded_ids_q,
feed_filter
) do
%Geo.Point{coordinates: {search_lon, search_lat}, srid: 4326} = location
location_h3 = :h3.from_geo({search_lat, search_lon}, 7)

feed_profiles_q(user_id, gender, feed_filter.genders)
|> where([p], p.user_id not in subquery(feeded_ids_q))
|> join(:left, [p, g], s in SeenProfile, on: [user_id: p.user_id, by_user_id: ^user_id])
|> maybe_filter_by_sticker(feed_category)
|> order_by_feed_category(feed_category, location)
|> maybe_apply_age_filters(feed_filter)
|> maybe_apply_distance_filter(location, feed_filter.distance)
|> maybe_apply_distance_filter(feed_filter.distance)
|> limit(^@feed_fetch_count)
|> select([p, g, s], %{p | distance: distance_km(^location, p.location)})
|> select([p, g, s], %{p | distance: Since.Geo.distance_km(^location_h3, p.h3)})
|> Repo.all()
end

Expand Down Expand Up @@ -313,10 +317,9 @@ defmodule Since.Feeds do
end
end

defp maybe_apply_distance_filter(query, location, distance) do
defp maybe_apply_distance_filter(query, distance) do
if distance do
meters = distance * 1000
where(query, [p], st_dwithin_in_meters(^location, p.location, ^meters))
having(query, [p], p.distance <= ^distance)
else
query
end
Expand Down Expand Up @@ -352,9 +355,12 @@ defmodule Since.Feeds do

@spec get_mate_feed_profile(Ecto.UUID.t(), Geo.Point.t()) :: %FeedProfile{} | nil
def get_mate_feed_profile(user_id, location) do
%Geo.Point{coordinates: {lon, lat}, srid: 4326} = location
location_h3 = :h3.from_geo({lat, lon}, 7)

not_hidden_profiles_q()
|> where(user_id: ^user_id)
|> select([p], %{p | distance: distance_km(^location, p.location)})
|> select([p], %{p | distance: Since.Geo.distance_km(^location_h3, p.h3)})
|> Repo.one()
end

Expand Down
20 changes: 20 additions & 0 deletions lib/since/geo.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
defmodule Since.Geo do
@moduledoc "Helpers for working with geographic data."
import Ecto.Query

@spec point_to_h3(Geo.Point.t()) :: :h3.h3_index()
def point_to_h3(point) do
%Geo.Point{coordinates: {lon, lat}, srid: 4326} = point
:h3.from_geo({lat / 1, lon / 1}, 7)
end

@doc """
Computes [approximated](https://jonisalonen.com/2014/computing-distance-between-coordinates-can-be-simple-and-fast/)
distance in kilometers between two H3 indexes.
"""
defmacro fast_distance_km(target_h3, origin_h3) do
quote do
fragment("fast_distance_km(?,?)", unquote(target_h3), unquote(origin_h3))
end
end
end
1 change: 1 addition & 0 deletions lib/since_web/channels/feed_channel.ex
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ defmodule SinceWeb.FeedChannel do
defp join_normal_mode(user_id, params, socket) do
feed_filter = Feeds.get_feed_filter(user_id)

# TODO
{old_location, gender, premium, hidden?} =
Accounts.get_location_gender_premium_hidden?(user_id)

Expand Down
14 changes: 5 additions & 9 deletions lib/since_web/views/profile_view.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ defmodule SinceWeb.ProfileView do
alias Since.Accounts.Profile

def render("show_with_location.json", %{
profile: %Profile{location: location} = profile,
profile: %Profile{h3: h3} = profile,
screen_width: screen_width,
version: version
}) do
{lat, lon} = if h3, do: :h3.to_geo(h3), else: {nil, nil}

profile
|> render_profile(
[
Expand All @@ -27,16 +29,10 @@ defmodule SinceWeb.ProfileView do
screen_width,
_env = :profile
)
|> Map.put(:latitude, lat(location))
|> Map.put(:longitude, lon(location))
|> Map.put(:latitude, lat)
|> Map.put(:longitude, lon)
end

defp lat(%Geo.Point{coordinates: {_lon, lat}}), do: lat
defp lat(nil), do: nil

defp lon(%Geo.Point{coordinates: {lon, _lat}}), do: lon
defp lon(nil), do: nil

defp render_profile(%Profile{story: story} = profile, fields, version, screen_width, env) do
profile
|> Map.take(fields)
Expand Down
6 changes: 3 additions & 3 deletions test/t_web/channels/feed_channel_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ defmodule SinceWeb.FeedChannelTest do
story: [],
user_id: p3.id,
gender: "M",
distance: 9510,
distance: 9485,
address: %{
"en_US" => %{
"city" => "Buenos Aires",
Expand Down Expand Up @@ -248,7 +248,7 @@ defmodule SinceWeb.FeedChannelTest do
story: [],
user_id: p2.id,
gender: "N",
distance: 9510,
distance: 9485,
address: %{
"en_US" => %{
"city" => "Buenos Aires",
Expand Down Expand Up @@ -277,7 +277,7 @@ defmodule SinceWeb.FeedChannelTest do
story: [],
user_id: p1.id,
gender: "F",
distance: 9510,
distance: 9485,
address: %{
"en_US" => %{
"city" => "Buenos Aires",
Expand Down
Loading