Skip to content
This repository has been archived by the owner on Jan 30, 2019. It is now read-only.

Allow repeated keys in query params #13

Open
favila opened this issue May 1, 2014 · 2 comments
Open

Allow repeated keys in query params #13

favila opened this issue May 1, 2014 · 2 comments

Comments

@favila
Copy link

favila commented May 1, 2014

(This is a strawman proposal.)

Query parameters in uris allow keys to be repeated. It would be nice if this were supported as many services make use of this, e.g. default query param parsing in PHP and ring.middleware.params.

I propose that if the value in query map is a seq, repeat the key in the serialized uri for each value in the seq. Likewise on deserialization emit a vector of values for the key.

E.g.

(-> (url "http://example.org?a=foo&a=bar") (:query))
; => {"a" ["foo" "bar"]}
(str (-> (url "http://example.org") (assoc :a ["foo" "bar"]))
; => "http://example.org?a=foo&a=bar"

The problem with this is that the cardinality of query param values in parsed urls will depend on the query string.

Other approaches I've seen paper over this difference by using a custom type that pretends all keys have scalar values unless you do something special to ask for the other values for a key (e.g. many Python libraries use a "MultiDict" with a special .getall(k) method). This could be done in clojure with a custom type for query parameters that returns the last seen value for lookups, but has some extra protocol that returns a vector. (However if you go down this route you no longer have a simple library.)

Another possibility is to force the user to be explicit about which query params are cardinality-many. This could be done with different url functions, examples:

(-> (multi-query-url "http://example?a=foo&a=bar&b=baz") :query)
;=> {"a" ["foo" "bar"] "b" ["baz"]}
(-> (multi-query-url "http://example?a=foo&a=bar&b=baz" #{"a"}) :query)
;=> {"a" ["foo" "bar"] "b" "baz"}
(-> (multi-query-url "http://example?a=foo&a=bar&b=baz" (complement #{"a"})) :query)
;=> {"a" "bar" "b" ["baz"]}
;;;  (A subset of) PHP style:
(-> (multi-query-url "http://example?a[]=foo&a[]=bar&b=baz" #(.endsWith % "[]")) :query)
;=> {"a" ["foo" "bar"] "b" "baz"}

Thoughts?

@cemerick
Copy link
Owner

Right, so we actually need to work with multimaps. I don't think a special type is necessary, or helpful in this case. I'm happy to have params that have a single value be represented with just a string; no need for single-element vectors. I'm not sure of the value of allowing some kind of filter function as a param to url.

I'd accept a PR supporting this.

@saintx
Copy link

saintx commented Oct 31, 2018

Yes, please!

I was very surprised that

{:protocol "http",
 :username nil,
 :password nil,
 :host "localhost",
 :port 3000,
 :path "/foo",
 :query {:bar ["baz" "qux"]},
 :anchor nil}

doesn't become http://localhost:3000/foo?bar=baz&bar=qux

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants