solr.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957
  1. /*!
  2. * solr client
  3. * Copyright(c) 2011-2012 HipSnip Limited
  4. * Copyright(c) 2013-2014 Rémy Loubradou
  5. * Author Rémy Loubradou <remyloubradou@gmail.com>
  6. * MIT Licensed
  7. */
  8. /**
  9. * Load dependencies
  10. */
  11. var http = require('http'),
  12. https = require('https'),
  13. Query = require('./query'),
  14. Collection = require('./collection'),
  15. querystring = require('querystring'),
  16. format = require('./utils/format'),
  17. SolrError = require('./error/solr-error'),
  18. JSONStream = require('JSONStream'),
  19. duplexer = require('duplexer'),
  20. request = require('request'),
  21. JSONbig = require('json-bigint'),
  22. versionUtils = require('./utils/version');
  23. /**
  24. * Expose `createClient()`.
  25. */
  26. exports.createClient = createClient;
  27. /**
  28. * Create an instance of `Client`
  29. *
  30. * @param {String|Object} [host='127.0.0.1'] - IP address or host address of the Solr server
  31. * @param {Number|String} [port='8983'] - port of the Solr server
  32. * @param {String} [core=''] - name of the Solr core requested
  33. * @param {String} [path='/solr'] - root path of all requests
  34. * @param {http.Agent} [agent] - HTTP Agent which is used for pooling sockets used in HTTP(s) client requests
  35. * @param {Boolean} [secure=false] - if true HTTPS will be used instead of HTTP
  36. * @param {Boolean} [bigint=false] - if true JSONbig serializer/deserializer will be used instead
  37. * of JSON native serializer/deserializer
  38. * @param solrVersion ['3.2', '4.0', '5.0', '5.1'], check lib/utils/version.js for full reference
  39. *
  40. * @return {Client}
  41. * @api public
  42. */
  43. function createClient(host, port, core, path, agent, secure, bigint, solrVersion){
  44. var options = (typeof host === 'object') ? host : {
  45. host : host,
  46. port : port,
  47. core : core,
  48. path : path,
  49. agent : agent,
  50. secure : secure,
  51. bigint : bigint,
  52. solrVersion: solrVersion
  53. };
  54. return new Client(options);
  55. }
  56. /**
  57. * Solr client
  58. * @constructor
  59. *
  60. * @param {Object} options - set of options used to request the Solr server
  61. * @param {String} options.host - IP address or host address of the Solr server
  62. * @param {Number|String} options.port - port of the Solr server
  63. * @param {String} options.core - name of the Solr core requested
  64. * @param {String} options.path - root path of all requests
  65. * @param {http.Agent} [options.agent] - HTTP Agent which is used for pooling sockets used in HTTP(s) client requests
  66. * @param {Boolean} [options.secure=false] - if true HTTPS will be used instead of HTTP
  67. * @param {Boolean} [options.bigint=false] - if true JSONbig serializer/deserializer will be used instead
  68. * of JSON native serializer/deserializer
  69. *
  70. * @return {Client}
  71. * @api private
  72. */
  73. function Client(options){
  74. this.options = {
  75. host : options.host || '127.0.0.1',
  76. port : options.port || '8983',
  77. core : options.core || '',
  78. path : options.path || '/solr',
  79. agent : options.agent,
  80. secure : options.secure || false,
  81. bigint : options.bigint || false,
  82. get_max_request_entity_size: options.get_max_request_entity_size || false,
  83. solrVersion: options.solrVersion || versionUtils.Solr3_2
  84. };
  85. // Default paths of all request handlers
  86. this.UPDATE_JSON_HANDLER = (versionUtils.version(this.options.solrVersion) >= versionUtils.Solr4_0) ? 'update' : 'update/json';
  87. this.UPDATE_HANDLER = 'update';
  88. this.SELECT_HANDLER = 'select';
  89. this.COLLECTIONS_HANDLER = 'admin/collections';
  90. this.ADMIN_PING_HANDLER = 'admin/ping';
  91. this.REAL_TIME_GET_HANDLER = 'get';
  92. this.SPELL_HANDLER = 'spell';
  93. }
  94. /**
  95. * Create credential using the basic access authentication method
  96. *
  97. * @param {String} username
  98. * @param {String} password
  99. *
  100. * @return {Client}
  101. * @api public
  102. */
  103. Client.prototype.basicAuth = function(username,password){
  104. var self = this;
  105. this.options.authorization = 'Basic ' + new Buffer(username + ':' + password).toString('base64');
  106. return self;
  107. }
  108. /**
  109. * Remove authorization header
  110. *
  111. * @return {Client}
  112. * @api public
  113. */
  114. Client.prototype.unauth = function(){
  115. var self = this;
  116. delete this.options.authorization;
  117. return self;
  118. }
  119. /**
  120. * Add a document or a list of documents
  121. *
  122. * @param {Object|Array} doc - document or list of documents to add into the Solr database
  123. * @param {Object} [options] -
  124. * @param {Function} callback(err,obj) - a function executed when the Solr server responds or an error occurs
  125. * @param {Error} callback().err
  126. * @param {Object} callback().obj - JSON response sent by the Solr server deserialized
  127. *
  128. * @return {http.ClientRequest}
  129. * @api public
  130. */
  131. Client.prototype.add = function(docs,options,callback){
  132. if(typeof(options) === 'function'){
  133. callback = options;
  134. options = {};
  135. }
  136. docs = format.dateISOify(docs); // format `Date` object into string understable for Solr as a date.
  137. docs = Array.isArray(docs) ? docs : [docs];
  138. return this.update(docs,options,callback);
  139. }
  140. /**
  141. * Get a document by id or a list of documents by ids using the Real-time-get feature
  142. * in SOLR4 (https://wiki.apache.org/solr/RealTimeGet)
  143. *
  144. * @param {String|Array} ids - id or list of ids that identify the documents to get
  145. * @param {Query|Object|String} [query] -
  146. * @param {Function} callback(err,obj) - a function executed when the Solr server responds or an error occurs
  147. * @param {Error} callback().err
  148. * @param {Object} callback().obj - JSON response sent by the Solr server deserialized
  149. *
  150. * @return {http.ClientRequest}
  151. * @api public
  152. */
  153. Client.prototype.realTimeGet = function(ids, query, callback){
  154. if(typeof query === 'function'){
  155. callback = query;
  156. query = {};
  157. }
  158. ids = Array.isArray(ids) ? ids : [ids];
  159. query.ids = ids.join(',');
  160. return this.get(this.REAL_TIME_GET_HANDLER,query,callback);
  161. }
  162. /**
  163. * Add the remote resource located at the given path `options.path` into the Solr database.
  164. *
  165. * @param {Object} options -
  166. * @param {String} options.path - path of the file. HTTP URL, the full path or a path relative to the CWD of the running solr server must be used.
  167. * @param {String} [options.format='xml'] - format of the resource. XML, CSV or JSON formats must be used.
  168. * @param {String} [options.contentType='text/plain;charset=utf-8'] - content type of the resource
  169. * @param {Object} [options.parameters] - set of extras parameters pass along in the query.
  170. * @param {Function} callback(err,obj) - a function executed when the Solr server responds or an error occurs
  171. * @param {Error} callback().err
  172. * @param {Object} callback().obj - JSON response sent by the Solr server deserialized
  173. *
  174. * @return {http.ClientRequest}
  175. * @api public
  176. */
  177. Client.prototype.addRemoteResource = function(options,callback){
  178. options.parameters = options.parameters || {};
  179. options.format = (options.format === 'xml' ? '' : options.format || ''); // reason: the default route of the XmlUpdateRequestHandle is /update and not /update/xml.
  180. options.parameters.commit = (options.parameters.commit === undefined ? false : options.parameters.commit);
  181. options.parameters['stream.contentType'] = options.contentType || 'text/plain;charset=utf-8';
  182. if(options.path.match(/^https?:\/\//)){
  183. options.parameters['stream.url'] = options.path;
  184. }else{
  185. options.parameters['stream.file'] = options.path;
  186. }
  187. var handler = this.UPDATE_HANDLER + '/' + options.format.toLowerCase();
  188. var query = querystring.stringify(options.parameters);
  189. return this.get(handler,query,callback);
  190. }
  191. /**
  192. * Create a writable/readable `Stream` to add documents into the Solr database
  193. *
  194. * @param {Object} [options] -
  195. *
  196. * return {Stream}
  197. * @api public
  198. */
  199. Client.prototype.createAddStream = function(options){
  200. var path = [this.options.path,this.options.core, this.UPDATE_JSON_HANDLER + '?' + querystring.stringify(options) +'&wt=json']
  201. .filter(function(element){
  202. return element;
  203. })
  204. .join('/');
  205. var headers = {
  206. 'content-type' : 'application/json',
  207. 'charset' : 'utf-8'
  208. };
  209. if(this.options.authorization){
  210. headers['authorization'] = this.options.authorization;
  211. }
  212. var protocol = this.options.secure ? 'https' : 'http';
  213. var optionsRequest = {
  214. url : protocol + '://' + this.options.host +':' + this.options.port + path ,
  215. method : 'POST',
  216. headers : headers
  217. };
  218. var jsonStreamStringify = JSONStream.stringify();
  219. var postRequest = request(optionsRequest);
  220. jsonStreamStringify.pipe(postRequest);
  221. var duplex = duplexer(jsonStreamStringify,postRequest);
  222. return duplex ;
  223. }
  224. /**
  225. * Commit last added and removed documents, that means your documents are now indexed.
  226. *
  227. * @param {Object} options
  228. * @param {Function} callback(err,obj) - a function executed when the Solr server responds or an error occurs
  229. * @param {Error} callback().err
  230. * @param {Object} callback().obj - JSON response sent by the Solr server deserialized
  231. *
  232. * @return {http.ClientRequest}
  233. * @api public
  234. */
  235. Client.prototype.commit = function(options,callback){
  236. if(typeof(options) === 'function'){
  237. callback = options;
  238. options = {};
  239. }
  240. var data = {
  241. commit : options || {}
  242. };
  243. return this.update(data,callback);
  244. }
  245. /**
  246. * Call Lucene's IndexWriter.prepareCommit, the changes won't be visible in the index.
  247. *
  248. * @param {Function} callback(err,obj) - a function executed when the Solr server responds or an error occurs
  249. * @param {Error} callback().err
  250. * @param {Object} callback().obj - JSON response sent by the Solr server deserialized
  251. *
  252. * @return {http.ClientRequest}
  253. * @api public
  254. */
  255. Client.prototype.prepareCommit = function(callback){
  256. return this.update({},{ prepareCommit : true},callback);
  257. }
  258. /**
  259. * Soft commit all changes
  260. *
  261. * @param {Function} callback(err,obj) - a function executed when the Solr server responds or an error occurs
  262. * @param {Error} callback().err
  263. * @param {Object} callback().obj - JSON response sent by the Solr server deserialized
  264. *
  265. * @return {http.ClientRequest}
  266. * @api public
  267. */
  268. Client.prototype.softCommit = function(callback){
  269. return this.update({},{ softCommit : true},callback);
  270. }
  271. /**
  272. * Delete documents based on the given `field` and `text`.
  273. *
  274. * @param {String} field
  275. * @param {String} text
  276. * @param {Object} [options]
  277. * @param {Function} callback(err,obj) - a function executed when the Solr server responds or an error occurs
  278. * @param {Error} callback().err
  279. * @param {Object} callback().obj - JSON response sent by the Solr server deserialized
  280. *
  281. * @return {http.ClientRequest}
  282. * @api public
  283. */
  284. Client.prototype.delete = function(field,text,options,callback) {
  285. if(typeof(options) === 'function'){
  286. callback = options;
  287. options = {};
  288. }
  289. text = format.dateISOify(text);
  290. var data = {
  291. 'delete' : {
  292. query : field + ':' + format.escapeSpecialChars(text)
  293. }
  294. };
  295. return this.update(data,options,callback);
  296. }
  297. /**
  298. * Delete a range of documents based on the given `field`, `start` and `stop` arguments.
  299. *
  300. * @param {String} field
  301. * @param {String|Date} start
  302. * @param {String|Date} stop
  303. * @param {Function} callback(err,obj) - a function executed when the Solr server responds or an error occurs
  304. * @param {Error} callback().err
  305. * @param {Object} callback().obj - JSON response sent by the Solr server deserialized
  306. *
  307. * @return {http.ClientRequest}
  308. * @api public
  309. */
  310. Client.prototype.deleteByRange = function(field,start,stop,options,callback){
  311. if(typeof(options) === 'function'){
  312. callback = options;
  313. options = {};
  314. }
  315. start = format.dateISOify(start);
  316. stop = format.dateISOify(stop);
  317. var query = field + ':[' + start + ' TO ' + stop + ']';
  318. return this.deleteByQuery(query,options,callback);
  319. }
  320. /**
  321. * Delete the document with the given `id`
  322. *
  323. * @param {String|Number} id - id of the document you want to delete
  324. * @param {Object} [options] -
  325. * @param {Function} callback(err,obj) - a function executed when the Solr server responds or an error occurs
  326. * @param {Error} callback().err
  327. * @param {Object} callback().obj - JSON response sent by the Solr server deserialized
  328. *
  329. * @return {http.ClientRequest}
  330. * @api public
  331. */
  332. Client.prototype.deleteByID = function(id,options,callback){
  333. if(typeof(options) === 'function'){
  334. callback = options;
  335. options = {};
  336. }
  337. var data = {
  338. 'delete' : {
  339. id : id
  340. }
  341. };
  342. return this.update(data,options,callback);
  343. }
  344. /**
  345. * Delete documents matching the given `query`
  346. *
  347. * @param {String} query -
  348. * @param {Object} [options] -
  349. * @param {Function} callback(err,obj) - a function executed when the Solr server responds or an error occurs
  350. * @param {Error} callback().err
  351. * @param {Object} callback().obj - JSON response sent by the Solr server deserialized
  352. *
  353. * @return {http.ClientRequest}
  354. * @api public
  355. */
  356. Client.prototype.deleteByQuery = function(query,options,callback){
  357. if(typeof(options) === 'function'){
  358. callback = options;
  359. options = {};
  360. }
  361. var data = {
  362. 'delete' : {
  363. query : query
  364. }
  365. };
  366. return this.update(data,options,callback);
  367. }
  368. /**
  369. * Delete all documents
  370. *
  371. * @param {Object} [options] -
  372. * @param {Function} callback(err,obj) - a function executed when the Solr server responds or an error occurs
  373. * @param {Error} callback().err
  374. * @param {Object} callback().obj - JSON response sent by the Solr server deserialized
  375. *
  376. * @return {http.ClientRequest}
  377. * @api public
  378. */
  379. Client.prototype.deleteAll = function(options,callback){
  380. return this.deleteByQuery('*:*',options,callback);
  381. }
  382. /**
  383. * Optimize the index
  384. *
  385. * @param {Object} options -
  386. * @param {Function} callback(err,obj) - a function executed when the Solr server responds or an error occurs
  387. * @param {Error} callback().err
  388. * @param {Object} callback().obj - JSON response sent by the Solr server deserialized
  389. *
  390. * @return {http.ClientRequest}
  391. * @api public
  392. */
  393. Client.prototype.optimize = function(options,callback){
  394. if(typeof(options) === 'function'){
  395. callback = options;
  396. options = {};
  397. }
  398. var data = {
  399. optimize : options || {}
  400. };
  401. return this.update(data,callback);
  402. }
  403. /**
  404. * Rollback all add/delete commands made since the last commit.
  405. *
  406. * @param {Function} callback(err,obj) - a function executed when the Solr server responds or an error occurs
  407. * @param {Error} callback().err
  408. * @param {Object} callback().obj - JSON response sent by the Solr server deserialized
  409. *
  410. * @return {http.ClientRequest}
  411. * @api public
  412. */
  413. Client.prototype.rollback = function(callback){
  414. var data = {
  415. rollback : {}
  416. };
  417. return this.update(data,callback);
  418. }
  419. /**
  420. * Send an update command to the Solr server with the given `data` stringified in the body.
  421. *
  422. * @param {Object} data - data sent to the Solr server
  423. * @param {Object} [options] -
  424. * @param {Function} callback(err,obj) - a function executed when the Solr server responds or an error occurs
  425. * @param {Error} callback().err
  426. * @param {Object} callback().obj - JSON response sent by the Solr server deserialized
  427. *
  428. * @return {http.ClientRequest}
  429. * @api private
  430. */
  431. Client.prototype.update = function(data,options,callback){
  432. if(typeof(options) === 'function'){
  433. callback = options;
  434. options = {};
  435. }
  436. var json = pickJSON(this.options.bigint).stringify(data);
  437. var fullPath = [this.options.path,this.options.core, this.UPDATE_JSON_HANDLER + '?' + querystring.stringify(options) +'&wt=json']
  438. .filter(function(element){
  439. return element;
  440. })
  441. .join('/');
  442. var params = {
  443. host : this.options.host,
  444. port : this.options.port,
  445. fullPath : fullPath,
  446. json : json,
  447. secure : this.options.secure,
  448. bigint : this.options.bigint,
  449. authorization : this.options.authorization,
  450. agent : this.options.agent
  451. };
  452. return postJSON(params,callback);
  453. }
  454. /**
  455. * Search documents matching the `query`
  456. *
  457. * @param {Query|Object|String} query
  458. * @param {Function} callback(err,obj) - a function executed when the Solr server responds or an error occurs
  459. * @param {Error} callback().err
  460. * @param {Object} callback().obj - JSON response sent by the Solr server deserialized
  461. *
  462. * @return {http.ClientRequest}
  463. * @api public
  464. */
  465. Client.prototype.search = function(query,callback){
  466. return this.get(this.SELECT_HANDLER, query, callback);
  467. }
  468. /**
  469. * Execute an Admin Collections task on `collection`
  470. *
  471. * @param {Query|Object|String} collection
  472. * @param {Function} callback(err,obj) - a function executed when the Solr server responds or an error occurs
  473. * @param {Error} callback().err
  474. * @param {Object} callback().obj - JSON response sent by the Solr server deserialized
  475. *
  476. * @return {http.ClientRequest}
  477. * @api public
  478. */
  479. Client.prototype.executeCollection = function(collection,callback){
  480. return this.get(this.COLLECTIONS_HANDLER, collection, callback);
  481. }
  482. /**
  483. * Search for all documents
  484. *
  485. * @param {Function} callback(err,obj) - a function executed when the Solr server responds or an error occurs
  486. * @param {Error} callback().err
  487. * @param {Object} callback().obj - JSON response sent by the Solr server deserialized
  488. *
  489. * @return {http.ClientRequest}
  490. * @api public
  491. */
  492. Client.prototype.searchAll = function(callback){
  493. return this.search('q=*', callback);
  494. }
  495. /**
  496. * Search documents matching the `query`
  497. *
  498. * Spellcheck is also enabled.
  499. *
  500. * @param {Query|Object|String} query
  501. * @param {Function} callback(err,obj) - a function executed when the Solr server responds or an error occurs
  502. * @param {Error} callback().err
  503. * @param {Object} callback().obj - JSON response sent by the Solr server deserialized
  504. *
  505. * @return {http.ClientRequest}
  506. * @api public
  507. */
  508. Client.prototype.spell = function(query,callback){
  509. return this.get(this.SPELL_HANDLER, query, callback);
  510. }
  511. /**
  512. * Send an arbitrary HTTP GET request to Solr on the specified `handler` (as Solr like to call it i.e path)
  513. *
  514. * @param {String} handler
  515. * @param {Query|Object|String} [query]
  516. * @param {Function} callback(err,obj) - a function executed when the Solr server responds or an error occurs
  517. * @param {Error} callback().err
  518. * @param {Object} callback().obj - JSON response sent by the Solr server deserialized
  519. *
  520. * @return {http.ClientRequest}
  521. * @api public
  522. */
  523. Client.prototype.get = function(handler,query,callback){
  524. var parameters = '';
  525. if(typeof query === 'function'){
  526. callback = query;
  527. }else if((query instanceof Query) || (query instanceof Collection)){
  528. parameters += query.build();
  529. }else if(typeof query === 'object'){
  530. parameters += querystring.stringify(query);
  531. }else if(typeof query === 'string'){
  532. parameters += query;
  533. }
  534. var pathArray;
  535. if(handler != 'admin/collections'){
  536. pathArray = [this.options.path,this.options.core,handler + '?' + parameters + '&wt=json'];
  537. } else {
  538. pathArray = [this.options.path,handler + '?' + parameters + '&wt=json'];
  539. }
  540. var fullPath = pathArray.filter(function(element){
  541. return element;
  542. }).join('/');
  543. var approxUrlLength = 10 + Buffer.byteLength(this.options.host) + (this.options.port+"").length + Buffer.byteLength(fullPath); // Buffer (10) accounts for protocol and special characters like ://, port colon, and initial slash etc
  544. if (this.options.get_max_request_entity_size === false || approxUrlLength <= this.options.get_max_request_entity_size) {
  545. var params = {
  546. host: this.options.host,
  547. port: this.options.port,
  548. fullPath: fullPath,
  549. secure: this.options.secure,
  550. bigint: this.options.bigint,
  551. authorization: this.options.authorization,
  552. agent: this.options.agent
  553. };
  554. return getJSON(params, callback);
  555. } else {
  556. // Funnel this through a POST because it's too large
  557. return this.post(handler, query, callback);
  558. }
  559. }
  560. /**
  561. * Send an arbitrary HTTP POST request to Solr on the specified `handler` (as Solr like to call it i.e path)
  562. *
  563. * @param {String} handler
  564. * @param {Query|Object|String} [query]
  565. * @param {Function} callback(err,obj) - a function executed when the Solr server responds or an error occurs
  566. * @param {Error} callback().err
  567. * @param {Object} callback().obj - JSON response sent by the Solr server deserialized
  568. *
  569. * @return {http.ClientRequest}
  570. * @api public
  571. */
  572. Client.prototype.post = function(handler,query,callback){
  573. var parameters = '';
  574. if(typeof query === 'function'){
  575. callback = query;
  576. }else if(query instanceof Query){
  577. parameters += query.build();
  578. }else if(typeof query === 'object'){
  579. parameters += querystring.stringify(query);
  580. }else if(typeof query === 'string'){
  581. parameters += query;
  582. }
  583. var pathArray;
  584. if(handler != 'admin/collections'){
  585. pathArray = [this.options.path,this.options.core,handler + '?' + parameters + '&wt=json'];
  586. } else {
  587. pathArray = [this.options.path,handler + '?' + parameters + '&wt=json'];
  588. }
  589. var fullPath = pathArray.filter(function(element){
  590. return element;
  591. }).join('/');
  592. var params = {
  593. host : this.options.host,
  594. port : this.options.port,
  595. fullPath : fullPath,
  596. params : parameters,
  597. secure : this.options.secure,
  598. bigint : this.options.bigint,
  599. authorization : this.options.authorization,
  600. agent : this.options.agent
  601. };
  602. return postForm(params,callback);
  603. }
  604. /**
  605. * Create an instance of `Query`
  606. *
  607. * @return {Query}
  608. * @api public
  609. */
  610. Client.prototype.query = function(){
  611. return new Query(this.options);
  612. }
  613. /**
  614. * Create an instance of `Query`
  615. * NOTE: This method will be deprecated in the v0.6 release. Please use `Client.query()` instead.
  616. *
  617. * @return {Query}
  618. * @api public
  619. */
  620. Client.prototype.createQuery = function(){
  621. return new Query(this.options);
  622. }
  623. /**
  624. * Create an instance of `Collection`
  625. *
  626. * @return {Collection}
  627. * @api public
  628. */
  629. Client.prototype.collection = function(){
  630. return new Collection();
  631. }
  632. /**
  633. * Expose `format.escapeSpecialChars` from `Client.escapeSpecialChars`
  634. */
  635. Client.prototype.escapeSpecialChars = format.escapeSpecialChars;
  636. /**
  637. * Ping the Solr server
  638. *
  639. * @param {Function} callback(err,obj) - a function executed when the Solr server responds or an error occurs
  640. * @param {Error} callback().err
  641. * @param {Object} callback().obj - JSON response sent by the Solr server deserialized
  642. *
  643. * @return {http.ClientRequest}
  644. * @api public
  645. */
  646. Client.prototype.ping = function(callback){
  647. return this.get(this.ADMIN_PING_HANDLER, callback);
  648. }
  649. /**
  650. * Pick appropriate protocol based on the given `secure` flag
  651. *
  652. * @param {Boolean} secure -
  653. * @return {Object} http or https module
  654. * @api private
  655. */
  656. function pickProtocol(secure){
  657. return secure ? https : http;
  658. };
  659. /**
  660. * Pick appropriate JSON serializer/deserializer library based on the given `bigint` flag
  661. * @param {Boolean} bigint - whenever to handle big number correctly or not (the reason for not using JSONbig all the times is it has an important performance cost)
  662. * @return {Object} JSON or JSONbig serializer/deserializer
  663. * @api private
  664. */
  665. function pickJSON(bigint){
  666. return bigint ? JSONbig : JSON;
  667. };
  668. /**
  669. * HTTP POST request. Send update commands to the Solr server (commit, add, delete, optimize)
  670. *
  671. * @param {Object} params
  672. * @param {String} params.host - IP address or host address of the Solr server
  673. * @param {Number|String} params.port - port of the Solr server
  674. * @param {String} params.core - name of the Solr core requested
  675. * @param {String} params.authorization - value of the authorization header
  676. * @param {String} params.fullPath - full path of the request
  677. * @param {String} params.json -
  678. * @param {Boolean} params.secure -
  679. * @param {Boolean} params.bigint -
  680. * @param {http.Agent} [params.agent] -
  681. * @param {Function} callback(err,obj) - a function executed when the Solr server responds or an error occurs
  682. * @param {Error} callback().err
  683. * @param {Object} callback().obj - JSON response sent by the Solr server deserialized
  684. *
  685. * @return {http.ClientRequest}
  686. * @api private
  687. */
  688. function postJSON(params,callback){
  689. var headers = {
  690. 'content-type' : 'application/json; charset=utf-8',
  691. 'content-length': Buffer.byteLength(params.json),
  692. 'accept' : 'application/json; charset=utf-8'
  693. };
  694. if(params.authorization){
  695. headers['authorization'] = params.authorization;
  696. }
  697. var options = {
  698. host : params.host,
  699. port : params.port,
  700. method : 'POST',
  701. headers : headers,
  702. path : params.fullPath
  703. };
  704. if(params.agent !== undefined){
  705. options.agent = params.agent;
  706. }
  707. var request = pickProtocol(params.secure).request(options);
  708. request.on('response', handleJSONResponse(request, params.bigint, callback));
  709. request.on('error',function onError(err){
  710. if (callback) callback(err,null);
  711. });
  712. request.write(params.json);
  713. request.end();
  714. return request;
  715. };
  716. /**
  717. * HTTP POST request. Send update commands to the Solr server using form encoding (e.g. search)
  718. *
  719. * @param {Object} params
  720. * @param {String} params.host - IP address or host address of the Solr server
  721. * @param {Number|String} params.port - port of the Solr server
  722. * @param {String} params.core - name of the Solr core requested
  723. * @param {String} params.authorization - value of the authorization header
  724. * @param {String} params.fullPath - full path of the request
  725. * @param {String} params.params - form params
  726. * @param {Boolean} params.secure -
  727. * @param {Boolean} params.bigint -
  728. * @param {http.Agent} [params.agent] -
  729. * @param {Function} callback(err,obj) - a function executed when the Solr server responds or an error occurs
  730. * @param {Error} callback().err
  731. * @param {Object} callback().obj - JSON response sent by the Solr server deserialized
  732. *
  733. * @return {http.ClientRequest}
  734. * @api private
  735. */
  736. function postForm(params,callback){
  737. var headers = {
  738. 'content-type' : 'application/x-www-form-urlencoded; charset=utf-8',
  739. 'content-length': Buffer.byteLength(params.params),
  740. 'accept' : 'application/json; charset=utf-8'
  741. };
  742. if(params.authorization){
  743. headers['authorization'] = params.authorization;
  744. }
  745. var options = {
  746. host : params.host,
  747. port : params.port,
  748. method : 'POST',
  749. headers : headers,
  750. path : params.fullPath
  751. };
  752. if(params.agent !== undefined){
  753. options.agent = params.agent;
  754. }
  755. var request = pickProtocol(params.secure).request(options);
  756. request.on('response', handleJSONResponse(request, params.bigint, callback));
  757. request.on('error',function onError(err){
  758. if (callback) callback(err,null);
  759. });
  760. request.write(params.params);
  761. request.end();
  762. return request;
  763. };
  764. /**
  765. * HTTP GET request. Send a query command to the Solr server (query)
  766. *
  767. * @param {Object} params
  768. * @param {String} params.host - IP address or host address of the Solr server
  769. * @param {Number|String} params.port - port of the Solr server
  770. * @param {String} params.core - name of the Solr core requested
  771. * @param {String} params.authorization - value of the authorization header
  772. * @param {String} params.fullPath - full path of the request, contains query parameters
  773. * @param {Boolean} params.secure -
  774. * @param {Boolean} params.bigint -
  775. * @param {http.Agent} [params.agent] -
  776. * @param {Function} callback(err,obj) - a function executed when the Solr server responds or an error occurs
  777. * @param {Error} callback().err
  778. * @param {Object} callback().obj - JSON response sent by the Solr server deserialized
  779. *
  780. * @return {http.ClientRequest}
  781. * @api private
  782. */
  783. function getJSON(params,callback){
  784. var headers = {
  785. 'accept' : 'application/json; charset=utf-8'
  786. };
  787. var options = {
  788. host : params.host,
  789. port : params.port,
  790. path : params.fullPath,
  791. headers : headers
  792. };
  793. if(params.agent !== undefined){
  794. options.agent = params.agent;
  795. }
  796. if(params.authorization){
  797. var headers = {
  798. 'authorization' : params.authorization
  799. };
  800. options.headers = headers;
  801. }
  802. var request = pickProtocol(params.secure).get(options);
  803. request.on('response', handleJSONResponse(request, params.bigint, callback));
  804. request.on('error',function(err){
  805. if (callback) callback(err,null);
  806. });
  807. return request;
  808. };
  809. /**
  810. * Handle HTTP JSON response from Solr
  811. *
  812. * @param {Function} callback(err,obj) - a function executed when the Solr server responds or an error occurs
  813. * @param {Error} callback().err
  814. * @param {Object} callback().obj - JSON response sent by the Solr server deserialized
  815. *
  816. * @api private
  817. */
  818. function handleJSONResponse(request, bigint, callback){
  819. return function onJSONResponse(response){
  820. var text = '';
  821. var err = null;
  822. var data = null;
  823. // This properly handles multi-byte characters
  824. response.setEncoding('utf-8');
  825. response.on('data',function(chunk){
  826. text += chunk;
  827. });
  828. response.on('end',function(){
  829. if(response.statusCode < 200 || response.statusCode > 299){
  830. err = new SolrError(request,response,text);
  831. if(callback) callback(err,null);
  832. }else{
  833. try{
  834. data = pickJSON(bigint).parse(text);
  835. }catch(error){
  836. err = error;
  837. }finally{
  838. if(callback) callback(err,data);
  839. }
  840. }
  841. });
  842. };
  843. };