clojureql icon indicating copy to clipboard operation
clojureql copied to clipboard

The project+ fn

Open r0man opened this issue 14 years ago • 7 comments

Sometimes I want to add artificial fields to a query but can't use project, because it overwrites previous selections. What about adding something like:

(defn project+
  [relation fields]
  (assoc relation :tcols (concat (:tcols relation) fields)))

to ClojureQL? With this function I can add a "distance calculation" to the query with something like this:

(-> spots-with-address
    (select-in-bounding-box :spots.location bounding-box)
    (project+ [[(str "distance(spots.location, ST_GeomFromText('" (make-point-2d 0 0) "'))") :as :distance]])
    (sort [:distance]))

Thoughts? Suggestions?

r0man avatar Mar 26 '11 23:03 r0man

We debated this quite a bit when doing the initial implementation. What it came down to is that project is by definition non-additive. Since I cant offer deeper insights into RA than the creators of it, I decided to adhere by their standards. I think this is a good example of how easy it is to extend ClojureQL, without the need to modify ClojureQL itself.

Your thoughts?

LauJensen avatar Mar 27 '11 00:03 LauJensen

I can understand that you want to keep it clean. On the other hand I can see me copying this fn to all of my ClojureQL projects. And I hate copying :) Maybe somthing for clojureql.utils?

r0man avatar Mar 27 '11 00:03 r0man

Thats not a bad idea. We really should compile a list of things for cql.utils

LauJensen avatar Mar 27 '11 00:03 LauJensen

What about making it so this does what you want?

(project [:* ["distance(...)" :as :distance]])

ninjudd avatar Mar 27 '11 04:03 ninjudd

Agreed. I think :* should mean: all columns projected so far. If you want all columns in a particular table, then use :spots.*

ninjudd avatar Mar 27 '11 13:03 ninjudd

Oops. Was trying to fix formatting on r0man's post, but accidentally hit delete (fingers are too big for my iPhone).

ninjudd avatar Mar 27 '11 14:03 ninjudd

Here is r0man's message:

I tried this, but the spots-with-address is actually defined like this:

(def spots-with-address
  (-> spots
    (join (project (table :countries) [[:name :as :address_country]])
          (where (= :spots.country_id :countries.id)))
    (join (project (table :regions) [[:name :as :address_region]])
          (where (= :spots.region_id :regions.id)))
    (outer-join (project (table :addresses)
                         [[:locality :as :address_locality]
                          [:postal_code :as :address_postal_code]
                          [:street_address :as :address_street_address]
                          [:extended_address :as :address_extended_address]])
                :left (where (geo= :spots.location :addresses.location)))))

When using spots-with-address defined like above and applying the project operator in another function like this:

(project [:* ["distance(...)" :as :distance]])

... it selects everything from the spots table (spots.*) and the distance. All the other columns I have joined above get thrown away.

The shortcut :* in this example is really :spots._. Applying :_ doesn't honor my previous selection, but starts back looking at the spots table and not my new relation spots-with-address.

I think since :* is not defined in RA (you give all the attributes you want to project) it's questionalble if the shortcut :* applys to the relation (spots-with-address) at hand or the original one (spots). I think the first one is the route to true composability ;)

ninjudd avatar Mar 27 '11 14:03 ninjudd