PageRenderTime 50ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/share/www/script/jquery.couch.js

http://github.com/apache/couchdb
JavaScript | 1079 lines | 635 code | 50 blank | 394 comment | 121 complexity | 9cc59cdfbc7f922ac750e3b183aa3339 MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0
  1. // Licensed under the Apache License, Version 2.0 (the "License"); you may not
  2. // use this file except in compliance with the License. You may obtain a copy of
  3. // the License at
  4. //
  5. // http://www.apache.org/licenses/LICENSE-2.0
  6. //
  7. // Unless required by applicable law or agreed to in writing, software
  8. // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  9. // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  10. // License for the specific language governing permissions and limitations under
  11. // the License.
  12. /**
  13. * @namespace
  14. * $.couch is used to communicate with a CouchDB server, the server methods can
  15. * be called directly without creating an instance. Typically all methods are
  16. * passed an <code>options</code> object which defines a success callback which
  17. * is called with the data returned from the http request to CouchDB, you can
  18. * find the other settings that can be used in the <code>options</code> object
  19. * from <a href="http://api.jquery.com/jQuery.ajax/#jQuery-ajax-settings">
  20. * jQuery.ajax settings</a>
  21. * <pre><code>$.couch.activeTasks({
  22. * success: function (data) {
  23. * console.log(data);
  24. * }
  25. * });</code></pre>
  26. * Outputs (for example):
  27. * <pre><code>[
  28. * {
  29. * "pid" : "<0.11599.0>",
  30. * "status" : "Copied 0 of 18369 changes (0%)",
  31. * "task" : "recipes",
  32. * "type" : "Database Compaction"
  33. * }
  34. *]</code></pre>
  35. */
  36. (function($) {
  37. $.couch = $.couch || {};
  38. /** @lends $.couch */
  39. /**
  40. * @private
  41. */
  42. function encodeDocId(docID) {
  43. var parts = docID.split("/");
  44. if (parts[0] == "_design") {
  45. parts.shift();
  46. return "_design/" + encodeURIComponent(parts.join('/'));
  47. }
  48. return encodeURIComponent(docID);
  49. }
  50. /**
  51. * @private
  52. */
  53. var uuidCache = [];
  54. $.extend($.couch, {
  55. urlPrefix: '',
  56. /**
  57. * You can obtain a list of active tasks by using the /_active_tasks URL.
  58. * The result is a JSON array of the currently running tasks, with each task
  59. * being described with a single object.
  60. * @see <a href="http://techzone.couchbase.com/sites/default/files/uploads/
  61. * all/documentation/couchbase-api-misc.html#couchbase-api-misc_active-task
  62. * s_get">docs for /_active_tasks</a>
  63. * @param {ajaxSettings} options <a href="http://api.jquery.com/jQuery.ajax
  64. * /#jQuery-ajax-settings">jQuery ajax settings</a>
  65. */
  66. activeTasks: function(options) {
  67. ajax(
  68. {url: this.urlPrefix + "/_active_tasks"},
  69. options,
  70. "Active task status could not be retrieved"
  71. );
  72. },
  73. /**
  74. * Returns a list of all the databases in the CouchDB instance
  75. * @see <a href="http://techzone.couchbase.com/sites/default/files/uploads/
  76. * all/documentation/couchbase-api-misc.html#couchbase-api-misc_active-task
  77. * s_get">docs for /_all_dbs</a>
  78. * @param {ajaxSettings} options <a href="http://api.jquery.com/jQuery.ajax
  79. * /#jQuery-ajax-settings">jQuery ajax settings</a>
  80. */
  81. allDbs: function(options) {
  82. ajax(
  83. {url: this.urlPrefix + "/_all_dbs"},
  84. options,
  85. "An error occurred retrieving the list of all databases"
  86. );
  87. },
  88. /**
  89. * View and edit the CouchDB configuration, called with just the options
  90. * parameter the entire config is returned, you can be more specific by
  91. * passing the section and option parameters, if you specify a value that
  92. * value will be stored in the configuration.
  93. * @see <a href="http://techzone.couchbase.com/sites/default/files/uploads/
  94. * all/documentation/couchbase-api-config.html#couchbase-api-config_config
  95. * -section-key_put">docs for /_config</a>
  96. * @param {ajaxSettings} options
  97. * <a href="http://api.jquery.com/jQuery.ajax/#jQuery-ajax-settings">
  98. * jQuery ajax settings</a>
  99. * @param {String} [section] the section of the config
  100. * @param {String} [option] the particular config option
  101. * @param {String} [value] value to be set
  102. */
  103. config: function(options, section, option, value) {
  104. var req = {url: this.urlPrefix + "/_config/"};
  105. if (section) {
  106. req.url += encodeURIComponent(section) + "/";
  107. if (option) {
  108. req.url += encodeURIComponent(option);
  109. }
  110. }
  111. if (value === null) {
  112. req.type = "DELETE";
  113. } else if (value !== undefined) {
  114. req.type = "PUT";
  115. req.data = toJSON(value);
  116. req.contentType = "application/json";
  117. req.processData = false
  118. }
  119. ajax(req, options,
  120. "An error occurred retrieving/updating the server configuration"
  121. );
  122. },
  123. /**
  124. * Returns the session information for the currently logged in user.
  125. * @param {ajaxSettings} options
  126. * <a href="http://api.jquery.com/jQuery.ajax/#jQuery-ajax-settings">
  127. * jQuery ajax settings</a>
  128. */
  129. session: function(options) {
  130. options = options || {};
  131. ajax({
  132. type: "GET", url: this.urlPrefix + "/_session",
  133. beforeSend: function(xhr) {
  134. xhr.setRequestHeader('Accept', 'application/json');
  135. },
  136. complete: function(req) {
  137. var resp = $.parseJSON(req.responseText);
  138. if (req.status == 200) {
  139. if (options.success) options.success(resp);
  140. } else if (options.error) {
  141. options.error(req.status, resp.error, resp.reason);
  142. } else {
  143. throw "An error occurred getting session info: " + resp.reason;
  144. }
  145. }
  146. });
  147. },
  148. /**
  149. * @private
  150. */
  151. userDb : function(callback) {
  152. $.couch.session({
  153. success : function(resp) {
  154. var userDb = $.couch.db(resp.info.authentication_db);
  155. callback(userDb);
  156. }
  157. });
  158. },
  159. /**
  160. * Create a new user on the CouchDB server, <code>user_doc</code> is an
  161. * object with a <code>name</code> field and other information you want
  162. * to store relating to that user, for example
  163. * <code>{"name": "daleharvey"}</code>
  164. * @param {Object} user_doc Users details
  165. * @param {String} password Users password
  166. * @param {ajaxSettings} options
  167. * <a href="http://api.jquery.com/jQuery.ajax/#jQuery-ajax-settings">
  168. * jQuery ajax settings</a>
  169. */
  170. signup: function(user_doc, password, options) {
  171. options = options || {};
  172. user_doc.password = password;
  173. user_doc.roles = user_doc.roles || [];
  174. user_doc.type = user_doc.type = "user" || [];
  175. var user_prefix = "org.couchdb.user:";
  176. user_doc._id = user_doc._id || user_prefix + user_doc.name;
  177. $.couch.userDb(function(db) {
  178. db.saveDoc(user_doc, options);
  179. });
  180. },
  181. /**
  182. * Authenticate against CouchDB, the <code>options</code> parameter is
  183. *expected to have <code>name</code> and <code>password</code> fields.
  184. * @param {ajaxSettings} options
  185. * <a href="http://api.jquery.com/jQuery.ajax/#jQuery-ajax-settings">
  186. * jQuery ajax settings</a>
  187. */
  188. login: function(options) {
  189. options = options || {};
  190. $.ajax({
  191. type: "POST", url: this.urlPrefix + "/_session", dataType: "json",
  192. data: {name: options.name, password: options.password},
  193. beforeSend: function(xhr) {
  194. xhr.setRequestHeader('Accept', 'application/json');
  195. },
  196. complete: function(req) {
  197. var resp = $.parseJSON(req.responseText);
  198. if (req.status == 200) {
  199. if (options.success) options.success(resp);
  200. } else if (options.error) {
  201. options.error(req.status, resp.error, resp.reason);
  202. } else {
  203. throw 'An error occurred logging in: ' + resp.reason;
  204. }
  205. }
  206. });
  207. },
  208. /**
  209. * Delete your current CouchDB user session
  210. * @param {ajaxSettings} options
  211. * <a href="http://api.jquery.com/jQuery.ajax/#jQuery-ajax-settings">
  212. * jQuery ajax settings</a>
  213. */
  214. logout: function(options) {
  215. options = options || {};
  216. $.ajax({
  217. type: "DELETE", url: this.urlPrefix + "/_session", dataType: "json",
  218. username : "_", password : "_",
  219. beforeSend: function(xhr) {
  220. xhr.setRequestHeader('Accept', 'application/json');
  221. },
  222. complete: function(req) {
  223. var resp = $.parseJSON(req.responseText);
  224. if (req.status == 200) {
  225. if (options.success) options.success(resp);
  226. } else if (options.error) {
  227. options.error(req.status, resp.error, resp.reason);
  228. } else {
  229. throw 'An error occurred logging out: ' + resp.reason;
  230. }
  231. }
  232. });
  233. },
  234. /**
  235. * @namespace
  236. * $.couch.db is used to communicate with a specific CouchDB database
  237. * <pre><code>var $db = $.couch.db("mydatabase");
  238. *$db.allApps({
  239. * success: function (data) {
  240. * ... process data ...
  241. * }
  242. *});
  243. * </code></pre>
  244. */
  245. db: function(name, db_opts) {
  246. db_opts = db_opts || {};
  247. var rawDocs = {};
  248. function maybeApplyVersion(doc) {
  249. if (doc._id && doc._rev && rawDocs[doc._id] &&
  250. rawDocs[doc._id].rev == doc._rev) {
  251. // todo: can we use commonjs require here?
  252. if (typeof Base64 == "undefined") {
  253. throw 'Base64 support not found.';
  254. } else {
  255. doc._attachments = doc._attachments || {};
  256. doc._attachments["rev-"+doc._rev.split("-")[0]] = {
  257. content_type :"application/json",
  258. data : Base64.encode(rawDocs[doc._id].raw)
  259. };
  260. return true;
  261. }
  262. }
  263. };
  264. return /** @lends $.couch.db */{
  265. name: name,
  266. uri: this.urlPrefix + "/" + encodeURIComponent(name) + "/",
  267. /**
  268. * Request compaction of the specified database.
  269. * @see <a href="http://techzone.couchbase.com/sites/default/files/
  270. * uploads/all/documentation/couchbase-api-db.html#couchbase-api-db_
  271. * db-compact_post">docs for /db/_compact</a>
  272. * @param {ajaxSettings} options
  273. * <a href="http://api.jquery.com/jQuery.ajax/#jQuery-ajax-settings">
  274. * jQuery ajax settings</a>
  275. */
  276. compact: function(options) {
  277. $.extend(options, {successStatus: 202});
  278. ajax({
  279. type: "POST", url: this.uri + "_compact",
  280. data: "", processData: false
  281. },
  282. options,
  283. "The database could not be compacted"
  284. );
  285. },
  286. /**
  287. * Cleans up the cached view output on disk for a given view.
  288. * @see <a href="http://techzone.couchbase.com/sites/default/files/
  289. * uploads/all/documentation/couchbase-api-db.html#couchbase-api-db
  290. * _db-view-cleanup_post">docs for /db/_compact</a>
  291. * @param {ajaxSettings} options <a href="http://api.jquery.com/
  292. * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
  293. */
  294. viewCleanup: function(options) {
  295. $.extend(options, {successStatus: 202});
  296. ajax({
  297. type: "POST", url: this.uri + "_view_cleanup",
  298. data: "", processData: false
  299. },
  300. options,
  301. "The views could not be cleaned up"
  302. );
  303. },
  304. /**
  305. * Compacts the view indexes associated with the specified design
  306. * document. You can use this in place of the full database compaction
  307. * if you know a specific set of view indexes have been affected by a
  308. * recent database change.
  309. * @see <a href="http://techzone.couchbase.com/sites/default/files/upl
  310. * oads/all/documentation/couchbase-api-db.html#couchbase-api-db_db-
  311. * compact-design-doc_post">docs for /db/_compact/design-doc</a>
  312. * @param {String} groupname Name of design-doc to compact
  313. * @param {ajaxSettings} options <a href="http://api.jquery.com/
  314. * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
  315. */
  316. compactView: function(groupname, options) {
  317. $.extend(options, {successStatus: 202});
  318. ajax({
  319. type: "POST", url: this.uri + "_compact/" + groupname,
  320. data: "", processData: false
  321. },
  322. options,
  323. "The view could not be compacted"
  324. );
  325. },
  326. /**
  327. * Create a new database
  328. * @see <a href="http://techzone.couchbase.com/sites/default/files/
  329. * uploads/all/documentation/couchbase-api-db.html#couchbase-api-db_
  330. * db_put">docs for PUT /db/</a>
  331. * @param {ajaxSettings} options <a href="http://api.jquery.com/
  332. * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
  333. */
  334. create: function(options) {
  335. $.extend(options, {successStatus: 201});
  336. ajax({
  337. type: "PUT", url: this.uri, contentType: "application/json",
  338. data: "", processData: false
  339. },
  340. options,
  341. "The database could not be created"
  342. );
  343. },
  344. /**
  345. * Deletes the specified database, and all the documents and
  346. * attachments contained within it.
  347. * @see <a href="http://techzone.couchbase.com/sites/default/files/
  348. * uploads/all/documentation/couchbase-api-db.html#couchbase-api-db_
  349. * db_delete">docs for DELETE /db/</a>
  350. * @param {ajaxSettings} options <a href="http://api.jquery.com/
  351. * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
  352. */
  353. drop: function(options) {
  354. ajax(
  355. {type: "DELETE", url: this.uri},
  356. options,
  357. "The database could not be deleted"
  358. );
  359. },
  360. /**
  361. * Gets information about the specified database.
  362. * @see <a href="http://techzone.couchbase.com/sites/default/files/
  363. * uploads/all/documentation/couchbase-api-db.html#couchbase-api-db
  364. * _db_get">docs for GET /db/</a>
  365. * @param {ajaxSettings} options <a href="http://api.jquery.com/
  366. * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
  367. */
  368. info: function(options) {
  369. ajax(
  370. {url: this.uri},
  371. options,
  372. "Database information could not be retrieved"
  373. );
  374. },
  375. /**
  376. * @namespace
  377. * $.couch.db.changes provides an API for subscribing to the changes
  378. * feed
  379. * <pre><code>var $changes = $.couch.db("mydatabase").changes();
  380. *$changes.onChange = function (data) {
  381. * ... process data ...
  382. * }
  383. * $changes.stop();
  384. * </code></pre>
  385. */
  386. changes: function(since, options) {
  387. options = options || {};
  388. // set up the promise object within a closure for this handler
  389. var timeout = 100, db = this, active = true,
  390. listeners = [],
  391. promise = /** @lends $.couch.db.changes */ {
  392. /**
  393. * Add a listener callback
  394. * @see <a href="http://techzone.couchbase.com/sites/default/
  395. * files/uploads/all/documentation/couchbase-api-db.html#couch
  396. * base-api-db_db-changes_get">docs for /db/_changes</a>
  397. * @param {Function} fun Callback function to run when
  398. * notified of changes.
  399. */
  400. onChange : function(fun) {
  401. listeners.push(fun);
  402. },
  403. /**
  404. * Stop subscribing to the changes feed
  405. */
  406. stop : function() {
  407. active = false;
  408. }
  409. };
  410. // call each listener when there is a change
  411. function triggerListeners(resp) {
  412. $.each(listeners, function() {
  413. this(resp);
  414. });
  415. };
  416. // when there is a change, call any listeners, then check for
  417. // another change
  418. options.success = function(resp) {
  419. timeout = 100;
  420. if (active) {
  421. since = resp.last_seq;
  422. triggerListeners(resp);
  423. getChangesSince();
  424. };
  425. };
  426. options.error = function() {
  427. if (active) {
  428. setTimeout(getChangesSince, timeout);
  429. timeout = timeout * 2;
  430. }
  431. };
  432. // actually make the changes request
  433. function getChangesSince() {
  434. var opts = $.extend({heartbeat : 10 * 1000}, options, {
  435. feed : "longpoll",
  436. since : since
  437. });
  438. ajax(
  439. {url: db.uri + "_changes"+encodeOptions(opts)},
  440. options,
  441. "Error connecting to "+db.uri+"/_changes."
  442. );
  443. }
  444. // start the first request
  445. if (since) {
  446. getChangesSince();
  447. } else {
  448. db.info({
  449. success : function(info) {
  450. since = info.update_seq;
  451. getChangesSince();
  452. }
  453. });
  454. }
  455. return promise;
  456. },
  457. /**
  458. * Fetch all the docs in this db, you can specify an array of keys to
  459. * fetch by passing the <code>keys</code> field in the
  460. * <code>options</code>
  461. * parameter.
  462. * @see <a href="http://techzone.couchbase.com/sites/default/files/
  463. * uploads/all/documentation/couchbase-api-db.html#couchbase-api-db_
  464. * db-all-docs_get">docs for /db/all_docs/</a>
  465. * @param {ajaxSettings} options <a href="http://api.jquery.com/
  466. * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
  467. */
  468. allDocs: function(options) {
  469. var type = "GET";
  470. var data = null;
  471. if (options["keys"]) {
  472. type = "POST";
  473. var keys = options["keys"];
  474. delete options["keys"];
  475. data = toJSON({ "keys": keys });
  476. }
  477. ajax({
  478. type: type,
  479. data: data,
  480. url: this.uri + "_all_docs" + encodeOptions(options)
  481. },
  482. options,
  483. "An error occurred retrieving a list of all documents"
  484. );
  485. },
  486. /**
  487. * Fetch all the design docs in this db
  488. * @param {ajaxSettings} options <a href="http://api.jquery.com/
  489. * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
  490. */
  491. allDesignDocs: function(options) {
  492. this.allDocs($.extend(
  493. {startkey:"_design", endkey:"_design0"}, options));
  494. },
  495. /**
  496. * Fetch all the design docs with an index.html, <code>options</code>
  497. * parameter expects an <code>eachApp</code> field which is a callback
  498. * called on each app found.
  499. * @param {ajaxSettings} options <a href="http://api.jquery.com/
  500. * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
  501. */
  502. allApps: function(options) {
  503. options = options || {};
  504. var self = this;
  505. if (options.eachApp) {
  506. this.allDesignDocs({
  507. success: function(resp) {
  508. $.each(resp.rows, function() {
  509. self.openDoc(this.id, {
  510. success: function(ddoc) {
  511. var index, appPath, appName = ddoc._id.split('/');
  512. appName.shift();
  513. appName = appName.join('/');
  514. index = ddoc.couchapp && ddoc.couchapp.index;
  515. if (index) {
  516. appPath = ['', name, ddoc._id, index].join('/');
  517. } else if (ddoc._attachments &&
  518. ddoc._attachments["index.html"]) {
  519. appPath = ['', name, ddoc._id, "index.html"].join('/');
  520. }
  521. if (appPath) options.eachApp(appName, appPath, ddoc);
  522. }
  523. });
  524. });
  525. }
  526. });
  527. } else {
  528. throw 'Please provide an eachApp function for allApps()';
  529. }
  530. },
  531. /**
  532. * Returns the specified doc from the specified db.
  533. * @see <a href="http://techzone.couchbase.com/sites/default/files/
  534. * uploads/all/documentation/couchbase-api-dbdoc.html#couchbase-api-
  535. * dbdoc_db-doc_get">docs for GET /db/doc</a>
  536. * @param {String} docId id of document to fetch
  537. * @param {ajaxSettings} options <a href="http://api.jquery.com/
  538. * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
  539. * @param {ajaxSettings} ajaxOptions <a href="http://api.jquery.com/
  540. * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
  541. */
  542. openDoc: function(docId, options, ajaxOptions) {
  543. options = options || {};
  544. if (db_opts.attachPrevRev || options.attachPrevRev) {
  545. $.extend(options, {
  546. beforeSuccess : function(req, doc) {
  547. rawDocs[doc._id] = {
  548. rev : doc._rev,
  549. raw : req.responseText
  550. };
  551. }
  552. });
  553. } else {
  554. $.extend(options, {
  555. beforeSuccess : function(req, doc) {
  556. if (doc["jquery.couch.attachPrevRev"]) {
  557. rawDocs[doc._id] = {
  558. rev : doc._rev,
  559. raw : req.responseText
  560. };
  561. }
  562. }
  563. });
  564. }
  565. ajax({url: this.uri + encodeDocId(docId) + encodeOptions(options)},
  566. options,
  567. "The document could not be retrieved",
  568. ajaxOptions
  569. );
  570. },
  571. /**
  572. * Create a new document in the specified database, using the supplied
  573. * JSON document structure. If the JSON structure includes the _id
  574. * field, then the document will be created with the specified document
  575. * ID. If the _id field is not specified, a new unique ID will be
  576. * generated.
  577. * @see <a href="http://techzone.couchbase.com/sites/default/files/
  578. * uploads/all/documentation/couchbase-api-dbdoc.html#couchbase-api-
  579. * dbdoc_db_post">docs for GET /db/doc</a>
  580. * @param {String} doc document to save
  581. * @param {ajaxSettings} options <a href="http://api.jquery.com/
  582. * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
  583. */
  584. saveDoc: function(doc, options) {
  585. options = options || {};
  586. var db = this;
  587. var beforeSend = fullCommit(options);
  588. if (doc._id === undefined) {
  589. var method = "POST";
  590. var uri = this.uri;
  591. } else {
  592. var method = "PUT";
  593. var uri = this.uri + encodeDocId(doc._id);
  594. }
  595. var versioned = maybeApplyVersion(doc);
  596. $.ajax({
  597. type: method, url: uri + encodeOptions(options),
  598. contentType: "application/json",
  599. dataType: "json", data: toJSON(doc),
  600. beforeSend : beforeSend,
  601. complete: function(req) {
  602. var resp = $.parseJSON(req.responseText);
  603. if (req.status == 200 || req.status == 201 || req.status == 202) {
  604. doc._id = resp.id;
  605. doc._rev = resp.rev;
  606. if (versioned) {
  607. db.openDoc(doc._id, {
  608. attachPrevRev : true,
  609. success : function(d) {
  610. doc._attachments = d._attachments;
  611. if (options.success) options.success(resp);
  612. }
  613. });
  614. } else {
  615. if (options.success) options.success(resp);
  616. }
  617. } else if (options.error) {
  618. options.error(req.status, resp.error, resp.reason);
  619. } else {
  620. throw "The document could not be saved: " + resp.reason;
  621. }
  622. }
  623. });
  624. },
  625. /**
  626. * Save a list of documents
  627. * @see <a href="http://techzone.couchbase.com/sites/default/files/
  628. * uploads/all/documentation/couchbase-api-db.html#couchbase-api-db_
  629. * db-bulk-docs_post">docs for /db/_bulk_docs</a>
  630. * @param {Object[]} docs List of documents to save
  631. * @param {ajaxSettings} options <a href="http://api.jquery.com/
  632. * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
  633. */
  634. bulkSave: function(docs, options) {
  635. var beforeSend = fullCommit(options);
  636. $.extend(options, {successStatus: 201, beforeSend : beforeSend});
  637. ajax({
  638. type: "POST",
  639. url: this.uri + "_bulk_docs" + encodeOptions(options),
  640. contentType: "application/json", data: toJSON(docs)
  641. },
  642. options,
  643. "The documents could not be saved"
  644. );
  645. },
  646. /**
  647. * Deletes the specified document from the database. You must supply
  648. * the current (latest) revision and <code>id</code> of the document
  649. * to delete eg <code>removeDoc({_id:"mydoc", _rev: "1-2345"})</code>
  650. * @see <a href="http://techzone.couchbase.com/sites/default/files/
  651. * uploads/all/documentation/couchbase-api-dbdoc.html#couchbase-api
  652. * -dbdoc_db-doc_delete">docs for DELETE /db/doc</a>
  653. * @param {Object} doc Document to delete
  654. * @param {ajaxSettings} options <a href="http://api.jquery.com/
  655. * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
  656. */
  657. removeDoc: function(doc, options) {
  658. ajax({
  659. type: "DELETE",
  660. url: this.uri +
  661. encodeDocId(doc._id) +
  662. encodeOptions({rev: doc._rev})
  663. },
  664. options,
  665. "The document could not be deleted"
  666. );
  667. },
  668. /**
  669. * Remove a set of documents
  670. * @see <a href="http://techzone.couchbase.com/sites/default/files/
  671. * uploads/all/documentation/couchbase-api-db.html#couchbase-api-db_
  672. * db-bulk-docs_post">docs for /db/_bulk_docs</a>
  673. * @param {String[]} docs List of document id's to remove
  674. * @param {ajaxSettings} options <a href="http://api.jquery.com/
  675. * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
  676. */
  677. bulkRemove: function(docs, options){
  678. docs.docs = $.each(
  679. docs.docs, function(i, doc){
  680. doc._deleted = true;
  681. }
  682. );
  683. $.extend(options, {successStatus: 201});
  684. ajax({
  685. type: "POST",
  686. url: this.uri + "_bulk_docs" + encodeOptions(options),
  687. data: toJSON(docs)
  688. },
  689. options,
  690. "The documents could not be deleted"
  691. );
  692. },
  693. /**
  694. * The COPY command (which is non-standard HTTP) copies an existing
  695. * document to a new or existing document.
  696. * @see <a href="http://techzone.couchbase.com/sites/default/files/
  697. * uploads/all/documentation/couchbase-api-dbdoc.html#couchbase-api-
  698. * dbdoc_db-doc_copy">docs for COPY /db/doc</a>
  699. * @param {String[]} docId document id to copy
  700. * @param {ajaxSettings} options <a href="http://api.jquery.com/
  701. * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
  702. * @param {ajaxSettings} options <a href="http://api.jquery.com/
  703. * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
  704. */
  705. copyDoc: function(docId, options, ajaxOptions) {
  706. ajaxOptions = $.extend(ajaxOptions, {
  707. complete: function(req) {
  708. var resp = $.parseJSON(req.responseText);
  709. if (req.status == 201) {
  710. if (options.success) options.success(resp);
  711. } else if (options.error) {
  712. options.error(req.status, resp.error, resp.reason);
  713. } else {
  714. throw "The document could not be copied: " + resp.reason;
  715. }
  716. }
  717. });
  718. ajax({
  719. type: "COPY",
  720. url: this.uri + encodeDocId(docId)
  721. },
  722. options,
  723. "The document could not be copied",
  724. ajaxOptions
  725. );
  726. },
  727. /**
  728. * Creates (and executes) a temporary view based on the view function
  729. * supplied in the JSON request.
  730. * @see <a href="http://techzone.couchbase.com/sites/default/files/
  731. * uploads/all/documentation/couchbase-api-db.html#couchbase-api-db
  732. * _db-temp-view_post">docs for /db/_temp_view</a>
  733. * @param {Function} mapFun Map function
  734. * @param {Function} reduceFun Reduce function
  735. * @param {Function} language Language the map / reduce funs are
  736. * implemented in
  737. * @param {ajaxSettings} options <a href="http://api.jquery.com/
  738. * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
  739. */
  740. query: function(mapFun, reduceFun, language, options) {
  741. language = language || "javascript";
  742. if (typeof(mapFun) !== "string") {
  743. mapFun = mapFun.toSource ? mapFun.toSource()
  744. : "(" + mapFun.toString() + ")";
  745. }
  746. var body = {language: language, map: mapFun};
  747. if (reduceFun != null) {
  748. if (typeof(reduceFun) !== "string")
  749. reduceFun = reduceFun.toSource ? reduceFun.toSource()
  750. : "(" + reduceFun.toString() + ")";
  751. body.reduce = reduceFun;
  752. }
  753. ajax({
  754. type: "POST",
  755. url: this.uri + "_temp_view" + encodeOptions(options),
  756. contentType: "application/json", data: toJSON(body)
  757. },
  758. options,
  759. "An error occurred querying the database"
  760. );
  761. },
  762. /**
  763. * Fetch a _list view output, you can specify a list of
  764. * <code>keys</code> in the options object to recieve only those keys.
  765. * @see <a href="http://techzone.couchbase.com/sites/default/files/
  766. * uploads/all/documentation/couchbase-api-design.html#couchbase-api
  767. * -design_db-design-designdoc-list-listname-viewname_get">
  768. * docs for /db/_design/design-doc/_list/l1/v1</a>
  769. * @param {String} list Listname in the form of ddoc/listname
  770. * @param {String} view View to run list against
  771. * @param {options} CouchDB <a href="http://wiki.apache.org/couchdb/
  772. * HTTP_view_API">View Options</a>
  773. * @param {ajaxSettings} options <a href="http://api.jquery.com/
  774. * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
  775. */
  776. list: function(list, view, options, ajaxOptions) {
  777. var list = list.split('/');
  778. var options = options || {};
  779. var type = 'GET';
  780. var data = null;
  781. if (options['keys']) {
  782. type = 'POST';
  783. var keys = options['keys'];
  784. delete options['keys'];
  785. data = toJSON({'keys': keys });
  786. }
  787. ajax({
  788. type: type,
  789. data: data,
  790. url: this.uri + '_design/' + list[0] +
  791. '/_list/' + list[1] + '/' + view + encodeOptions(options)
  792. },
  793. ajaxOptions, 'An error occured accessing the list'
  794. );
  795. },
  796. /**
  797. * Executes the specified view-name from the specified design-doc
  798. * design document, you can specify a list of <code>keys</code>
  799. * in the options object to recieve only those keys.
  800. * @see <a href="http://techzone.couchbase.com/sites/default/files/
  801. * uploads/all/documentation/couchbase-api-design.html#couchbase-api-
  802. * design_db-design-designdoc-view-viewname_get">docs for /db/
  803. * _design/design-doc/_list/l1/v1</a>
  804. * @param {String} name View to run list against (string should have
  805. * the design-doc name followed by a slash and the view name)
  806. * @param {ajaxSettings} options <a href="http://api.jquery.com/
  807. * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
  808. */
  809. view: function(name, options) {
  810. var name = name.split('/');
  811. var options = options || {};
  812. var type = "GET";
  813. var data= null;
  814. if (options["keys"]) {
  815. type = "POST";
  816. var keys = options["keys"];
  817. delete options["keys"];
  818. data = toJSON({ "keys": keys });
  819. }
  820. ajax({
  821. type: type,
  822. data: data,
  823. url: this.uri + "_design/" + name[0] +
  824. "/_view/" + name[1] + encodeOptions(options)
  825. },
  826. options, "An error occurred accessing the view"
  827. );
  828. },
  829. /**
  830. * Fetch an arbitrary CouchDB database property
  831. * @see <a href="http://techzone.couchbase.com/sites/default/files/
  832. * uploads/all/documentation/couchbase-api.html">docs for /db/_prop</a>
  833. * @param {String} propName Propery name to fetch
  834. * @param {ajaxSettings} options <a href="http://api.jquery.com/
  835. * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
  836. * @param {ajaxSettings} ajaxOptions <a href="http://api.jquery.com/
  837. * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
  838. */
  839. getDbProperty: function(propName, options, ajaxOptions) {
  840. ajax({url: this.uri + propName + encodeOptions(options)},
  841. options,
  842. "The property could not be retrieved",
  843. ajaxOptions
  844. );
  845. },
  846. /**
  847. * Set an arbitrary CouchDB database property
  848. * @see <a href="http://techzone.couchbase.com/sites/default/files/
  849. * uploads/all/documentation/couchbase-api.html">docs for /db/_prop</a>
  850. * @param {String} propName Propery name to fetch
  851. * @param {String} propValue Propery value to set
  852. * @param {ajaxSettings} options <a href="http://api.jquery.com/
  853. * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
  854. * @param {ajaxSettings} ajaxOptions <a href="http://api.jquery.com/
  855. * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
  856. */
  857. setDbProperty: function(propName, propValue, options, ajaxOptions) {
  858. ajax({
  859. type: "PUT",
  860. url: this.uri + propName + encodeOptions(options),
  861. data : JSON.stringify(propValue)
  862. },
  863. options,
  864. "The property could not be updated",
  865. ajaxOptions
  866. );
  867. }
  868. };
  869. },
  870. encodeDocId: encodeDocId,
  871. /**
  872. * Accessing the root of a CouchDB instance returns meta information about
  873. * the instance. The response is a JSON structure containing information
  874. * about the server, including a welcome message and the version of the
  875. * server.
  876. * @see <a href="http://techzone.couchbase.com/sites/default/files/uploads/
  877. * all/documentation/couchbase-api-misc.html#couchbase-api-misc_root_get">
  878. * docs for GET /</a>
  879. * @param {ajaxSettings} options <a href="http://api.jquery.com/
  880. * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
  881. */
  882. info: function(options) {
  883. ajax(
  884. {url: this.urlPrefix + "/"},
  885. options,
  886. "Server information could not be retrieved"
  887. );
  888. },
  889. /**
  890. * Request, configure, or stop, a replication operation.
  891. * @see <a href="http://techzone.couchbase.com/sites/default/files/
  892. * uploads/all/documentation/couchbase-api-misc.html#couchbase-api-
  893. * misc_replicate_post">docs for POST /_replicate</a>
  894. * @param {String} source Path or url to source database
  895. * @param {String} target Path or url to target database
  896. * @param {ajaxSettings} ajaxOptions <a href="http://api.jquery.com/
  897. * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
  898. * @param {Object} repOpts Additional replication options
  899. */
  900. replicate: function(source, target, ajaxOptions, repOpts) {
  901. repOpts = $.extend({source: source, target: target}, repOpts);
  902. if (repOpts.continuous && !repOpts.cancel) {
  903. ajaxOptions.successStatus = 202;
  904. }
  905. ajax({
  906. type: "POST", url: this.urlPrefix + "/_replicate",
  907. data: JSON.stringify(repOpts),
  908. contentType: "application/json"
  909. },
  910. ajaxOptions,
  911. "Replication failed"
  912. );
  913. },
  914. /**
  915. * Fetch a new UUID
  916. * @see <a href="http://techzone.couchbase.com/sites/default/files/
  917. * uploads/all/documentation/couchbase-api-misc.html#couchbase-api-
  918. * misc_uuids_get">docs for /_uuids</a>
  919. * @param {Int} cacheNum Number of uuids to keep cached for future use
  920. */
  921. newUUID: function(cacheNum) {
  922. if (cacheNum === undefined) {
  923. cacheNum = 1;
  924. }
  925. if (!uuidCache.length) {
  926. ajax({url: this.urlPrefix + "/_uuids", data: {count: cacheNum}, async:
  927. false}, {
  928. success: function(resp) {
  929. uuidCache = resp.uuids;
  930. }
  931. },
  932. "Failed to retrieve UUID batch."
  933. );
  934. }
  935. return uuidCache.shift();
  936. }
  937. });
  938. /**
  939. * @private
  940. */
  941. function ajax(obj, options, errorMessage, ajaxOptions) {
  942. var timeStart;
  943. var defaultAjaxOpts = {
  944. contentType: "application/json",
  945. headers:{"Accept": "application/json"}
  946. };
  947. options = $.extend({successStatus: 200}, options);
  948. ajaxOptions = $.extend(defaultAjaxOpts, ajaxOptions);
  949. errorMessage = errorMessage || "Unknown error";
  950. timeStart = (new Date()).getTime();
  951. $.ajax($.extend($.extend({
  952. type: "GET", dataType: "json", cache : !$.browser.msie,
  953. beforeSend: function(xhr){
  954. if(ajaxOptions && ajaxOptions.headers){
  955. for (var header in ajaxOptions.headers){
  956. xhr.setRequestHeader(header, ajaxOptions.headers[header]);
  957. }
  958. }
  959. },
  960. complete: function(req) {
  961. var reqDuration = (new Date()).getTime() - timeStart;
  962. try {
  963. var resp = $.parseJSON(req.responseText);
  964. } catch(e) {
  965. if (options.error) {
  966. options.error(req.status, req, e);
  967. } else {
  968. throw errorMessage + ': ' + e;
  969. }
  970. return;
  971. }
  972. if (options.ajaxStart) {
  973. options.ajaxStart(resp);
  974. }
  975. if (req.status == options.successStatus) {
  976. if (options.beforeSuccess) options.beforeSuccess(req, resp, reqDuration);
  977. if (options.success) options.success(resp, reqDuration);
  978. } else if (options.error) {
  979. options.error(req.status, resp && resp.error ||
  980. errorMessage, resp && resp.reason || "no response",
  981. reqDuration);
  982. } else {
  983. throw errorMessage + ": " + resp.reason;
  984. }
  985. }
  986. }, obj), ajaxOptions));
  987. }
  988. /**
  989. * @private
  990. */
  991. function fullCommit(options) {
  992. var options = options || {};
  993. if (typeof options.ensure_full_commit !== "undefined") {
  994. var commit = options.ensure_full_commit;
  995. delete options.ensure_full_commit;
  996. return function(xhr) {
  997. xhr.setRequestHeader('Accept', 'application/json');
  998. xhr.setRequestHeader("X-Couch-Full-Commit", commit.toString());
  999. };
  1000. }
  1001. };
  1002. /**
  1003. * @private
  1004. */
  1005. // Convert a options object to an url query string.
  1006. // ex: {key:'value',key2:'value2'} becomes '?key="value"&key2="value2"'
  1007. function encodeOptions(options) {
  1008. var buf = [];
  1009. if (typeof(options) === "object" && options !== null) {
  1010. for (var name in options) {
  1011. if ($.inArray(name,
  1012. ["error", "success", "beforeSuccess", "ajaxStart"]) >= 0)
  1013. continue;
  1014. var value = options[name];
  1015. if ($.inArray(name, ["key", "startkey", "endkey"]) >= 0) {
  1016. value = toJSON(value);
  1017. }
  1018. buf.push(encodeURIComponent(name) + "=" + encodeURIComponent(value));
  1019. }
  1020. }
  1021. return buf.length ? "?" + buf.join("&") : "";
  1022. }
  1023. /**
  1024. * @private
  1025. */
  1026. function toJSON(obj) {
  1027. return obj !== null ? JSON.stringify(obj) : null;
  1028. }
  1029. })(jQuery);