source: Dev/trunk/src/qed-client/store/CouchStore.js @ 446

Last change on this file since 446 was 446, checked in by hendrikvanantwerpen, 12 years ago

Rewrote CouchStore? to use dojo/request.

File size: 10.9 KB
Line 
1define([
2    "dojo/_base/Deferred",
3    "dojo/_base/array",
4    "dojo/_base/declare",
5    "dojo/_base/json",
6    "dojo/_base/lang",
7    "dojo/request",
8    "dojo/store/util/QueryResults"
9], function(Deferred, array, declare, json, lang, request, QueryResults) {
10   
11    function getCouchError(err){
12        var reason = err.response &&
13                     err.response.data &&
14                     json.fromJson(err.response.data).reason;
15        return reason || "Unknown error.";
16    }
17
18    var CouchStore = declare(null, {
19        /** dojo Store implementation for CouchDB
20         *
21         * See for details on the REST API, the wiki
22         * at http://wiki.apache.org/couchdb/HTTP_Document_API.
23         */
24        target: "",
25        accepts: "application/json",
26        idProperty: "_id",
27        revProperty: "_rev",
28        _responseIdProperty: "id",
29        _responseRevProperty: "rev",
30        constructor: function(options){
31            declare.safeMixin(this, options);
32        },
33        getIdentity: function(object){
34            return object[this.idProperty];
35        },
36        getRevision: function(object){
37            return object[this.revProperty];
38        },
39        info: function(){
40            var dfd = new Deferred();
41            request(this.target, {
42                method: "GET",
43                handleAs: "json",
44                headers: {
45                    Accept: this.accepts
46                }
47            }).then(function(result){
48                if ( result.error ) {
49                    dfd.reject(result.reason);
50                } else {
51                    dfd.resolve(result);
52                }
53            }, function(err){
54                dfd.reject(getCouchError(err));
55            });
56            return dfd.promise;
57        },
58        get: function(id){
59            var dfd = new Deferred();
60            request(this.target + encodeURIComponent(id), {
61                method: "GET",
62                handleAs: "json",
63                headers: {
64                    Accept: this.accepts
65                }
66            }).then(function(result){
67                if ( result.error ) {
68                    dfd.reject(result.reason);
69                } else {
70                    result[this.idProperty] = result[this._responseIdProperty];
71                    result[this.revProperty] = result[this._responseRevProperty];
72                    dfd.resolve(result);
73                }
74            }, function(err){
75                dfd.reject(getCouchError(err));
76            });
77            return dfd.promise;
78        },
79        validate: function(object) {
80            return true;
81        },
82        put: function(object, options){
83             // summary:
84             //     put an object in CouchDB
85             // object: Object
86             //     The object to put
87             // options: Object
88             //     Options object as
89             //         id: String
90             //
91
92            if ( !this.validate(object) ) {
93                var dfd = new Deferred();
94                dfd.reject("Invalid document.");
95                return dfd.promise;
96            }
97            return this._putValid(object, options);
98
99        },
100        _putValid: function(object,options) {
101            var dfd = new Deferred();
102            options = options || {};
103            var id = options.id ? options.id : this.getIdentity(object);
104            var hasId = typeof id !== "undefined";
105            request(hasId ? this.target + encodeURIComponent(id) : this.target, {
106                method: hasId ? "PUT" : "POST",
107                data: json.toJson(object),
108                handleAs: "json",
109                headers:{
110                    "Content-Type": "application/json",
111                    Accept: this.accepts
112                }
113            }).then(lang.hitch(this,function(result){
114                if ( result.error ) {
115                    dfd.reject(result.reason);
116                } else {
117                    object[this.idProperty] = result[this._responseIdProperty];
118                    object[this.revProperty] = result[this._responseRevProperty];
119                    dfd.resolve(object);
120                }
121            }), function(err){
122                dfd.reject(getCouchError(err));
123            });
124            return dfd.promise;
125        },
126        add: function(object, options){
127            return this.put(object,options);
128        },
129        remove: function(id,rev){
130            var dfd = new Deferred();
131            request(this.target + encodeURIComponent(id), {
132                method: "DELETE",
133                headers: {
134                    'If-Match': rev
135                }
136            }).then(function(result){
137                if ( result.error ) {
138                    dfd.reject(result.reason);
139                } else {
140                    dfd.resolve();
141                }
142            },function(err){
143                dfd.reject(getCouchError(err));
144            });
145            return dfd.promise;
146        },
147        query: function(query, options){
148            // summary:
149            //    query a couchdb view
150            // query: String
151            //    name of a couchdb view you want to query, relative to the current database
152            // options: Object
153            //     options object as
154            //        start: Number
155            //            Start results at this item
156            //        count: Number
157            //            Number of items to return
158            //        sort: [{attribute:'key',descending:true|false}]
159            //            CouchDB only support sorting by key, so only 'key'
160            //            is allowed as attribute value. Multiple sort items
161            //            are ignored.
162            //        key: String|Array|Object
163            //            Return only values with this key.
164            //            Excludes start/endkey usage.
165            //        startkey: String|Array|Object
166            //            Return values starting from this key.
167            //        endkey: String|Array|Object
168            //            Return values with key lower than this key.
169            //        include_docs: true|false
170            //            Return the full documents instead of the view
171            //            values.
172            //        reduce: true|false
173            //            Execute reduce on the view or not. Default depends
174            //            on if a reduce function is defined on the view.
175            //        group: true|false
176            //            Should values be grouped per key or not? Default
177            //            is false.
178            //        group_level: Number
179            //            When group = true and the key is an array,
180            //            determines which elements starting from the first
181            //            are used for grouping. Default is 0.
182            //        get_keys: true|false
183            //            Instead of returning the values or documents,
184            //            return the array of keys as the result.
185            //            This does not affect the forPairs function.
186            options = options || {};
187
188            var dfd = new Deferred();
189            var queryOpts = {};
190            if ( !query ) {
191                query = '_all_docs';
192            }
193
194            if (!lang.isString(query)) {
195                console.warn("Query must be a view name");
196            }
197
198            // Standard options
199            if (options.start >= 0) {
200                queryOpts.skip = options.start;
201            }
202            if (options.count >= 0) {
203                queryOpts.limit = options.count;
204            }
205            if (options.sort) {
206                if (options.sort[0]) {
207                    if (options.sort[0].attribute && options.sort[0].attribute !== "key") {
208                        console.warn("Can only sort on key");
209                    }
210                    if (options.sort[0].descending) {
211                        queryOpts.descending = true;
212                    }
213                }
214                if (options.sort.length > 1) {
215                    console.warn("multiple sort fields not supported");
216                }
217            }
218
219            // Custom options
220            if (options.key !== undefined) {
221                queryOpts.key = options.key;
222            } else if (options.keys !== undefined) {
223                queryOpts.keys = options.keys;
224            } else if (options.startkey !== undefined || options.endkey !== undefined) {
225                queryOpts.startkey = options.startkey;
226                queryOpts.endkey = options.endkey;
227            }
228            if (options.include_docs !== undefined) {
229                queryOpts.include_docs = options.include_docs;
230            }
231            if (options.reduce !== undefined) {
232                queryOpts.reduce = options.reduce;
233            }
234            if (options.group !== undefined) {
235                queryOpts.group = options.group;
236                if (options.group_level !== undefined) {
237                    queryOpts.group_level = options.group_level;
238                }
239            }
240
241            for ( var opt in queryOpts ) {
242                queryOpts[opt] = json.toJson(queryOpts[opt]);
243            }
244           
245            request(this.target + query, {
246                method: "GET",
247                handleAs: "json",
248                query: queryOpts,
249                headers: {
250                    Accept: this.accepts
251                }
252            }).then(function(result){
253                if (result.error) {
254                    dfd.reject(result.reason);
255                } else  {
256                    var results;
257                    var values = array.map(result.rows,function(result){
258                        return options.include_docs === true ? result.doc : result.value;
259                    });
260                    var keys = array.map(result.rows,function(result){
261                        return result.key;
262                    });
263                    if (options.get_keys === true) {
264                        results = keys;
265                        results.values = values;
266                    } else {
267                        results = values;
268                        results.keys = keys;
269                    }
270                    dfd.resolve(results);
271                }
272            },function(err){
273                dfd.reject(getCouchError(err));
274            });
275            return couchResults(dfd.promise);
276        }
277    });
278
279    var queryResults = QueryResults;
280
281    function couchResults(results) {
282        results = queryResults(results);
283        results.forPairs = function(callback,thisObject) {
284            callback = lang.hitch(thisObject,callback);
285            return Deferred.when(results,function(results) {
286                var values = results.values || results;
287                var keys = results.keys || results;
288                return array.forEach(values, function(value,index) {
289                    callback(value,keys[index],index);
290                });
291            });
292        };
293        return results;
294    }
295
296    return CouchStore;
297
298});
Note: See TracBrowser for help on using the repository browser.