Express JS what is the correct way to inject non-field property into Mongoose model before return it to client - express

Using Mongoose model method findOne, I receive a model. Now I want to custom that model before sending it to client, augmenting several attributes into that model.
However, the only way I found for the moment is to turn that model into plain object and augment that object.
I don't know is there any better way doing it?
Here is my lengthy code for that simple purpose:
Topic.find({}).exec(function (err, topics) {
var i, topic_obj, topic_obj_list;
topic_obj_list = [];
if (err) { return next(err); }
for (i = 0; i < topics.length; i++) {
topic_obj = topics[i].toObject();
if (req.user.is_following) {
topic_obj.is_following = true;
} else {
topic_obj.is_following = false;
}
topic_obj_list.push(topic_obj);
}
return res.json(200, topic_obj_list);
});
P/S: I already tried simple solution like: topics[i].is_following = true, bit it didn't work.

You can shorten it to something like this:
Topic.find({}).exec(function (err, topics) {
if (err) { return next(err); }
return res.json(topics.map(function(topic) {
return topic.set(
'is_following',
req.user.is_following ? true : false,
{ strict : false }
);
}));
});
Explanation:
topics.map runs a function on each item of the topics array; the value that is returned from the function ends up in the result returned by map;
with topic.set(FIELD, VALUE, [{ strict : false }]) you can add/overwrite fields of a Mongoose document; when strict is false, the field doesn't have to exist in the schema;

Related

Vue VeeValidate - How to handle exception is custom validation

I have a custom validation in VeeValidate for EU Vat Numbers. It connects to our API, which routes it to the VIES webservice. This webservice is very unstable though, and a lot of errors occur, which results in a 500 response. Right now, I return false when an error has occured, but I was wondering if there was a way to warn the user that something went wrong instead of saying the value is invalid?
Validator.extend('vat', {
getMessage: field => 'The ' + field + ' is invalid.',
validate: async (value) => {
let countryCode = value.substr(0, 2)
let number = value.substr(2, value.length - 2)
try {
const {status, data} = await axios.post('/api/euvat', {countryCode: countryCode, vatNumber: number})
return status === 200 ? data.success : false
} catch (e) {
return false
}
},
}, {immediate: false})
EDIT: Changed code with try-catch.
You can use:
try {
your logic
}
catch(error) {
warn user if API brokes (and maybe inform them to try again)
}
finally {
this is optional (you can for example turn of your loader here)
}
In your case try catch finally block would go into validate method
OK, first of all I don't think that informing user about broken API in a form validation error message is a good idea :-| (I'd use snackbar or something like that ;) )
any way, maybe this will help you out:
I imagine you are extending your form validation in created hook so maybe getting message conditionaly to variable would work. Try this:
created() {
+ let errorOccured = false;
Validator.extend('vat', {
- getMessage: field => 'The ' + field + ' is invalid.',
+ getMessage: field => errorOccured ? `Trouble with API` : `The ${field} is invalid.`,
validate: async (value) => {
let countryCode = value.substr(0, 2)
let number = value.substr(2, value.length - 2)
const {status, data} = await axios.post('/api/euvat', {countryCode: countryCode, vatNumber: number})
+ errorOccured = status !== 200;
return status === 200 ? data.success : false;
},
}, {immediate: false})
}
After searching a lot, I found the best approach to do this. You just have to return an object instead of a boolean with these values:
{
valid: false,
data: { message: 'Some error occured.' }
}
It will override the default message. If you want to return an object with the default message, you can just set the data value to undefined.
Here is a veeValidate v3 version for this:
import { extend } from 'vee-validate';
extend('vat', async function(value) {
const {status, data} = await axios.post('/api/validate-vat', {vat: value})
if (status === 200 && data.valid) {
return true;
}
return 'The {_field_} field must be a valid vat number';
});
This assumes your API Endpoint is returning json: { valid: true } or { valid: false }

Using map to reduce in Gun

I am new to Gun. I have existing code that very effectively reduces an array of objects based on a pattern. I am thinking I should tweak this to run in the context of Gun's .map and return undefined for non-matches. I think I will also have to provide two arguments, one of which is the where clause and the other the properties I want shown on returned objects. I also presume that if I use .on future matches will automagically get spit out! Am I on the right path?
const match = (object,key,value) => {
const type = typeof(value);
if(value && type==="object") {
return Object.keys(value).every(childkey =>
match(object[key],childkey,value[childkey]));
if(type==="function") return value(object[key]);
return object[key]===value;
}
const reduce = (objects,where) => {
const keys = Object.keys(where);
return objects.reduce((accumulator,current) => {
if(keys.every(key => match(current,key,where[key]))) {
accumulator.push(current);
}
return accumulator;
},[]);
}
let rows = reduce([{name: "Joe",address:{city: "Seattle"},age:25},
{name: "Mary",address:{city: "Seattle"},age:16},
{name: "Joe",address:{city: "New York"},age:20}],
{name: () => true,
address: {city: "Seattle"},
age: (age) => age > 10});
// results in
[{name: "Joe",address:{city: "Seattle"},age:25},
{name: "Mary",address:{city: "Seattle"},age:16}]
Further exploration of this resulted in the code below, which is stylistically different, but conforms to the immediate responsive nature of Gun. However, it is unclear how to deal with nested objects. The code below only works for primitives.
const match = (object,key,value) => {
const type = typeof(value);
if(!object || typeof(object)!=="object") return false;
if(value && type==="object") {
const child = gun.get(object[key]["#"]);
for(let key in value) {
const value = {};
child.get(key).val(v => value[key] = v,{wait:0});
if(!match(value,key,value[key])) return;
}
}
if(type==="function") return value(object[key]);
return object[key]===value;
}
const gun = Gun(["http://localhost:8080/gun"]),
users = [{name: "Joe",address:{city: "Seattle"},age:25},
{address:{city: "Seattle"},age:25},
{name: "Mary",address:{city: "Seattle"},age:16},
{name: "Joe",address:{city: "New York"},age:20}];
//gun.get("users").map().put(null);
for(let user of users) {
const object = gun.get(user.name).put(user);
gun.get("users").set(object);
}
gun.get("users").map(user => {
const pattern = {name: (value) => value!=null, age: (age) => age > 20}; //, address: {city: "Seattle"}
for(let key in pattern) {
if(!match(user,key,pattern[key])) return;
}
return user;
}).on(data => console.log(data));
Yes. GUN's .map method does more than what it seems.
Say we have var users = gun.get('users'). We can do:
users.map() with no callback acts like a forEach because the default callback is to return the data as-is.
users.map(user => user.age * 2) with a callback, it lets you transform the data like you would expect from a map, except where:
users.map(function(){ return }) if you return undefined, it will filter out that record.
WARNING: As of the current time, .map(transform) function is currently experimental and my have bugs with it. Please try it and report any you find.
Now we can combine it with some other methods, to get some cool behavior:
users.map().on(cb) will get current and future users as they are added to the table, and gets notified for updates on each of those users.
users.map().val(cb) will get current and future users as they are added to the table, but only gets each one once.
users.val().map().on(cb) gets only the current users (not future), but gets the updates to those users.
users.val().map().val(cb) gets only the current users (not future), and only gets them once.
So yes, you are on the right track. For instance, I have a test in gun core that does this:
list.map(user => user.age === 27? user.name + "thezombie" : u).on(function(data){
// verify
});
list.set({name: 'alice', age: 27});
list.set({name: 'bob', age: 27});
list.set({name: 'carl', age: 29});
list.set({name: 'dave', age: 25});
This creates a live map that filters the results and locally (view only) transforms the data.
In the future, this is how the SQL and MongoDB Mango query extensions will work for gun.
Note: GUN only loads the property you request on an object/node, so it is bandwidth efficient. If we do users.map().get('age') it will only load the age value on every user, nothing else.
So internally, you can do some efficient checks, and if all your conditionals match, only /then/ load the entire object. Additionally, there are two other options: (1) you can use an in-memory version of gun to create server-side request-response patterns, so you can have server-side filtering/querying that is efficient. (2) if you become an adapter developer and learn the simple wire spec and then write your own custom query language extensions!
Anything else? Hit me up! More than happy to answer.
Edit: My reply in the comments, comments apparently can't have code. Here is pseudo-code of how to "build up" more complex queries, which will be similar to how SQL/Mango query extensions will work:
mutli-value & nested value matching can be "built up" from this as the base, but yes, you are right, until we have SQL/Mango query examples, there isn't a simple/immediate "out of the box" example. This is pseudo code, but should get the idea across:
```
Gun.chain.match = function(query, cb){
var gun = this;
var fields = Object.keys(query);
var check = {};
fields.forEach(function(field){
check[field] = true;
gun.get(field).val(function(val){
if(val !== query[field]){ return }
check[field] = false;
//all checks done?
cb(results)
});
});
return gun;
}
```
Solution, the trick is to use map and not val:
Gun.chain.match = function(pattern,cb) {
let node = this,
passed = true,
keys = Object.keys(pattern);
keys.every(key => {
const test = pattern[key],
type = typeof(test);
if(test && type==="object") {
node.get(key).match(test);
} else if(type==="function") {
node.get(key).map(value => {
if(test(value[key])) {
return value;
} else {
passed = false;
}
});
} else {
node.get(key).map(value => {
if(value[key]===test) {
return value;
} else {
passed = false;
}
});
}
return passed;
});
if(passed && cb) this.val(value => cb(value))
return this;
}
const gun = new Gun();
gun.get("Joe").put({name:"Joe",address:{city:"Seattle"},age:20});
gun.get("Joe").match({age: value => value > 15,address:{ city: "Seattle"}},value => console.log("cb1",value));

DataTables != search

var table = $('.dataTables-huntQueue').DataTable();
if($('#switch-mine').is(':checked')) {
table.search( 'closed' ).draw();
} else {
table.search( '' ).draw();
}
The above works great when all I'm looking for is the 'closed' value. But what I really want is anything that is NOT closed. Is there a ! operator here that I'm missing?
You can use a regular expression :
table.search('^((?!(closed)).)*$', true, false, true).draw();
The meaning of the params is - see the documentation for search() :
rows which not have columns containing "closed"
It is a regular expression
no "smart" filtering, in this case it doesnt seem to matter though
case insensitive
The downside by using a regex through the API is that you will get an ugly meaningless ^((?!(closed)).)*$ value in the search / filter box. Instead you can make a custom filter that does exactly the same :
function notSearch(notString) {
notString = notString.toLowerCase();
$.fn.dataTable.ext.search.push(
function(settings, data, dataIndex) {
for (var i=0;i<data.length;i++) {
if (data[i].toLowerCase().indexOf(notString)>-1) return false;
}
return true;
}
)
table.draw();
$.fn.dataTable.ext.search.pop();
}
Then call it by
notSearch('closed');
You can even register the above custom filter so it appears as part of the native API :
jQuery.fn.dataTable.Api.register('notSearch()', function(notString) {
notString = notString.toLowerCase();
$.fn.dataTable.ext.search.push(
function(settings, data, dataIndex) {
for (var i=0;i<data.length;i++) {
if (data[i].toLowerCase().indexOf(notString)>-1) return false;
}
return true;
}
)
table.draw();
$.fn.dataTable.ext.search.pop();
})
table.rows().notSearch('closed');

ExtJS Callback Functions Example

I'm a newbie at ExtJs and I'm struggling to figure out how to use callback functions in ExtJs. The ExtJs version I'm using is 4.2.1
Basically I want to chain the execution of 2 functions:
func1: function() {
}
func2: function() {
}
so that func2() only starts executing after func1() completes.
From what I've read so far, I need to use callback function, but for the life of me I cannot get it.
Here Is my code:
filter: function (filters, value) {
if (Ext.isString(filters)) {
filters = {
property: filters,
value: value
};
}
var me = this,
decoded = me.decodeFilters(filters),
i = 0,
length = decoded.length;
for (; i < length; i++) {
me.filters.replace(decoded[i]);
}
Ext.Array.each(me.filters.items, function (filter) {
Ext.Object.each(me.tree.nodeHash, function (key, node) {
if (filter.filterFn) {
if (!filter.filterFn(node)) node.remove();
} else {
if (node.data[filter.property] != filter.value) node.remove();
}
});
});
me.hasFilter = true;
console.log(me);
},
clearFilter: function() {
var me = this;
me.filters.clear();
me.hasFilter = false;
me.load();
},
isFiltered: function() {
return this.hasFilter;
},
filterNavAdminSTByUserName: function (nameValue) {
this.clearFilter();
this.filter([{
property: 'userName',
value: nameValue
}]);
}
My problem is that this.filter() gets executed before this.clearFilter(); How do I force this.filter() to execute only after this.clearFilter() completes?
Thanks in advance!
After some soul searching I've finally figured out how callback functions work.
So here is the solution:
clearAndFilter: function (nameValue) {
var me = this;
me.filters.clear();
me.hasFilter = false;
me.load({
scope: me,
callback: function () {
// filter the store
me.filter('userName', nameValue);
}
});
},
filterNavAdminSTByUserName: function (nameValue) {
this.clearAndFilter(nameValue);
}
Feels good to answer to my first ever post here!

using contains instead of stringStartsWith knockout js

I have the folliwng on my model:
self.filteredItems = ko.computed(function () {
var filter = this.filter().toLowerCase();
if (!filter) {
return this.sites();
} else {
return ko.utils.arrayFilter(this.sites(), function (item) {
return ko.utils.stringStartsWith(item.Name().toLowerCase(), filter);
});
}
}, self);
I use it for a search on my page but rather than stringStartsWith I'd like some sort of .contains instead so I get results where my searchterm is contained anywhere in the string rather than just at the beginning.
I imagine this must be a pretty common request but couldnt find anything obvious.
Any suggestion?
You can use simply the string.indexOf method to check for "string contains":
self.filteredItems = ko.computed(function () {
var filter = this.filter().toLowerCase();
if (!filter) {
return this.sites();
} else {
return ko.utils.arrayFilter(this.sites(), function (item) {
return item.Name().toLowerCase().indexOf(filter) !== -1;
});
}
}, self);