Skip to content
Snippets Groups Projects
Commit 1632e405 authored by Cam Saul's avatar Cam Saul
Browse files

Basic mongo subfield support

parent 5b4b7b80
No related branches found
No related tags found
No related merge requests found
......@@ -5,6 +5,7 @@
(def ^:const driver-optional-features
"A set on optional features (as keywords) that may or may not be supported by individual drivers."
#{:foreign-keys
:nested-fields ; are nested Fields (i.e., Mongo-style nested keys) supported?
:set-timezone
:standard-deviation-aggregations
:unix-timestamp-special-type-fields})
......
......@@ -42,7 +42,7 @@
(def ^:const ^:private mongo-driver-features
"Optional features supported by the Mongo driver."
#{}) ; nothing yet
#{:nested-fields})
(deftype MongoDriver []
IDriver
......
......@@ -82,10 +82,15 @@
[{$match *constraints*}])
~@(filter identity forms)]))
(defn field->$str
(defn- field->name
[{:keys [field-name subfield]}]
(if subfield (format "%s.%s" field-name subfield)
field-name))
(defn- field->$str
"Given a FIELD, return a `$`-qualified field name for use in a Mongo aggregate query, e.g. `\"$user_id\"`."
[field]
(format "$%s" (name (:field-name field))))
(format "$%s" (name (field->name field))))
(defn- aggregation:rows []
`(doall (with-collection ^DBApiLayer *mongo-connection* ~*collection-name*
......@@ -99,7 +104,7 @@
([field]
`[{:count (mc/count ^DBApiLayer *mongo-connection* ~*collection-name*
(merge ~*constraints*
{(:field-name field) {$exists true}}))}]))
{(field->name field) {$exists true}}))}]))
(defn- aggregation:avg [field]
(aggregate {$group {"_id" nil
......@@ -176,7 +181,7 @@
is present, this is essentialy a separate implementation :/"
[{aggregation :aggregation, breakout-fields :breakout, order-by :order-by, limit :limit, :as query}]
(let [[ag-field ag-clause] (breakout-aggregation->field-name+expression aggregation)
fields (map :field-name breakout-fields)
fields (map field->name breakout-fields)
$fields (map field->$str breakout-fields)
fields->$fields (zipmap fields $fields)]
(aggregate {$group (merge {"_id" (if (= (count fields) 1) (first $fields)
......@@ -190,7 +195,7 @@
(into {})))}
{$sort (->> order-by
(mapcat (fn [{:keys [field direction]}]
[(:field-name field) (case direction
[(field->name field) (case direction
:ascending 1
:descending -1)]))
(apply sorted-map))}
......@@ -241,7 +246,7 @@
;; ### fields
(defclause :fields fields
`[(fields ~(mapv :field-name fields))])
`[(fields ~(mapv field->name fields))])
;; ### filter
......@@ -254,13 +259,13 @@
value))
(defn- parse-filter-subclause [{:keys [filter-type field value] :as filter}]
(let [field (when field (:field-name field))
(let [field (when field (field->name field))
value (when value (format-value value))]
(case filter-type
:inside (let [lat (:lat filter)
lon (:lon filter)]
{$and [{(:field-name (:field lat)) {$gte (format-value (:min lat)), $lte (format-value (:max lat))}}
{(:field-name (:field lon)) {$gte (format-value (:min lon)), $lte (format-value (:max lon))}}]})
{$and [{(field->name (:field lat)) {$gte (format-value (:min lat)), $lte (format-value (:max lat))}}
{(field->name (:field lon)) {$gte (format-value (:min lon)), $lte (format-value (:max lon))}}]})
:between {field {$gte (format-value (:min-val filter))
$lte (format-value (:max-val filter))}}
:is-null {field {$exists false}}
......@@ -290,7 +295,7 @@
;; ### order_by
(defclause :order-by subclauses
(let [sort-options (mapcat (fn [{:keys [field direction]}]
[(:field-name field) (case direction
[(field->name field) (case direction
:ascending 1
:descending -1)])
subclauses)]
......
......@@ -243,9 +243,9 @@
(defrecord FieldPlaceholder [^Integer field-id]
IResolve
(resolve-field [this field-id->fields]
(-> (:field-id this)
field-id->fields
map->Field)))
(->> (field-id->fields field-id)
(merge this)
map->Field)))
(defn- parse-value
"Convert the `value` of a `Value` to a date or timestamp if needed.
......@@ -276,14 +276,23 @@
If `*field-ids*` is bound, "
([field-id]
(match field-id
(field-id :guard integer?) (do (swap! *field-ids* conj field-id)
(->FieldPlaceholder field-id))
["fk->"
(fk-field-id :guard integer?)
(dest-field-id :guard integer?)] (do (assert-driver-supports :foreign-keys)
(swap! *field-ids* conj dest-field-id)
(swap! *fk-field-ids* conj fk-field-id)
(->FieldPlaceholder dest-field-id))))
(id :guard integer?)
(do (swap! *field-ids* conj id)
(->FieldPlaceholder id))
["fk->" (fk-field-id :guard integer?) (dest-field-id :guard integer?)]
(do (assert-driver-supports :foreign-keys)
(swap! *field-ids* conj dest-field-id)
(swap! *fk-field-ids* conj fk-field-id)
(->FieldPlaceholder dest-field-id))
["." (id :guard integer?) subfield]
(do (assert-driver-supports :nested-fields)
(swap! *field-ids* conj id)
(map->FieldPlaceholder {:field-id id
:subfield subfield}))
_ (throw (Exception. (str "Invalid field: " field-id)))))
([field-id value]
(->ValuePlaceholder (:field-id (ph field-id)) value)))
......
......@@ -912,3 +912,28 @@
[["fk->" &sightings.category_id:id &categories.name:id] "descending"]
[&sightings.id:id "ascending"]]
:limit 10))
;; +------------------------------------------------------------------------------------------------------------------------+
;; | MONGO NESTED-FIELD ACCESS |
;; +------------------------------------------------------------------------------------------------------------------------+
(defn y []
(datasets/with-dataset :mongo
(with-temp-db [_ (dataset-loader) defs/geographical-tips]
(sel :many Field :table_id &tips:id))))
(defn x []
(datasets/with-dataset :mongo
(query-with-temp-db defs/geographical-tips
:aggregation ["rows"]
:source_table &tips:id
:filter ["=" ["." &tips.venue:id "name"] "Kyle's Low-Carb Grill"]
:limit 10)))
(defn z []
(datasets/with-dataset :mongo
(metabase.driver.mongo.util/with-mongo-connection [^com.mongodb.DBApiLayer db (metabase.test.data/get-or-create-database! defs/geographical-tips)]
(doall (monger.query/with-collection db "tips"
(monger.query/find {:venue {:name "Kyle's Low-Carb Grill"}})
(monger.query/limit 10))))))
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment