Construct paths

cMQL provides document operators

  • and the nested versions of them like get-in,assoc-in etc
  • they work in both arrays and documents nested
  • they costruct paths
    • constants
    • variables in paths
    • conditions in paths
      • member condtion
        for example (get doc $$key) if $$key+$$avar=5
      • parent condition
        for example {:name smith cars [...]} get the cars if name=smith

MongoDB paths

  • stage operators (addfields,unset,project) and . for the path path is very limited not mixed,no variables,no conditions this means that its only for top level,or nested documents where all parents are documents for example i can do (addFields {:mydoc1.mydoc2.h 5}) but its very limited (addFields {:mydoc.2.$$x.h 5}) doesnt work

  • aggregate operators using those we can do them but its hard and requires lot of code with $map,$mergeObjects,$ObjectToArray,$ArrayToObject etc

  • For example

    {:myarray [1 {:a "b" :c {:d [1 2 3]}}]}

    how to update [1 :c :d 1] to be 10 ?
    its hard even to get there,and even harder to re-construct :myarray back

cMQL path operators

  1. get/get-in get and contains
  2. assoc/assoc-in to add/update
  3. dissoc/dissoc-in to remove

For example in the above

Get it

(get-in [1 "c" "d" 1])

Update it

(assoc-in [1 "c" "d" 1] 10)

Remove it

(dissoc-in [1 "c" "d" 1])

Conditions

Many times we don't know the exact index or key,so we give a filter(condition)

Types of conditions
icond condition of the index
kcond condition for the key
cond condition for the parent
Predefined variables
I use those when i define the conditions
:a. array parent
:o. object parent
:k. key for objects only
:v. value for objects/arrays

Get with condition

Same as above with simple equality or type conditions
{:myarray [1 {:a "b" :c {:d [1 2 3]}}]}
(get-in :myarray
[
{
:icond (= :v. {:a "b" :c {:d [1 2 3]}})
}
;;i am now in {:a "b" :c {:d [1 2 3]}}
{
;;key "c" is ok only if its value is an object
:kcond (and (= :k. "c")
(object? :v.))
}
;;we are at {:d [1 2 3]} now
"d"
;;we are at [1 2 3]
1])
Returns 2 again like above

Same as the above but with parent and more conditions

{:myarray [1 {:a "b" :c {:d [1 2 3]}}]}
(get-in- :myarray
[
{
;;parent cond,second member to be object => parent pass
:cond (object? (get :a. 1))
;;index cond,to be object(1 dont pass,{:a ...} passes)
:icond (object? :v.)
}
;;i am now in {:a "b" :c {:d [1 2 3]}}
{
;;parent cond,has "c" key with value object => parent passes
:cond (object? (get :o. "c"))
;; :c key passes, its not equal with "a" and doesn't have value "b"
:kcond (and (not= :k. "a")
(not= :v. "b"))
}
;;we are at {:d [1 2 3]} now
"d"
;;we are at [1 2 3]
1])
Returns 2 again like above

With get-in i can easily get into nested structures even if mixed and even if i doesn't know what i am looking for exactly

Global conditions allows to filter the parent And :k- :v- allows to filter the key/value or the member of the array to select where i will go.

Update with condition

assoc-in works exactly the same just takes one more argument the value to update If update is successful i get an array/document depending of the update else i get nil to know that it failed

This is useful when i don't know if a field exists for example

{:myarray [1 {:a "b" :c {:d [1 2 3]}}]}
Withous conditions
(assoc-in :myarray [1 "c" "d" 1] 20)
;; returns [1 {:a "b" :c {:d [1 20 3]}}]
(assoc-in :myarray
[
{
;;parent cond,second member to be object => parent pass
:cond (object? (get :a. 1))
;;index cond,to be object(1 dont pass,{:a ...} passes)
:icond (object? :v.)
}
;;i am now in {:a "b" :c {:d [1 2 3]}}
{
;;parent cond,has "c" key with value object => parent passes
:cond (object? (get :o. "c"))
;; :c key passes, its not equal with "a" and doesn't have value "b"
:kcond (and (not= :k. "a")
(not= :v. "b"))
}
;;we are at {:d [1 2 3]} now
"d"
;;we are at [1 2 3]
1]
20)
;; returns [1 {:a "b" :c {:d [1 20 3]}}]

Performance

if key-cond or index-cond they are slow (both in MQL handmade or cMQL assoc/get) (key-cond requires $objectToArray and then search)

get

  • if constants its fast (the common use case)
  • if variables fast only for arrays

*perfomance like handmade MQL

assoc

  • if objects fast ($mergeObjects) (the common use case)
  • if arrays slow (if add in the end 1 concat,else split and concat )

*slower than handmade MQL,if >million documents and deep nested like 5 levels test perfomance(we send bigger queries because MQL doenst have functions)
for smaller collections its safe similar to handmade like 1x-1.5x

dissoc

  • slow for both arrays and objects
  • objects $objectToArray filter and back $ArrayToObject
  • arrays (if the last one 1 slice,else 2 slices and concat)

Conclusion

  • fast means constant cost independent of array size
  • slow matters only for big arrays/objects
  • index-cond and key-cond save us from writing complicated code