308 lines
9 KiB
JavaScript
308 lines
9 KiB
JavaScript
/*********************************************************
|
|
* Simple Node.js module for using the Bing Search API *
|
|
*********************************************************/
|
|
|
|
// Require dependencies
|
|
var request = require('request'),
|
|
url = require('url'),
|
|
_ = require('underscore'),
|
|
qs = require('querystring');
|
|
|
|
/**
|
|
* @param {Object} options Options to all Bing calls, allows overriding of
|
|
* rootUri, accKey (Bing API key), userAgent, reqTimeout
|
|
* @returns {Bing}
|
|
* @constructor
|
|
*/
|
|
var Bing = function (options) {
|
|
|
|
if (!(this instanceof Bing)) return new Bing(options);
|
|
|
|
var defaults = {
|
|
|
|
//Bing Search API URI
|
|
rootUri: "https://api.cognitive.microsoft.com/bing/v5.0/",
|
|
|
|
//Account Key
|
|
accKey: null,
|
|
|
|
//Bing UserAgent
|
|
userAgent: 'Bing Search Client for Node.js',
|
|
|
|
//Request Timeout
|
|
reqTimeout: 5000
|
|
};
|
|
|
|
// Merge options passed in with defaults
|
|
this.options = _.extend(defaults, options);
|
|
|
|
|
|
// Performs the search request for the given vertical
|
|
this.searchVertical = function (query, vertical, options, callback) {
|
|
|
|
if (typeof options === 'function') {
|
|
callback = options;
|
|
}
|
|
if (typeof callback != 'function') {
|
|
throw "Error: Callback function required!";
|
|
}
|
|
|
|
// Create a copy of the options, to avoid permanent overwrites
|
|
var opts = JSON.parse(JSON.stringify(this.options));
|
|
|
|
if (typeof options === 'object') {
|
|
_.extend(opts, options);
|
|
}
|
|
|
|
// Map options and supported variation for old versions with the new names
|
|
var opMap = {
|
|
top: 'count',
|
|
skip: 'offset',
|
|
videosortby: 'videoSortBy',
|
|
videofilters: 'videoFilters',
|
|
adult: 'safeSearch',
|
|
safesearch: 'safeSearch',
|
|
market: 'mkt'
|
|
}
|
|
Object.keys(opts).forEach(function (opt) {
|
|
var newOp = opMap[opt]
|
|
if (newOp) {
|
|
opts[newOp] = opts[newOp] || opts[opt]
|
|
delete opts[opt]
|
|
}
|
|
})
|
|
|
|
var reqUri = opts.rootUri + vertical + "?q=" + query
|
|
|
|
// Filter the no-query options (accKey, rootUri, userAgent)
|
|
var queryOpts = {}
|
|
|
|
Object.keys(opts).forEach(function (opt) {
|
|
if (!~Object.keys(defaults).indexOf(opt)) {
|
|
queryOpts[opt] = opts[opt]
|
|
}
|
|
})
|
|
|
|
var qStr = qs.stringify(queryOpts);
|
|
|
|
reqUri += qStr ? '&' + qStr : '';
|
|
|
|
reqUri = encodeURI(reqUri);
|
|
|
|
request({
|
|
uri: reqUri,
|
|
method: opts.method || "GET",
|
|
headers: {
|
|
"User-Agent": opts.userAgent,
|
|
"Ocp-Apim-Subscription-Key": opts.accKey
|
|
},
|
|
timeout: opts.reqTimeout,
|
|
pool: {
|
|
maxSockets: opts.maxSockets ? opts.maxSockets : Infinity
|
|
}
|
|
|
|
}, function (err, res, body) {
|
|
|
|
if (res && res.statusCode !== 200) {
|
|
err = new Error(body);
|
|
} else {
|
|
|
|
// Parse body, if body
|
|
body = typeof body === 'string' ? JSON.parse(body) : body;
|
|
}
|
|
|
|
callback(err, res, body);
|
|
});
|
|
};
|
|
|
|
};
|
|
|
|
|
|
/**
|
|
* @callback requestCallback
|
|
* @param {String} error Error evaluates to true when an error has occurred.
|
|
* @param {Object} response Response object from the Bing call.
|
|
* @param {Object} body JSON of the response.
|
|
*/
|
|
|
|
|
|
/**
|
|
* Performs a Bing search in the Web vertical.
|
|
*
|
|
* @param {String} query Query term to search for.
|
|
*
|
|
* @param {Object} options Options to command, allows overriding
|
|
* of rootUri, accKey (Bing API key),
|
|
* userAgent, reqTimeout, count, offset
|
|
*
|
|
* @param {requestCallback} callback Callback called with (potentially
|
|
* json-parsed) response.
|
|
*
|
|
* @function
|
|
*/
|
|
Bing.prototype.web = function (query, options, callback) {
|
|
this.searchVertical(query, "search", options, callback);
|
|
};
|
|
|
|
// Alias Bing.search to Bing.web
|
|
// Note: Keep this for compatibility with older versions
|
|
Bing.prototype.search = Bing.prototype.web;
|
|
|
|
|
|
/**
|
|
* Performs a Bing search in the Composite vertical.
|
|
*
|
|
* @param {String} query Query term to search for.
|
|
*
|
|
* @param {Object} options Options to command, allows overriding
|
|
* of rootUri, accKey (Bing API key),
|
|
* userAgent, reqTimeout, count, offset,
|
|
*
|
|
* @param {requestCallback} callback Callback called with (potentially
|
|
* json-parsed) response.
|
|
* @function
|
|
*/
|
|
Bing.prototype.composite = function (query, options, callback) {
|
|
this.searchVertical(query, "search", options, callback);
|
|
};
|
|
|
|
/**
|
|
* Performs a Bing search in the News vertical.
|
|
*
|
|
* @param {String} query Query term to search for.
|
|
*
|
|
* @param {Object} options Options to command, allows overriding
|
|
* of rootUri, accKey (Bing API key),
|
|
* userAgent, reqTimeout, count, offset,
|
|
*
|
|
* @param {requestCallback} callback Callback called with (potentially
|
|
* json-parsed) response.
|
|
* @function
|
|
*/
|
|
Bing.prototype.news = function (query, options, callback) {
|
|
this.searchVertical(query, "news/search", options, callback);
|
|
};
|
|
|
|
/**
|
|
* Performs a Bing search in the Video vertical.
|
|
*
|
|
* @param {String} query Query term to search for.
|
|
*
|
|
* @param {Object} options Options to command, allows overriding
|
|
* of rootUri, accKey (Bing API key),
|
|
* userAgent, reqTimeout, count, offset,
|
|
*
|
|
* @param {requestCallback} callback Callback called with (potentially
|
|
* json-parsed) response.
|
|
* @function
|
|
*/
|
|
Bing.prototype.video = function (query, options, callback) {
|
|
if (options && typeof options === 'object') {
|
|
|
|
//compatibility with older versions
|
|
options.videoFilters = options.videoFilters || options.videofilters || '';
|
|
|
|
if (options.videoFilters && typeof options.videoFilters === 'object') {
|
|
|
|
var filterQuery = Object.keys(options.videoFilters)
|
|
.map(function(key){
|
|
return capitalise(key) + ':'
|
|
+ capitalise(options.videoFilters[key]);
|
|
})
|
|
.join('+');
|
|
|
|
options.videoFilters = filterQuery;
|
|
}
|
|
}
|
|
this.searchVertical(query, "videos/search", options, callback);
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* Performs a Bing search in the Images vertical.
|
|
*
|
|
* @param {String} query Query term to search for.
|
|
*
|
|
* @param {Object} options Options to command, allows overriding of
|
|
* rootUri, accKey (Bing API key),
|
|
* userAgent, reqTimeout, count, offset,
|
|
* imageFilters
|
|
*
|
|
* @param {requestCallback} callback Callback called with (potentially
|
|
* json-parsed) response.
|
|
* @function
|
|
*/
|
|
Bing.prototype.images = function (query, options, callback) {
|
|
if (options && typeof options === 'object') {
|
|
|
|
//compatibility with older versions
|
|
options.imageFilters = options.imageFilters || options.imagefilters || '';
|
|
|
|
if (options.imageFilters && typeof options.imageFilters === 'object') {
|
|
|
|
var filterQuery = Object.keys(options.imageFilters)
|
|
.map(function(key){
|
|
return capitalise(key) + ':'
|
|
+ capitalise(options.imageFilters[key]);
|
|
})
|
|
.join('+');
|
|
|
|
options.imageFilters = filterQuery;
|
|
}
|
|
}
|
|
this.searchVertical(query, "images/search", options, callback);
|
|
};
|
|
|
|
|
|
/**
|
|
* Performs a Bing search in the Related Search vertical.
|
|
*
|
|
* @param {String} query Query term to search for.
|
|
*
|
|
* @param {Object} options Options to command, allows overriding
|
|
* of rootUri, accKey (Bing API key),
|
|
* userAgent, reqTimeout, count, offset,
|
|
*
|
|
* @param {requestCallback} callback Callback called with (potentially
|
|
* json-parsed) response.
|
|
* @function
|
|
*/
|
|
Bing.prototype.relatedSearch = function (query, options, callback) {
|
|
this.searchVertical(query, "search", options, callback);
|
|
};
|
|
|
|
|
|
/**
|
|
* Performs a Bing search in the Spelling Suggestions vertical.
|
|
*
|
|
* @param {String} query Query term to search for.
|
|
*
|
|
* @param {Object} options Options to command, allows overriding
|
|
* of rootUri, accKey (Bing API key),
|
|
* userAgent, reqTimeout, count, offset,
|
|
*
|
|
* @param {requestCallback} callback Callback called with (potentially
|
|
* json-parsed) response.
|
|
* @function
|
|
*/
|
|
Bing.prototype.spelling = function (query, options, callback) {
|
|
this.searchVertical(query, "spellcheck", options, callback);
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* Capitalises the first word of the passed string
|
|
*
|
|
* @param {String} s String to be capitalised
|
|
*
|
|
* @function
|
|
*/
|
|
function capitalise(s) {
|
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
}
|
|
|
|
|
|
module.exports = Bing;
|
|
|