Correct: 5.2.2. Transform References
if defined(R.query) then
T.query = R.query;
else
T.query = Base.query;
endif;
i.e. the official algorithm uses the Base URI query parameters only if the Reference URI doesn’t have any query parameters - otherwise the Reference URI query parameters are used while the ones from the Base are ignored.
Am I missing a different way of merging query strings?
I’d be inclined to manage this situation a bit differently:
iex(1)> uri = "http://example.com/path/to/page?name=ferret&color=purple" |> URI.parse()
%URI{
authority: "example.com",
fragment: nil,
host: "example.com",
path: "/path/to/page",
port: 80,
query: "name=ferret&color=purple",
scheme: "http",
userinfo: nil
}
iex(2)> query_decoded = URI.decode_query(uri.query) # use a Map instead of string
%{"color" => "purple", "name" => "ferret"}
iex(3)> my_uri = uri |> Map.from_struct() |> Map.put(:query, query_decoded) # Use a Map instead of URI struct
%{
authority: "example.com",
fragment: nil,
host: "example.com",
path: "/path/to/page",
port: 80,
query: %{"color" => "purple", "name" => "ferret"},
scheme: "http",
userinfo: nil
}
iex(4)> query_key = Access.key(:query, %{}) # returns ":query" value or empty map if missing
#Function<4.127282935/3 in Access.key/2>
iex(5)> query_merge = fn uri, params ->
...(5)> merge_query_params = &(Map.merge(&1, params)) # params is a map to accept more than one param; last name/value wins for duplicate names
...(5)> update_in(uri, [query_key], merge_query_params)
...(5)> end
#Function<12.127694169/2 in :erl_eval.expr/5>
iex(6)> no_query = Map.delete(my_uri, :query) # remove existing query params
%{
authority: "example.com",
fragment: nil,
host: "example.com",
path: "/path/to/page",
port: 80,
scheme: "http",
userinfo: nil
}
iex(7)> my_weasel = query_merge.(no_query, %{name: "weasel"}) # add the first query params
%{
authority: "example.com",
fragment: nil,
host: "example.com",
path: "/path/to/page",
port: 80,
query: %{name: "weasel"},
scheme: "http",
userinfo: nil
}
iex(8)> my_green = query_merge.(my_weasel, %{color: "green"}) # append more query params
%{
authority: "example.com",
fragment: nil,
host: "example.com",
path: "/path/to/page",
port: 80,
query: %{color: "green", name: "weasel"},
scheme: "http",
userinfo: nil
}
iex(9)> query_encoded = URI.encode_query(my_green.query) # put it all together at the latest possible moment
"color=green&name=weasel"
iex(10)> new_my_uri = Map.put(my_green, :query, query_encoded)
%{
authority: "example.com",
fragment: nil,
host: "example.com",
path: "/path/to/page",
port: 80,
query: "color=green&name=weasel",
scheme: "http",
userinfo: nil
}
iex(11)> new_uri = struct(URI, new_my_uri) # dump map contents into URI struct
%URI{
authority: "example.com",
fragment: nil,
host: "example.com",
path: "/path/to/page",
port: 80,
query: "color=green&name=weasel",
scheme: "http",
userinfo: nil
}
iex(12)> URI.to_string(new_uri)
"http://example.com/path/to/page?color=green&name=weasel"
iex(13)>