0

Scenario: I want to have a searchbox that takes well-formed MongoDB db.collection.find() parameters in the same form as the MongoDB console, and will display the result on the webpage.

I have already the logic that works when it comes to a parameter, that I know. Eg. getting document by ID:

var query = {};
var selector = "id";
query[selector] = Number(id);
collection.find(
    query, {},
    function(e, docs) {
        if (e) { ... }
        if (docs != null && docs.length != 0) {
            res.json(docs);
        } else { ... }
    });

However, what to do if I want to have multiple parameters that I don't know beforehand on the bakcend?

What to look out for when sending the client GET request?

Thanks.

EDIT:

Perhaps a better way to explain the searchbox functionality would be to compare it to the formula editor in Excel. I would like to be able to insert 'search formulas' and get the data results.

EDIT2:

I am using Express for the project.

Searchbox value that should work:

{"pocet_nabidek":2}

This is the approach I took, based on @x_maras suggestions:

Client side

// send AJAX request when the search field changes
$('#searchField').keyup(function() {
    clearTimeout(timer);
    var timer = setTimeout(function() {

            //call your function here
            q = $('#searchField').val();
            if (q.length != 0 && q != undefined) {

                var payload = "/api/zakazka?q="+replaceURLParam(q);
                $.getJSON(payload, function(data){

                    console.log(data);

                });

            }

        }, 500) // delay
});

Server side

var express = require('express');
var router = express.Router();

// ... more code ...

router.get('/zakazka', function(req, res) {

    var pagesize = req.query["pagesize"];
    var offset = req.query["offset"];
    var q = req.query["q"];

    if (q != undefined || q != null) {

        console.log("got a query passed on!");
        q = decodeURI(q);
        q = JSON.parse(q);
        return
    } 

    var collection = req.db.get('zakazky');

    collection.find(q, {
        limit: pagesize,
        skip: offset
    }, function(e, docs) {
        if (e) {
            console.log(`Error: X doesn't seem to exist`);
        }
        if (docs != null && docs.length != 0) {
            res.json(docs);
        } else {
            res.writeHead(404, {
                "Content-type": "text/plain"
            });
            res.end(`Error: X doesn't seem to exist`);
        }
    });

});

If I modify the code as per below, the query works

// This actually works: 
// var q = {};
// var selector = "pocet_nabidek";
// q[selector] = Number(2);

This is a sample of a document we're querying

{
    "_id": ObjectId("568d91396912101c1007ab4e"),
    "cena": 1636363,
    "cena_celkem": 1500000,
    "cena_dopocitano": false,
    "created": "2015-04-07T13:45:10.420739",
    "datum_zadani": "2015-02-16",
    "dodavatel": "/api/v1/dodavatel/381836/",
    "druh_rizeni": "/api/v1/druh_rizeni/1116/",
    "id": 1312587,
    "modified": "2015-04-18T14:22:10.765733",
    "nazev": "Pohostinství",
    "pocet_nabidek": 2,
    "podporeno_eu": true,
    "popis": "Kurzy v oblasti pohostinství (formou profesní kvalifikace)",
    "ramcova_smlouva": true,
    "resource_uri": "/api/v1/zakazka/1312587/",
    "skupina": "490648-ISVZUS_2011",
    "typ_zakazky": "/api/v1/typ_zakazky/193/",
    "zadavatel": "/api/v1/zadavatel/131528/",
    "zdroj": "http://www.vestnikverejnychzakazek.cz/en/Form/Display/568547",
    "zdroj_nazev": "isvzus.cz",
    "cpv": ["80000000-4", "80400000-8", "", "", ""],
    "predpokladana_hodnota": "1 500 000,00"
}

1 Answer 1

1

Let's assume that you are making the following GET request

GET /v1/myendpoint?key1=value1&key2=value2

You could create a javascript object from the querystring like the following

{key1: 'value1', key2: 'value2'}

and use it in a mongo query

var cursor = collection.find({key1: 'value1', key2: 'value2'})

Updated solution based on the server side snippet

var express = require('express');
var router = express.Router();

// ... more code ...

router.get('/zakazka', function(req, res, next) {
    var query = req.query;
    var q = query.q;

    if (q) {
        console.log('got a query passed on!');
        q = decodeURI(q);
        q = JSON.parse(q);
        // You don't need a return here
    }

    req.db.collection('zakazky').find(q, {
        limit: query.pagesize,
        skip: query.offset
    }, function (err, docs) {
        if (err) {
            console.log('Error: ', err);
            /**
             * It would be good to use next here with a generic error handler middleware:
             * However, you don't have the error handling middleware and the following code it
             * won't work for you, therefore it is commented.
             *
             * var dbError = new Error('Database Error');
             * dbError.status = 500;
             * dbError.details = err;
             * return next(dbError);
             */
        }

        if (docs && docs.length !== 0) {
            return res.status(200).json(docs);
        }

        /**
         * You could use next here and pass the error to a generic error handler.
         * However, you don't have the error handling middleware and the following code it
         * won't work for you, therefore it is commented.
         *
         * var reqError = new Error('Error: X doesn\'t seem to exist');
         * reqError.status = 404;
         * next(reqError);
         */
        res.status(404).send('Error: X doesn\'t seem to exist');
    });

});
Sign up to request clarification or add additional context in comments.

10 Comments

Thanks for replying. How would this approach work when trying to do a textbook example query such as: db.collection.find( { field: { $gt: value1, $lt: value2 } } ); so then this is what I would insert into the input field: { field: { $gt: value1, $lt: value2 } } Am I naive to think it would be easier to pass the whole string in the URL parameter, instead breaking this down into ?key1=value1&key2=value2 ?
It all depends in what you precisely want to do. I would do all the logic on the server side. When you are submitting a form you know the fields (keys) what you don't know is the values. So check your form keys if they are there and construct your query appropriately.
Hm. I'd type { field: { $gt: value1, $lt: value2 } } into a field and thus run db.collection.find( { field: { $gt: value1, $lt: value2 } } ); in DB. Problem is, it's not a discreet set of keys I'm going to work with. It can by anything. From simple queries like get by "id" queries like:: - price bigger than X - document is about a transaction from the past 3 years - the transaction the document talks about happened between parties A,B,C. In that case the query string gets longer, more complicated and once I add or remove another thing, the logic can't break it up and digest it. ... ?
You can do that. However by allowing everything you might possibly expose important information to anyone that figures out how to query your system. Especially, if you contain privacy information in your db.
Thanks of the warning, I am aware of the security risk. I still want to go ahead. How to do that then?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.