filter mongoose documents based on the specific fields and attributes - angularjs

I'm developing a website using the MEAN stack (MongoDB/Express/Angular/Node).
I have a product schema with 12 different fields/properties, including size, color, brand, model, etc. What is the best and most efficient way to filter products, in Angular or on the server-side?And how can i chain the results if the client had selected more than one property?What would that look like?

Assuming there will be a lot of products, it will be too much to download to the client in order to filter using Angular. It doesn't scale very well. As the list of products gets bigger and bigger, it will be less and less performant. The better way would, generally, be to let MongoDB do the filtering for you. It's very fast.
But, you can control the filtering from Angular by posting to the server the filtering term you want on the endpoint used for that method of filtering, for example, using the http module
http.post('/api/filter/' + methodOfFiltering, { 'term': termtoFilterBy }, function(dataReturned) {
// use dataReturned to do something with the data
});
Put this in an angular service method, so you can inject it into any of your controllers/components.
Create an endpoint that will use the method and the keyword in the mongoose query. I'm assuming that you're using Express for your server routes.
app.post('/api/filter/:method', function(req, res) {
var method = req.params.method;
var termToFilterBy = req.body.term;
productSchema.find({method: termToFilterBy}, function(err, products) {
res.send(products);
});
});
Let me know if this helps.

Related

Access OData JSON model mock file vs SAP backend

I have an application that works for test purposes with local JSON mock data. The oData Object contains arrays with values and the application works as desired.
Now we are switching from the local mock data File to data consumption with an oData service from a SAP backend system.
Here I get the data in JSON Objects and not all the functionality works as desired (example filter functions).
Can somebody share some thoughts about JSON Objects and Arrays with me?
And how can I get the data from the backend system in an array instead of an object?
In the mock data version I do this to define my model:
this._oModel = new JSONModel(jQuery.sap.getModulePath("myApplication", "/localService/mockdata/nodesSet.json"));
In the oData Version the model is defined in manifest.json:
this._oModel = this.getOwnerComponent().getModel();
Note: I am aware of the different names of the entities (example: nodes vs nodesSet) and this is not part of the problem.
Thanks!
One simple way would be not to create the model in the Manifest and do an explicit read in the controller as:
var oModel = new sap.ui.model.odata.v2.ODataModel(url, true);
that=this;
oModel.read( "/Products", {
urlParameters: {
"$skip": 0,
"$top": 50
},
success: function( oData) {
//here oData shall have an array of objects "results"
**-------------Set the model here using this array -> results ------**
},
error: function(oError) {
alert("error");
}
});
I am however not proud of this solution and will check if I can comment better.

Meteor with query on publication is not reactive

I have a problem with a meteor publication not being reactive when using a query inside it.
Let's say I have many files, and each file has many projects, so I can go to the route:
http://localhost:3000/file/:file_id/projects
And I would like to both display the projects of the selected file and add new projects to it.
I am currently using angularjs, so the controller would look something like this:
class ProjectsCtrl {
//some setup
constructor($scope, $reactive, $stateParams){
'ngInject'
$reactive(this).attach($scope)
let ctrl = this
//retrieve current file id
ctrl.file_id = Number($stateParams.file)
//get info from DB and save it in a property of the controller
ctrl.subscribe('projects', function(){return [ctrl.file_id]}, function(){
ctrl.projects = Projects.find({file_id: ctrl.file_id}).fetch()
})
//function to add a new project
ctrl.addProject = function(){
if(ctrl.projectName){
Meteor.call('projects.insert', {name: ctrl.projectName, file_id: ctrl.file_id }, function(error, result){
if(error){
console.log(error)
}else{
console.log(result)
}
})
}
}
}
}
The publication looks something like this:
Meteor.publish('projects', function(file_id){
return Projects.find({file_id: file_id})
})
The problem is that, if I insert a new project to the DB the subscription doesn't run again, I mean the array stays the same instead of displaying the new projects I am adding.
I got many problems with this as I thought that meteor would work something like: "Oh there is a new project, let's re run the query and see if the publication change, if it does, let's return the new matching documents"... but no.
I have not found a problem similar to mine as every question regardind querys inside the publication is about how to reactively change the query (the file_id in this case) but that is not the problem here as I don't change the file_id unless I go to another route, and that triggers a new subscription.
My current solution is to expose the complete collection of projects and make the query using minimongo, but I don't know if it is a good workaround (many projects exposed uses too much memory of the browser, minimongo is not as fast as mongo... etc, I don't really know).
Your issue is that the Meteor.subscribe call doesn't know that file_id has changed. There's no reactive relationship between that argument and executing the subscription.
To fix this, whenever you are passing criteria in publish-subscribe, you must write a subscription of Collection inside a tracker.
To know more about trackers, Click here.
While I'm unsure how to do this in Angular, consider this simple Blaze template as an example:
Template.Name.onCreated(function(){
this.autorun(() => {
Meteor.subscribe('projects', file_id);
});
});
Whenever file_id changes, a new subscription is triggered, giving you the desired effect of auto pub-sub utility.
I hope this will give you some insight. It could be easily achieved via Angular JS as well.

Is there a way to query multiple tables at the same time in Sails?

I've been tasked with adding an Angular Typeahead search field to a site and the data needs to come from multiple tables. It needs to be a "search all the things" kind of query which looks for people, servers, and applications in one spot.
I was thinking the best way to do this would be to have a single API endpoint in Sails which could pull from 3 tables on the same DB and send the results, but I'm not quite sure how to go about it.
Use the built-in bluebird library, specifically Promise.all(). To handle the results, use .spread(). Example controller code (modify to suit your case):
var Promise = require('bluebird');
module.exports = {
searchForStuff: function(req, res) {
var params = req.allParams();
// Replace the 'find' criteria with whatever suitable for your case
var requests = [
Person.find({name: params.searchString}),
Server.find({name: params.searchString}),
Application.find({name: params.searchString})
];
Promise.all(requests)
.spread(function(people, servers, applications) {
return res.json({
people: people,
servers: servers,
applications: applications
})
})
}
}

Filter cached REST-Data vs multiple REST-calls

I'm building an Angular Shop-Frontend which consumes a REST-API with Restangular.
To get the articles from the API, I use Restangular.all("articles") and I setup Restangular to cache this request.
When I want to get one article from the API, for example on the article-detail page by it's linkname and later somewhere else (on the cart-summary) by it's id, I would need 3 REST-calls:
/api/articles
/api/articles?linkname=some_article
/api/articles/5
But actually, the data from the two later calls is already available from the cached first call.
So instead I thought about using the cached articles and filter them to save the additional REST-calls.
I built these functions into my ArticleService and it works as expected:
function getOne(articleId) {
var article = $q.defer();
restangular.all("articles").getList().then(function(articles) {
var filtered = $filter('filter')(wines, {id: articleId}, true);
article.resolve((filtered.length == 1) ? filtered[0] : null);
});
return article.promise;
}
function getOneByLinkname(linkname) {
var article = $q.defer();
restangular.all("articles").getList().then(function(articles) {
var filtered = $filter('filter')(articles, {linkname: linkname}, true);
article.resolve((filtered.length == 1) ? filtered[0] : null);
});
return article.promise;
}
My questions concerning this approach:
Are there any downsides I don't see right now? What would be the correct way to go? Is my approach legitimate, to have as little REST-calls as possible?
Thanks for your help.
Are there any downsides I don't see right now?
Depends on how the functionality of your application. If it requires real time data, then having REST calls performed to obtain the latest data would be a requirement.
What would be the correct way to go? Is my approach legitimate, to have as little REST-calls as possible?
Depends still. If you want, you can explore push data notifications, such that when your data from the server is changed or modified, you could push those info to your client. That way, the REST operations happens based on conditions you would have defined.

Partial Updates (aka PATCH) using a $resource based service?

We're building a web application using Django/TastyPie as the back-end REST service provider, and building an AngularJS based front end, using lots of $resource based services to CRUD objects on the server. Everything is working great so far!
But, we would like to reduce the amount of data that we're shipping around when we want to update only one or two changed fields on an object.
TastyPie supports this using the HTTP PATCH method. We have defined a .diff() method on our objects, so we can determine which fields we want to send when we do an update. I just can't find any documentation on how to define/implement the method on the instance object returned by $resource to do what we want.
What we want to do is add another method to the object instances, (as described in the Angular.js documentation here) like myobject.$partialupdate() which would:
Call our .diff() function to determine which fields to send, and then
Use an HTTP PATCH request to send only those fields to the server.
So far, I can't find any documentation (or other SO posts) describing how to do this, but would really appreciate any suggestions that anyone might have.
thank you.
I would suggest using
update: {
method: 'PATCH',
transformRequest: dropUnchangedFields
}
where
var dropUnchangedFields = function(data, headerGetter) {
/* compute from data using your .diff method by */
var unchangedFields = [ 'name', 'street' ];
/* delete unchanged fields from data using a for loop */
delete data['name'] ;
delete data['street'];
return data;
}
PS: not sure from memory, whether data is a reference to your resource of a copy of it, so you may need to create a copy of data, before deleting fields
Also, instead of return data, you may need return JSON.stringify(data).
Source (search for "transformRequest" on the documentation page)
We implemented $patchusing ngResource, but it's a bit involved (we use Django Rest Framework on the server-side). For your diff component, I'll leave to your own implementation. We use a pristine cache to track changes of resources, so I can poll a given object and see what (if any) has changed.
I leverage underscore's _.pick() method to pull the known fields to save off the existing instance, create a copy (along with the known primary key) and save that using $patch.
We also use some utility classes to extend the built-in resources.
app.factory 'PartUpdateMixin', ['$q', '_', ($q, _) ->
PartUpdateMixin = (klass) ->
partial_update: (keys...) ->
deferred = $q.defer()
params = _.pick(#, 'id', keys...)
o = new klass(params)
o.$patch(deferred.resolve, deferred.reject)
return deferred.promise
]
Here's the utility classes to enhance the Resources.
app.factory 'extend', ->
extend = (obj, mixins...) ->
for mixin in mixins
obj[name] = method for name, method of mixin
obj
app.factory 'include', ['extend', (extend) ->
include = (klass, mixins...) ->
extend klass.prototype, mixins...
return include
]
Finally, we can enhance our Resource
include TheResource, PartUpdateMixin(TheResource)
resourceInstance = TheResource.get(id: 1234)
# Later...
updatedFields = getChangedFields(resourceInstance)
resourceInstance.partial_update(updatedFields...)
I would suggest using Restangular over ngResource. The angular team keeps improving ngResource with every version, but Restangular still does a lot more, including allowing actions like PATCH that ngResource doesn't. Here'a a great SO question comparing the two What is the advantage of using Restangular over ngResource?

Resources