dives into publish_result
[myslice.git] / manifold / js / manifold.js
index 94e4469..726e68f 100644 (file)
@@ -17,6 +17,16 @@ function debug_query (msg, query) {
     else messages.debug ("debug_query: " + msg + " query= " + query);
 }
 
+// http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/
+Object.toType = (function toType(global) {
+  return function(obj) {
+    if (obj === global) {
+      return "global";
+    }
+    return ({}).toString.call(obj).match(/\s([a-z|A-Z]+)/)[1].toLowerCase();
+  }
+})(this);
+
 /* ------------------------------------------------------------ */
 
 // Constants that should be somehow moved to a plugin.js file
@@ -41,11 +51,12 @@ var SET_REMOVED    = 202;
 var FIELD_REQUEST_CHANGE  = 301;
 var FIELD_REQUEST_ADD     = 302;
 var FIELD_REQUEST_REMOVE  = 303;
-var FIELD_REQUEST_RESET   = 304;
+var FIELD_REQUEST_ADD_RESET = 304;
+var FIELD_REQUEST_REMOVE_RESET = 305;
 // status
-var FIELD_REQUEST_PENDING = 301;
-var FIELD_REQUEST_SUCCESS = 302;
-var FIELD_REQUEST_FAILURE = 303;
+var FIELD_REQUEST_PENDING = 401;
+var FIELD_REQUEST_SUCCESS = 402;
+var FIELD_REQUEST_FAILURE = 403;
 
 /* Query status */
 var STATUS_NONE               = 500; // Query has not been started yet
@@ -60,20 +71,26 @@ var STATUS_UPDATE_ERROR       = 507;
 /* Requests for query cycle */
 var RUN_UPDATE     = 601;
 
-// A structure for storing queries
+/* MANIFOLD types */
+var TYPE_VALUE  = 1;
+var TYPE_RECORD = 2;
+var TYPE_LIST_OF_VALUES = 3;
+var TYPE_LIST_OF_RECORDS = 4;
 
 
+// A structure for storing queries
 
 function QueryExt(query, parent_query_ext, main_query_ext, update_query_ext, disabled) {
 
     /* Constructor */
     if (typeof query == "undefined")
         throw "Must pass a query in QueryExt constructor";
-    this.query            = query
-    this.parent_query_ext = (typeof parent_query_ext == "undefined") ? null : parent_query_ext
-    this.main_query_ext   = (typeof main_query_ext   == "undefined") ? null : main_query_ext
-    this.update_query_ext = (typeof update_query_ext   == "undefined") ? null : update_query_ext
-    this.disabled         = (typeof update_query_ext   == "undefined") ? false : disabled
+    this.query                 = query;
+    this.parent_query_ext      = (typeof parent_query_ext      == "undefined") ? null  : parent_query_ext;
+    this.main_query_ext        = (typeof main_query_ext        == "undefined") ? null  : main_query_ext;
+    this.update_query_ext      = (typeof update_query_ext      == "undefined") ? null  : update_query_ext;
+    this.update_query_orig_ext = (typeof update_query_orig_ext == "undefined") ? null  : update_query_orig_ext;
+    this.disabled              = (typeof update_query_ext      == "undefined") ? false : disabled;
     
     this.status       = null;
     this.results      = null;
@@ -87,10 +104,9 @@ function QueryStore() {
 
     /* Insertion */
 
-    this.insert = function(query)
-    {
+    this.insert = function(query) {
         // We expect only main_queries are inserted
-
+       
         /* If the query has not been analyzed, then we analyze it */
         if (query.analyzed_query == null) {
             query.analyze_subqueries();
@@ -105,10 +121,13 @@ function QueryStore() {
         update_query.analyzed_query.action = 'update';
         update_query.params = {};
         update_query_ext = new QueryExt(update_query);
-        console.log("Update query created from Get query", update_query);
+
+        /* We remember the original query to be able to reset it */
+        update_query_orig_ext = new QueryExt(update_query.clone());
+
 
         /* We store the main query */
-        query_ext = new QueryExt(query, null, null, update_query_ext, false);
+        query_ext = new QueryExt(query, null, null, update_query_ext, update_query_orig_ext, false);
         manifold.query_store.main_queries[query.query_uuid] = query_ext;
         /* Note: the update query does not have an entry! */
 
@@ -138,23 +157,19 @@ function QueryStore() {
 
     /* Searching */
 
-    this.find_query_ext = function(query_uuid)
-    {
+    this.find_query_ext = function(query_uuid) {
         return this.main_queries[query_uuid];
     }
 
-    this.find_query = function(query_uuid)
-    {
+    this.find_query = function(query_uuid) {
         return this.find_query_ext(query_uuid).query;
     }
 
-    this.find_analyzed_query_ext = function(query_uuid)
-    {
+    this.find_analyzed_query_ext = function(query_uuid) {
         return this.analyzed_queries[query_uuid];
     }
 
-    this.find_analyzed_query = function(query_uuid)
-    {
+    this.find_analyzed_query = function(query_uuid) {
         return this.find_analyzed_query_ext(query_uuid).query;
     }
 }
@@ -184,20 +199,33 @@ var manifold = {
         } catch (err) { messages.debug("Cannot turn spins on/off " + err); }
     },
 
+    get_type: function(variable) {
+        switch(Object.toType(variable)) {
+            case 'number':
+            case 'string':
+                return TYPE_VALUE;
+            case 'object':
+                return TYPE_RECORD;
+            case 'array':
+                if ((variable.length > 0) && (Object.toType(variable[0]) === 'object'))
+                    return TYPE_LIST_OF_RECORDS;
+                else
+                    return TYPE_LIST_OF_VALUES;
+        }
+    },
+
     /************************************************************************** 
      * Metadata management
      **************************************************************************/ 
 
      metadata: {
 
-        get_table: function(method)
-        {
+        get_table: function(method) {
             var table = MANIFOLD_METADATA[method];
             return (typeof table === 'undefined') ? null : table;
         },
 
-        get_columns: function(method)
-        {
+        get_columns: function(method) {
             var table = this.get_table(method);
             if (!table) {
                 return null;
@@ -206,8 +234,7 @@ var manifold = {
             return (typeof table.column === 'undefined') ? null : table.column;
         },
 
-        get_key: function(method)
-        {
+        get_key: function(method) {
             var table = this.get_table(method);
             if (!table)
                 return null;
@@ -216,8 +243,7 @@ var manifold = {
         },
 
 
-        get_column: function(method, name)
-        {
+        get_column: function(method, name) {
             var columns = this.get_columns(method);
             if (!columns)
                 return null;
@@ -229,8 +255,7 @@ var manifold = {
             return null;
         },
 
-        get_type: function(method, name)
-        {
+        get_type: function(method, name) {
             var table = this.get_table(method);
             if (!table)
                 return null;
@@ -289,21 +314,22 @@ var manifold = {
     // trigger a query asynchroneously
     proxy_url : '/manifold/proxy/json/',
 
+    // reasonably low-noise, shows manifold requests coming in and out
     asynchroneous_debug : true,
+    // print our more details on result publication and related callbacks
+    publish_result_debug : false,
 
     /**
      * \brief We use js function closure to be able to pass the query (array)
      * to the callback function used when data is received
      */
-    success_closure: function(query, publish_uuid, callback /*domid*/)
-    {
+    success_closure: function(query, publish_uuid, callback /*domid*/) {
         return function(data, textStatus) {
             manifold.asynchroneous_success(data, query, publish_uuid, callback /*domid*/);
         }
     },
 
-    run_query: function(query, callback)
-    {
+    run_query: function(query, callback) {
         // default value for callback = null
         if (typeof callback === 'undefined')
             callback = null; 
@@ -348,7 +374,8 @@ var manifold = {
 
             // not quite sure what happens if we send a string directly, as POST data is named..
             // this gets reconstructed on the proxy side with ManifoldQuery.fill_from_POST
-                jQuery.post(manifold.proxy_url, {'json':query_json} , manifold.success_closure(query, publish_uuid, tuple.callback /*domid*/));
+            jQuery.post(manifold.proxy_url, {'json':query_json}, 
+                       manifold.success_closure(query, publish_uuid, tuple.callback /*domid*/));
         })
     },
 
@@ -361,7 +388,8 @@ var manifold = {
      */
     forward: function(query, callback /*domid*/) {
         var query_json = JSON.stringify(query);
-        $.post(manifold.proxy_url, {'json': query_json} , manifold.success_closure(query, query.query_uuid, callback/*domid*/));
+        $.post(manifold.proxy_url, {'json': query_json} , 
+              manifold.success_closure(query, query.query_uuid, callback/*domid*/));
     },
 
     /*!
@@ -391,17 +419,22 @@ var manifold = {
 
         // NEW PLUGIN API
         manifold.raise_record_event(query.query_uuid, CLEAR_RECORDS);
+        if (manifold.publish_result_debug) messages.debug(".. publish_result (1) ");
+       var count=0;
         $.each(result, function(i, record) {
             manifold.raise_record_event(query.query_uuid, NEW_RECORD, record);
+           count += 1;
         });
+        if (manifold.publish_result_debug) messages.debug(".. publish_result NEW API (2) count=" + count);
         manifold.raise_record_event(query.query_uuid, DONE);
 
         // OLD PLUGIN API BELOW
         /* Publish an update announce */
         var channel="/results/" + query.query_uuid + "/changed";
-        if (manifold.asynchroneous_debug)
-            messages.debug("publishing result on " + channel);
+        if (manifold.publish_result_debug) messages.debug(".. publish_result OLD API (3) " + channel);
         jQuery.publish(channel, [result, query]);
+
+       if (manifold.publish_result_debug) messages.debug(".. publish_result - END (4) q=" + query.__repr());
     },
 
     /*!
@@ -416,6 +449,7 @@ var manifold = {
          * otherwise, publish the main object as well as subqueries
          * XXX how much recursive are we ?
          */
+       if (manifold.publish_result_debug) messages.debug (">>>>> publish_result_rec " + query.object);
         if (manifold.query_expects_unique_result(query)) {
             /* Also publish subqueries */
             jQuery.each(query.subqueries, function(object, subquery) {
@@ -423,7 +457,241 @@ var manifold = {
                 /* TODO remove object from result */
             });
         }
+       if (manifold.publish_result_debug) messages.debug ("===== publish_result_rec " + query.object);
         manifold.publish_result(query, result);
+       if (manifold.publish_result_debug) messages.debug ("<<<<< publish_result_rec " + query.object);
+    },
+
+    setup_update_query: function(query, records) {
+        // We don't prepare an update query if the result has more than 1 entry
+        if (records.length != 1)
+            return;
+        var query_ext = manifold.query_store.find_query_ext(query.query_uuid);
+
+        var record = records[0];
+
+        var update_query_ext = query_ext.update_query_ext;
+        var update_query = update_query_ext.query;
+        var update_query_ext = query_ext.update_query_ext;
+        var update_query_orig = query_ext.update_query_orig_ext.query;
+
+        // Testing whether the result has subqueries (one level deep only)
+        // iif the query has subqueries
+        var count = 0;
+        var obj = query.analyzed_query.subqueries;
+        for (method in obj) {
+            if (obj.hasOwnProperty(method)) {
+                var key = manifold.metadata.get_key(method);
+                if (!key)
+                    continue;
+                if (key.length > 1)
+                    continue;
+                key = key[0];
+                var sq_keys = [];
+                var subrecords = record[method];
+                if (!subrecords)
+                    continue
+                $.each(subrecords, function (i, subrecord) {
+                    sq_keys.push(subrecord[key]);
+                });
+                update_query.params[method] = sq_keys;
+                update_query_orig.params[method] = sq_keys.slice();
+                count++;
+            }
+        }
+
+        if (count > 0) {
+            update_query_ext.disabled = false;
+            update_query_orig_ext.disabled = false;
+        }
+    },
+
+    process_get_query_records: function(query, records) {
+        this.setup_update_query(query, records);
+
+        /* Publish full results */
+        tmp_query = manifold.find_query(query.query_uuid);
+        manifold.publish_result_rec(tmp_query.analyzed_query, records);
+    },
+
+    /**
+     * 
+     * What we need to do when receiving results from an update query:
+     * - differences between what we had, what we requested, and what we obtained
+     *    . what we had : update_query_orig (simple fields and set fields managed differently)
+     *    . what we requested : update_query
+     *    . what we received : records
+     * - raise appropriate events
+     *
+     * The normal process is that results similar to Get will be pushed in the
+     * pubsub mechanism, thus repopulating everything while we only need
+     * diff's. This means we need to move the publish functionalities in the
+     * previous 'process_get_query_records' function.
+     */
+    process_update_query_records: function(query, records) {
+        // First issue: we request everything, and not only what we modify, so will will have to ignore some fields
+        var query_uuid        = query.query_uuid;
+        var query_ext         = manifold.query_store.find_analyzed_query_ext(query_uuid);
+        var update_query      = query_ext.main_query_ext.update_query_ext.query;
+        var update_query_orig = query_ext.main_query_ext.update_query_orig_ext.query;
+        
+        // Since we update objects one at a time, we can get the first record
+        var record = records[0];
+
+        // Let's iterate over the object properties
+        for (var field in record) {
+            switch (this.get_type(record[field])) {
+                case TYPE_VALUE:
+                    // Did we ask for a change ?
+                    var update_value = update_query[field];
+                    if (!update_value)
+                        // Not requested, if it has changed: OUT OF SYNC
+                        // How we can know ?
+                        // We assume it won't have changed
+                        continue;
+
+                    var result_value = record[field];
+                    if (!result_value)
+                        throw "Internal error";
+
+                    data = {
+                        request: FIELD_REQUEST_CHANGE,
+                        key   : field,
+                        value : update_value,
+                        status: (update_value == result_value) ? FIELD_REQUEST_SUCCESS : FIELD_REQUEST_FAILURE,
+                    }
+                    manifold.raise_record_event(query_uuid, FIELD_STATE_CHANGED, data);
+
+                    break;
+                case TYPE_RECORD:
+                    throw "Not implemented";
+                    break;
+
+                case TYPE_LIST_OF_VALUES:
+                    // Same as list of records, but we don't have to extract keys
+                    var result_keys  = record[field]
+                    
+                    // The rest of exactly the same (XXX factorize)
+                    var update_keys  = update_query_orig.params[field];
+                    var query_keys   = update_query.params[field];
+                    var added_keys   = $.grep(query_keys, function (x) { return $.inArray(x, update_keys) == -1 });
+                    var removed_keys = $.grep(update_keys, function (x) { return $.inArray(x, query_keys) == -1 });
+
+
+                    $.each(added_keys, function(i, key) {
+                        if ($.inArray(key, result_keys) == -1) {
+                            data = {
+                                request: FIELD_REQUEST_ADD,
+                                key   : field,
+                                value : key,
+                                status: FIELD_REQUEST_FAILURE,
+                            }
+                        } else {
+                            data = {
+                                request: FIELD_REQUEST_ADD,
+                                key   : field,
+                                value : key,
+                                status: FIELD_REQUEST_SUCCESS,
+                            }
+                        }
+                        manifold.raise_record_event(query_uuid, FIELD_STATE_CHANGED, data);
+                    });
+                    $.each(removed_keys, function(i, key) {
+                        if ($.inArray(key, result_keys) == -1) {
+                            data = {
+                                request: FIELD_REQUEST_REMOVE,
+                                key   : field,
+                                value : key,
+                                status: FIELD_REQUEST_SUCCESS,
+                            }
+                        } else {
+                            data = {
+                                request: FIELD_REQUEST_REMOVE,
+                                key   : field,
+                                value : key,
+                                status: FIELD_REQUEST_FAILURE,
+                            }
+                        }
+                        manifold.raise_record_event(query_uuid, FIELD_STATE_CHANGED, data);
+                    });
+
+
+                    break;
+                case TYPE_LIST_OF_RECORDS:
+                    // example: slice.resource
+                    //  - update_query_orig.params.resource = resources in slice before update
+                    //  - update_query.params.resource = resource requested in slice
+                    //  - keys from field = resources obtained
+                    var key = manifold.metadata.get_key(field);
+                    if (!key)
+                        continue;
+                    if (key.length > 1) {
+                        throw "Not implemented";
+                        continue;
+                    }
+                    key = key[0];
+
+                    /* XXX should be modified for multiple keys */
+                    var result_keys  = $.map(record[field], function(x) { return x[key]; });
+
+                    var update_keys  = update_query_orig.params[field];
+                    var query_keys   = update_query.params[field];
+                    var added_keys   = $.grep(query_keys, function (x) { return $.inArray(x, update_keys) == -1 });
+                    var removed_keys = $.grep(update_keys, function (x) { return $.inArray(x, query_keys) == -1 });
+
+
+                    $.each(added_keys, function(i, key) {
+                        if ($.inArray(key, result_keys) == -1) {
+                            data = {
+                                request: FIELD_REQUEST_ADD,
+                                key   : field,
+                                value : key,
+                                status: FIELD_REQUEST_FAILURE,
+                            }
+                        } else {
+                            data = {
+                                request: FIELD_REQUEST_ADD,
+                                key   : field,
+                                value : key,
+                                status: FIELD_REQUEST_SUCCESS,
+                            }
+                        }
+                        manifold.raise_record_event(query_uuid, FIELD_STATE_CHANGED, data);
+                    });
+                    $.each(removed_keys, function(i, key) {
+                        if ($.inArray(key, result_keys) == -1) {
+                            data = {
+                                request: FIELD_REQUEST_REMOVE,
+                                key   : field,
+                                value : key,
+                                status: FIELD_REQUEST_SUCCESS,
+                            }
+                        } else {
+                            data = {
+                                request: FIELD_REQUEST_REMOVE,
+                                key   : field,
+                                value : key,
+                                status: FIELD_REQUEST_FAILURE,
+                            }
+                        }
+                        manifold.raise_record_event(query_uuid, FIELD_STATE_CHANGED, data);
+                    });
+
+
+                    break;
+            }
+        }
+        
+        // XXX Now we need to adapt 'update' and 'update_orig' queries as if we had done a get
+        this.setup_update_query(query, records);
+    },
+
+    process_query_records: function(query, records) {
+        if (query.action == 'get') {
+            this.process_get_query_records(query, records);
+        } else if (query.action == 'update') {
+            this.process_update_query_records(query, records);
+        }
     },
 
     // if set domid allows the result to be directed to just one plugin
@@ -432,14 +700,29 @@ var manifold = {
     // e.g. an updater wants to publish its result as if from the original (get) query
     asynchroneous_success : function (data, query, publish_uuid, callback /*domid*/) {
         // xxx should have a nicer declaration of that enum in sync with the python code somehow
+       
+       var start = new Date();
+       if (manifold.asynchroneous_debug)
+           messages.debug(">>>>>>>>>> asynchroneous_success query.object=" + query.object);
 
         /* If a callback has been specified, we redirect results to it */
-        if (!!callback) { callback(data); return; }
+        if (!!callback) { 
+           callback(data); 
+           if (manifold.asynchroneous_debug) {
+               duration=new Date()-start;
+               messages.debug ("<<<<<<<<<< asynchroneous_success " + query.object + " -- callback ended " + duration + " ms");
+           }
+           return; 
+       }
 
         if (data.code == 2) { // ERROR
             // We need to make sense of error codes here
             alert("Your session has expired, please log in again");
             window.location="/logout/";
+           if (manifold.asynchroneous_debug) {
+               duration=new Date()-start;
+               messages.debug ("<<<<<<<<<< asynchroneous_success " + query.object + " -- error returned - logging out " + duration + " ms");
+           }
             return;
         }
         if (data.code == 1) { // WARNING
@@ -457,112 +740,55 @@ var manifold = {
             });
             
         }
+       if (manifold.asynchroneous_debug) 
+           messages.debug ("========== asynchroneous_success " + query.object + " -- before process_query_records");
+
         // once everything is checked we can use the 'value' part of the manifoldresult
         var result=data.value;
         if (result) {
-            //if (!!callback /* domid */) {
-            //    /* Directly inform the requestor */
-            //    if (manifold.asynchroneous_debug) messages.debug("directing result to callback");
-            //    callback(result);
-            //    //if (manifold.asynchroneous_debug) messages.debug("directing result to " + domid);
-            //    //jQuery('#' + domid).trigger('results', [result]);
-            //} else {
-                /* XXX Jordan XXX I don't need publish_uuid here... What is it used for ? */
-                /* query is the query we sent to the backend; we need to find the
-                 * corresponding analyezd_query in manifold.all_queries
-                 */
-
-                // XXX We might need to update the corresponding update_query here 
-                query_ext = manifold.query_store.find_query_ext(query.query_uuid);
-                query = query_ext.query;
-
-                // We don't prepare an update query if the result has more than 1 entry
-                if (result.length == 1) {
-                    var res = result[0];
-
-                    console.log("Locating update query for updating params", update_query);
-                    update_query_ext = query_ext.update_query_ext;
-                    update_query = update_query_ext.query;
-
-                    // Testing whether the result has subqueries (one level deep only)
-                    // iif the query has subqueries
-                    var count = 0;
-                    var obj = query.analyzed_query.subqueries;
-                    for (method in obj) {
-                        if (obj.hasOwnProperty(method)) {
-                            var key = manifold.metadata.get_key(method);
-                            if (!key)
-                                continue;
-                            if (key.length > 1)
-                                continue;
-                            key = key[0];
-                            var sq_keys = [];
-                            var records = res[method];
-                            if (!records)
-                                continue
-                            $.each(records, function (i, obj) {
-                                sq_keys.push(obj[key]);
-                            });
-                            update_query.params[method] = sq_keys;
-                            count++;
-                        }
-                    }
-
-                    if (count > 0) {
-                        update_query_ext.disabled = false;
-                    }
-
-                }
-
-
-                
-                // We have results from the main query
-                // inspect subqueries and get the key for each
-
-                // XXX note that we might need the name for each relation, but
-                // this might be for SET_ADD, since we need to recursively find
-                // the path from the main query
-
-
-                tmp_query = manifold.find_query(query.query_uuid);
-                manifold.publish_result_rec(tmp_query.analyzed_query, result);
-            //}
+            /* Eventually update the content of related queries (update, etc) */
+            this.process_query_records(query, result);
 
+            /* Publish results: disabled here, done in the previous call */
+            //tmp_query = manifold.find_query(query.query_uuid);
+            //manifold.publish_result_rec(tmp_query.analyzed_query, result);
         }
+       if (manifold.asynchroneous_debug) {
+           duration=new Date()-start;
+           messages.debug ("<<<<<<<<<< asynchroneous_success " + query.object + " -- done " + duration + " ms");
+       }
+
     },
 
     /************************************************************************** 
      * Plugin API helpers
      **************************************************************************/ 
 
-    raise_event_handler: function(type, query_uuid, event_type, value)
-    {
+    raise_event_handler: function(type, query_uuid, event_type, value) {
         if ((type != 'query') && (type != 'record'))
             throw 'Incorrect type for manifold.raise_event()';
 
         var channels = [ manifold.get_channel(type, query_uuid), manifold.get_channel(type, '*') ];
 
         $.each(channels, function(i, channel) {
-            if (value === undefined)
+            if (value === undefined) {
                 $('.plugin').trigger(channel, [event_type]);
-            else
+           } else {
                 $('.plugin').trigger(channel, [event_type, value]);
+           }
         });
     },
 
-    raise_query_event: function(query_uuid, event_type, value)
-    {
+    raise_query_event: function(query_uuid, event_type, value) {
         manifold.raise_event_handler('query', query_uuid, event_type, value);
     },
 
-    raise_record_event: function(query_uuid, event_type, value)
-    {
+    raise_record_event: function(query_uuid, event_type, value) {
         manifold.raise_event_handler('record', query_uuid, event_type, value);
     },
 
 
-    raise_event: function(query_uuid, event_type, value)
-    {
+    raise_event: function(query_uuid, event_type, value) {
         // Query uuid has been updated with the key of a new element
         query_ext    = manifold.query_store.find_analyzed_query_ext(query_uuid);
         query = query_ext.query;
@@ -577,14 +803,54 @@ var manifold = {
                 // 1. Update internal query store about the change in status
 
                 // 2. Update the update query
-                update_query = query_ext.main_query_ext.update_query_ext.query;
-                update_query.params[value.key].push(value.value);
+                update_query      = query_ext.main_query_ext.update_query_ext.query;
+                update_query_orig = query_ext.main_query_ext.update_query_orig_ext.query;
+
+                switch(value.request) {
+                    case FIELD_REQUEST_CHANGE:
+                        update_query.params[value.key] = value.value;
+                        break;
+                    case FIELD_REQUEST_ADD:
+                        if ($.inArray(value.value, update_query_orig.params[value.key]) != -1)
+                            value.request = FIELD_REQUEST_ADD_RESET;
+                        update_query.params[value.key].push(value.value);
+                        break;
+                    case FIELD_REQUEST_REMOVE:
+                        if ($.inArray(value.value, update_query_orig.params[value.key]) == -1)
+                            value.request = FIELD_REQUEST_REMOVE_RESET;
+
+                        var arr = update_query.params[value.key];
+                        arr = $.grep(arr, function(x) { return x != value.value; });
+                        update_query.params[value.key] = arr;
+
+                        break;
+                    case FIELD_REQUEST_ADD_RESET:
+                    case FIELD_REQUEST_REMOVE_RESET:
+                        // XXX We would need to keep track of the original query
+                        throw "Not implemented";
+                        break;
+                }
 
                 // 3. Inform others about the change
                 // a) the main query...
                 manifold.raise_record_event(query_uuid, event_type, value);
 
                 // b) subqueries eventually (dot in the key)
+                // Let's unfold 
+                var path_array = value.key.split('.');
+                var value_key = value.key.split('.');
+
+                var cur_query = query;
+                if (cur_query.analyzed_query)
+                    cur_query = cur_query.analyzed_query;
+                $.each(path_array, function(i, method) {
+                    cur_query = cur_query.subqueries[method];
+                    value_key.shift(); // XXX check that method is indeed shifted
+                });
+                value.key = value_key;
+
+                manifold.raise_record_event(cur_query.query_uuid, event_type, value);
+
                 // XXX make this DOT a global variable... could be '/'
                 break;
 
@@ -626,7 +892,8 @@ var manifold = {
                 // NOTE : we have to modify all child queries
                 // NOTE : parts of a query might not be started (eg slice.measurements, how to handle ?)
 
-                // if everything is done right, update_query should not be null. It is updated when we received results from the get query
+                // if everything is done right, update_query should not be null. 
+               // It is updated when we received results from the get query
                 // object = the same as get
                 // filter = key : update a single object for now
                 // fields = the same as get
@@ -684,8 +951,7 @@ var manifold = {
     },
 
     /* Publish/subscribe channels for internal use */
-    get_channel: function(type, query_uuid) 
-    {
+    get_channel: function(type, query_uuid) {
         if ((type !== 'query') && (type != 'record'))
             return null;
         return '/' + type + '/' + query_uuid;
@@ -772,4 +1038,3 @@ $.ajaxSetup({
          }
      } 
 });
-