tsturzl/express-search

View on GitHub
express-search.js

Summary

Maintainability
A
1 hr
Test Coverage
"use strict";
var QueryDSL = require('./src/queryDSL.js'),
    elasticsearch = require('elasticsearch');


//search constructor
var search = {};

/*    Query String parser     */

//define these regexps ahead of time for reuse
var regexp = /"[^"]*"/g;
var _replace = /@|#|"/g;
var mentionReg = /(^|\W)@\w+/g;
var tagReg = /(^|\W)#\w+/g;

//parse a queryString
function queryString(qs) {

    //phrase filtering
    var matches = _filter(qs, qs.match(regexp));
    qs = matches.qs;
    matches = matches.match;

    //"@" tag filtering
    var mentions = _filter(qs, qs.match(mentionReg));
    qs = mentions.qs;
    mentions = mentions.match;

    //"#" tag filtering
    var tags = _filter(qs, qs.match(tagReg));
    qs = tags.qs;
    tags = tags.match;

    var debug={
        text: qs, //String of search text
        phrases: matches, //Array of phrases
        tags: tags,
        mentions: mentions
    };
    console.log(debug);
    return debug;
}

//regex filter
function _filter(qs, arr) {

    if(arr) {
        var len = arr.length;
        for (var i = 0; i < len; i++) {
            qs = qs.replace(arr[i], '');
            arr[i] = arr[i].trim().replace(_replace, '');
        }
    }
    return {
        qs:qs,
        match:arr
    };
}

//Route factory
search.routeFactory = function (config, cb) {


    //pass client to queryDSL
    config.client=search.client;

    //construct queryDSL
    var dsl = new QueryDSL(config);

    //parse query string
    var qs = queryString(config.qs);

    dsl
        .page(config.page)              //Which page to query
        .project(config.projection);    //project fields(source filtering)

    //sorting
    if (config.sort) {
        dsl
            .sort(config.sort)  //sort order ('asc' or 'desc')
            .by(config.sortBy); //field to sort by
    }

    //build query
    dsl
        .must()                     //results must match
        .match(config.fields)       //this field
            .phrase(qs.phrases)     //with this phrase
        .should()
        .match(config.fields)
            .text(qs.text)
        .must()
        .match(config.mentions)
            .phrase(qs.mentions)
        .must()
        .match(config.tags)
            .text(qs.tags);

    //run query and callback results `cb(err,results)`
    dsl.exec(cb);
};

//configure a route to be searchable
/* config ex:
 {
 index: String,
 type: String,
 tags: Array,
 mentions: Array,
 fields: Array,
 projection: Array
 //these are added from query params
 page: Number,
 qs: String,
 //can be provided or overwritten by query param
 pageSize: Number,
 sort: String,
 sortBy: String
 }
 */
search.setup=function (config) {

    var me = this;

    //return function which can be routed
    return function (req, res, next) {
        if (req.method === 'GET') {
            //make sure these are numbers
            config.page = req.query.page ? Number(req.query.page) : 0;
            config.pageSize = req.query.pageSize ? Number(req.query.pageSize) : config.pageSize;

            //setup sort config
            config.sort = req.query.sort || config.sort;
            config.sortBy = req.query.sortBy || config.sortBy;

            //get queryString
            config.qs = req.query.q;

            //make sure queryString was provided
            if (!config.qs) {
                res.json({ok: 0, error: "Missing queryString(qs)!", _debug: config});
            }
            else {
                me.routeFactory(config, function (err, results) {
                    if (err) {
                        res.json({ok: false, error: err.message});
                    }
                    else {
                        res.json({
                            ok: true,
                            results: results,
                            pager: {
                                page: config.page,
                                pageSize: config.pageSize,
                                count: results.length
                            }
                        });
                    }
                    if (next) {
                        next();
                    }
                });
            }
        }
        else{
            res.send("Cannot " + req.method + " " + req.originalUrl + "\n");
            next();
        }
    };
};

var _export = function(){};

_export.prototype.connect=function(config) {

    search.client = new elasticsearch.Client(config);

    this.setup = function(conf) {
        return search.setup(conf);
    };
};


module.exports = new _export();