Making Mongoose/MongoDB query helper plugin return zero results - node.js

I'm developing a Mongoose Query Helper plugin that provides the chainable method .search(query). On certain conditions, I want the query to return zero results, no matter how the other methods in the query builder chain behave. Turns out this isn't so easy as I have assumed.
Basically, I have the following code:
schema.query.search = function search(query) {
if ("query is invalid") {
// return no results => no easy way to achieve that?
}
return this.find(query);
};
Now, I want SomeModel.find({}).search(someQuery).exec() to return no results in case the query is invalid. I first tried to return this.limit(0), but turns out a limit of 0 is equivalent to setting no limit.
As a temporary solution, I do return this.find({ nonExistingField: 'something' }) which always results in no results, but this does seem a bit awkward and is probably also not so optimal in terms of performance as it triggers a search when no search is needed.
Thanks in advance for your help!

Related

mongo db virtual get function how to return document

details:mongodb,mongoose,nodejs
example :
schema.virtual(''name').get(function() => {
return this.anything;
})
how the this key work to point document?
Is get method return document?
Might be a typo in the post but your string starts with two quotes.
You are mixing up the syntax for regular functions and arrow functions.
Do this:
schema.virtual('name').get(function() {
return this.anything;
});
You cannot use an arrow function in this situation anyway since arrow functions do not allow the rebinding of the this context.

Using chain validation to check existence of optional fields with Express Validator

I am trying to check for the existence of an optional field in an API request, and if that field exists, perform a nested validation to check if two other fields (one or the other, or implicitly both) exist inside of it. I am using Express Validator to try and accomplish this task.
// Sample request body
{
<...>
thresholds: {
min: 3,
max: 5
}
}
// (Attempted) validation chain
check('thresholds').optional()
.custom( innerBody => {
console.log('THRESHOLDS', innerBody);
oneOf([
check('innerBody.min').optional(),
check('innerBody.max').optional()
]);
})
The above snippet is part of a larger validation chain I'm validating the full request body on. I also tried removing the innerBody. string from the inner checks but still no luck. I am console.loging the threshold body, and it prints out correctly, however I still get a validation error, when I'm trying to get my integration test to pass:
{"name":"ValidationError","message":"ValidationError: Validation failed","errors":[{"location":"body","param":"thresholds","value":{"min":3,"max":5},"msg":"Invalid value"}]}
I am relatively new to Express Validator so if I'm chaining the validation wrong/not using oneOf correctly or something would love some pointers!
Thanks
Looks like the .custom function needs to return a Promise. Answer below:
.custom(innerBody => {
if (!(innerBody.min) || !(innerBody.max)) return Promise.reject('Missing min or max');
return Promise.resolve();
})
Remember: Always return a boolean value from the callback of .custom()
function. Otherwise your validation might not work as desired.
Source: Custom validation guide
In general, you might have needs in use of Promises if you deal with asynchronous .custom() function. Then you'll be obligated to return Promise.resolve() / Promise.reject() for correct validator behaviour.
Source: SO answer

My function could return a promise or value, how to avoid it? [duplicate]

This question already has answers here:
How to maintain a promise-like API in this case?
(2 answers)
Closed 6 years ago.
I think it's bad practice to have different return types. So, this is my function, and I want it to always return a promise.
I tried to simplify the scenario. Let's say I have two lines of products (line X and line Y) and the way I'm retrieving a product by name, from each line is totally different from the other.
Please also note that ideally, I wanted to have a function to "or" two promises and return whichever that resolves successfully. But I couldn't think of a better way to achieve this!
ProductService.findProductByName = function findProductByName(name) {
return LineXService.findOneByName(name) // promise
.then(function _returnProduct(product) {
return product
? product // value
: LineYService.findOneByName(name)); // promise
})
};
You're right that returning a consistent thing is better than an ambiguous result. Normally you can cast an arbitrary value into a promise easily enough. For example using resolve:
if (product) {
return Promise.resolve(product);
}
return LineYService.findOneByName(name);
It's worth noting, as Kevin B observes, that this is irrelevant in this particular case since your outer function always returns a promise. The need to promisify a value is only a concern if this is not part of a surrounding promise.

Passing parameters to db.query with arangojs

I'm having problems sending parameters with the ArangoJS library and was wondering if anyone could help.
With the example below, it is possible to execute db.query if parameter values are in the query, but as soon as I try to use bindVars I get silent errors and I can't extract any error details.
var db = require('arangojs')("http://127.0.0.1:8529");
/*
The '_system' database contains a collection called 'test' that contains one document:
{
"a": 1,
"b": 2
}
*/
// This works
db.query('FOR t IN test FILTER t.a == 1 RETURN t')
.then((cursor) => {
cursor.all()
.then(vals => {
console.log("\nNo bindVars");
console.log(vals);
});
});
// This does not work
db.query("FOR t IN #first FILTER t.a == #second RETURN t", { first: "test", second: 1 })
.then((cursor) => {
cursor.all()
.then(vals => {
console.log("\nUsing bindVars");
console.log(vals);
});
});
I'm new to Node.js and ArangoDB and would love to be able to use properly parameterized queries.
I'm also assuming that this use of parameters protects you from SQL Injection style attacks?
Thanks!
The problem isn't with the JavaScript driver or Node, the problem is with the query itself:
FOR t IN #first FILTER t.a == #second RETURN t
In AQL collection names can't be injected with ordinary bind parameters. This is because you're not actually trying to use the parameter as a string value but to refer to a collection with that name. To quote the AQL documentation:
A special type of bind parameter exists for injecting collection names. This type of bind parameter has a name prefixed with an additional # symbol (thus when using the bind parameter in a query, two # symbols must be used).
In other words, in AQL it has to be called ##first (instead of #first) and in the bind parameters argument to db.query it has to be called #first (instead of just first).
When using arangojs it's actually possible to avoid this entirely by using the aqlQuery template handler:
var aqlQuery = require('arangojs').aqlQuery;
var first = db.collection('test');
var second = 1;
db.query(aqlQuery`
FOR t IN ${first}
FILTER t.a == ${second}
RETURN t
`).then(
cursor => cursor.all()
).then(vals => {
console.log('Using aqlQuery');
console.log(vals);
});
This way you don't have to think about bind parameter syntax when writing queries and can write more complex queries without having to mess with extremely long strings. Note that it will recognize arangojs collection instances and handle them accordingly. Using a string instead of a collection instance would result in the same problems as in your example.
Additionally note that the template handler also exists in the arangosh shell and in ArangoDB itself (e.g. when using Foxx).

Reduce output must shrink more rapidly, what is this error about?

An user can post multiple comments in a thread, and I try to get list of threads (distinct) that an user has make comment to it, like :-
// comment table (relation table)
id, thread_id, user_id
select comment.thread_id, count(*)
from user
inner join comment on user.id=comment.user_id
where user.id = ?
group by comment.thread_id;
This is pretty easy in MySQL.
But to convert to couchdb :-
// map
function(doc)
{
emit(doc.user_id, doc.thread_id);
}
// reduce
function (key, thread_id)
{
return thread_id;
}
If I using the above map function, I will hit into an error like :-
"error": "reduce_overflow_error",
"reason": "Reduce output must shrink more rapidly: Current output: ...
I think I have applied the reduce function in wrong manner.
If using another way, like :-
// map
function (doc)
{
emit([doc.user_id, doc.thread_id], 1);
}
// reduce
function(keys, values)
{
return sum(values);
}
The group=true result is look exactly what mysql group-by does.
However, I'm unable to get ALL the list of thread by an user (given I only have the user_id during query time)
Third way, I can discard use of map reduce, and directly apply :-
emit(doc.user_id, doc.thread_id);
And do an PHP array like
foreach ( ... )
{
$threads[$thread_id] = TRUE;
}
array_keys($threads);
However, this is quite bloated and less efficient.
Second method look more accurate :-
key=[user_id, *] <-- it does not work, believe only work on exact match
key=[user_id, thread_id] <-- return one row
Is there a way to get all result without knowing the thread_id ?
(ps: I new to couchdb, and I might have describe the scenario in a bad manner)
Some reference I gotten via #jasonsmith :- http://guide.couchdb.org/draft/cookbook.html
As a rule of thumb, the reduce function should reduce to a single scalar value. That is, an integer; a string; or a small, fixed-size list or object that includes an aggregated value (or values) from the values argument. It should never just return values or similar. CouchDB will give you a warning if you try to use reduce “the wrong way”:
Follow closely to what this docs saying :-
http://wiki.apache.org/couchdb/View_Snippets#Generating_a_list_of_unique_values
// map
function(doc)
{
emit([doc.user_id, doc.thread_id], null);
}
// reduce
function (keys, values)
{
return null;
}
Query :-
?startkey=["$uid"]&endkey=["$uid",{}]&group=true
And the result now is accurate,
so the problem is lied on the reduce function and how the query being construct.

Resources