Backbone collection tree creation - javascript

I'm using backbone 1.1 and trying to create a collection from a 3-5 level deep tree-navigation.
A simplified version of my code looks like this.
var treeItem = Backbone.Model.extend({
defaults : {
'label' : '',
'children' : null
},
initialize : function() {
console.log('model init');
if (_.isArray(this.get('children'))) {
this.set({children : new treeItemCollection(this.get('children'))});
}
},
});
var treeItemCollection = Backbone.Collection.extend({
model: treeItem
});
var myTree = new treeItemCollection([
{ "label" : "first", "id": 1 },
{ "label" : "second", "id": 1, "children":
[
{ "label" : "second.first", "id" : 21 },
{ "label" : "second.second", "id" : 22 },
{ "label" : "second.third", "id" : 22, "children" : [
{ "label" : "third.first", "id" : 31 },
{ "label" : "third.second", "id" : 32 }
] }
]
}
]);
In my understanding this should create the deeper level child-collections correctly (as to my understanding, initialize should be called when the object is constructed thus creating the deeper levels correctly).
For some reason this doesn't seem to be the case. The second level (eg. myTree.models[0].get('children') is correctly a collection of the treeCollection type, but the 3rd level (myTree.models[0].get('children').models[0].get('children')) is just straight up JSON from the parameter object.
To me that's the weirdest part, that the second level is ok, but the third is not. The console.log in initialize() is to check, and quite right, it gets triggered 4 times, not 6.
I'm trying to understand why the 3rd level doesn't get converted to a collection.

You can overwrite the parse function in the model to do this.
var treeItem = Backbone.Model.extend({
defaults : {
'label' : '',
'children' : null
},
initialize : function() {
console.log('model init');
},
parse: function(response) {
if (response["children"]) {
response["children"] = new treeItemCollection(response["children"]);
}
return response;
}
});
this way, each time when you do fetch() or save(), it will automatically wrap your children (and their nested children, if exists) in treeItemCollection.
But this does not work when you bootstrap your data, or you are just using reset(). so you may want to overwrite the constructor method as well:
var treeItem = Backbone.Model.extend({
//other stuff shown above
constructor: function(attributes, options){
options = options || {};
options.parse = true;
Backbone.Model.call(this, attributes, options);
}
});
and it should work for your case.
We have used this pattern in many projects and we loved it. If you wanna apply it to all models/collections, and be more flexible, you probably wanna read this:
http://www.devmynd.com/blog/2013-6-backbone-js-with-a-spine-part-2-models-and-collections

Related

JS same function different parameters in every object value. How can I write less?

What I did
I call a function in objects' value.
And this function would be called in every object as much as users add an absent data.
What I want to do
Hence I recognize that I could have thousands of hundred of new object, I would rather minimize/eliminate duplication than write the function every time.
Explain what the code is for
The function's name I would like to auto-set is datesBetween().
datesBetween() can choose the mode, and it has parameters.
The mode refers the value from the key reason which is in same object. arr[0].absent[0].datesBetween's value should be datesBetween('leave')('2020-1-1','2020-1-4').
The parameters does same. First parameter should refer from the object key start which is in same object, second parameter should refer from the object key end.
Some idea
I guess this could use class but I am afraid to mix up with functions. if class would help, please do tell.
I write this thou, I am uncertain about this. Is using this could be a solution in this code?
var arr = [
{
name : "A",
absent :[
{
reason : "leave",
start : '2020-1-1',
end : '2020-1-4',
datesBetween : datesBetween('leave')(this.start, this.end)
}, {
reason : "sleeOver",
start : '2020-1-25',
end : '2020-1-26',
datesBetween : datesBetween('sleeOver')(this.start, this.end)
}
]
}, {
name : "B",
absent :[
{
reason : "weekendExcursion",
start : '2020-1-18',
end : '2020-1-19',
datesBetween : datesBetween('weekendExcursion')(this.start, this.end)
}
]
}
]
function autoAbsentSetter(){
//do I need class to use inheritance/property things?
}
function addAbsent(mode){
var funcs = {
'leave' : function absent_leave(name, absentName, absentStart, absentEnd){ //all kinds of leave are included. Detail further.
//some codes
},
'sleepOver' : function absent_sleepOver(name, absentName, absentStart, absentEnd){
//some codes
},
'weekdayExcursion' : function absent_weekdayExcursion(name, absentName, absentStart, absentEnd){
//some codes
},
'weekendExcursion' : function absent_weekendExcursion(name, absentName, absentStart, absentEnd){
//some codes
}
}
return funcs[mode];
}
You don't need to put those functions inside the array literal (and yes, you cannot access the other properties there anyway). Just write
var arr = [
{
name: "A",
absent: [
{
reason : "leave",
start : '2020-1-1',
end : '2020-1-4',
}, {
reason : "sleepOver",
start : '2020-1-25',
end : '2020-1-26',
}
]
}, {
name: "B",
absent: [
{
reason : "weekendExcursion",
start : '2020-1-18',
end : '2020-1-19',
}
]
}
];
for (const value of arr) {
for (const absence of value.absent) {
absence.datesBetween = datesBetween(absence.reason)(absence.start, absence.end);
}
}
You can do whatever you want in that loop processing your data.

mongoDB: Find missing documents in model tree structure with parent reference

I have some documents which are organized in a model tree structure (depth is variable!). Unfortunately some documents are missing and I need to find those broken chains. As you can see the last document in that chain has always the target field. This is the starting point and I have to look upwards using parent. The last element in that chain has always the field type.
{
"_id" : "K7NSxNEnNSr9nCszR",
"title" : "title",
"type" : "book",
"ancestors" : [ ]
}
{
"_id" : "diyvwYz66yoTCTt9L",
"field" : "something",
"parent" : "K7NSxNEnNSr9nCszR",
"ancestors" : [
"K7NSxNEnNSr9nCszR"
]
}
{
"_id" : "diyvwYz66yoTCTt9L",
"field" : "anything",
"target" : "D2YuXtM6Gzt4eWaW2",
"parent" : "QWvdAyftSGANM3zy8",
"ancestors" : [
"K7NSxNEnNSr9nCszR",
"QWvdAyftSGANM3zy8"
]
}
What I need to know is if any parent is missing or if the last element (=type existing) is missing.
var broken = [];
Collection.find({ target: { $exists: true }}).forEach(function(element) {
var startDocID = element._id;
if (Collection.find({ _id: element.parent }).count() === 0)
broken.push(startDocID);
});
console.log(broken);
But this isn't working well as I need to use a loop to get upwards until the top document (= type existing).
You're talking about recursion here if you need to go down the tree, so you probably need to write a recursive search function
var broken = [];
Collection.find({ target: { $exists: true }}).forEach(function(element) {
function recurse(e) {
var startDocID = e._id;
var nodes = Collection.find({ _id: e.parent });
if (node.count() === 0)
{broken.push(startDocID);}
else {
nodes.fetch().forEach(node) {
recurse(node)
}
}
recurse(element);
}
});
or something of that sort... (hard to debug without the data)

How to search by object with nested object in AngularJS

I want to filter my orders collection by search object. want to show only matching order in view.
I have orders array collection like:
$scope.orders = [{
"_id" : "56461e2b7caaf49345076709",
"customer" : {"_id": "76461e2b7caaf49345076a19b", "name": "cr1"},
"seller" : {"_id": "96461e2b7caaf49345076a18b", "name": "sl1"},
"address" : "Squire Park",
"qt" : 5
},
{
"_id" : "56461e2b7caaf49345076708",
"customer" : {"_id": "76461e2b7caaf49345076a19b1", "name": "cr2"},
"seller" : {"_id": "96461e2b7caaf49345076a18c1", "name": "sl2"},
"address" : "Squire Park1",
"qt" : 6
},
..................
];
and my search object like:
$scope.search = {
"qt": 5,
"customer" : {"_id": "76461e2b7caaf49345076a19b1"},
"seller" : {"_id": "96461e2b7caaf49345076a18c1"}
};
view:
<tr data-ng-repeat="order in orders | myFilter:search">
<td>{{order._id}}</td>
<td>{{order.customer.name}}</td>
</tr>
What should be my myFilter function?
Thanks advance.
A rather simple solution would be attaching a filter function to the scope
$scope.orderFilter = function(order) {
// implementation depends on how you want the search to behave
// for example, if the order should match every property of the searchObj
var pass = true;
for (var property in $scope.search) {
if ($scope.search.hasOwnProperty(property)) {
if($scope.search[property] != order[property]) {
pass = false;
}
}
}
return pass;
// .. though i wouldnt recommend this implementation
// you´d be better off by implementing a more specific matching
}
And in the view
<tr data-ng-repeat="order in orders | filter:orderFilter">
<td>{{order._id}}</td>
<td>{{order.customer.name}}</td>
</tr>
If you want the filter-expression you are using to work, you have to register a custom filter on your module. This would be done like so:
angular.module('app').filter('myFilter', function() {
return function(orders, searchObj) {
var filteredOrders = [];
orders.forEach(function(order) {
// evaluate if you want to have the order show up based on the searchOjb
// .. if so, add it to the array like so:
filteredOrders.push(order);
});
return filteredOrders;
}
});
NOTE
If you change a property of you search object, your filter function wont be called again, because angular filters arent listening for changes of nested properties. The filter will only be called if you change the whole search object.

MongoDB Map Reduce on object fields

With documents like:
{
_id: 123,
events: {
someEvent:{
created: ISODate("2015-06-27T16:51:03.000Z"),
metadata: {
some: "thing"
}
},
anotherEvent:{
created: ISODate("2015-06-27T16:51:01.000Z"),
metadata: {
some: "thing"
}
}
}
}
This is a simplified example of the data. The events object can have between 200 and 3,000 fields. There are thousands of documents like this.
I'm looking to use mapreduce on the collection so I only return one of the events (the one with the latest 'created' date) for each document in the collection.
Is this possible?
Yes it's possible. MapReduce is a bit of a "blunt edged sword" for this, but your element structure is "not great" and a possible 3000 elements needs something like this:
db.collection.mapReduce(
function() {
var doc = this;
var plucked = Object.keys(doc.events)
.map(function(key) {
var myObj = doc.events[key];
myObj.key = key;
return myObj;
})
.sort(function(a,b) {
return ( a.created > b.created )
? -1 : ( a.created < b.created )
? 1 : 0;
})[0];
emit(this._id, plucked);
},
function() {},
{ "out": { "inline": 1 } }
);
So basically that cylces though "events" and reshapes the data somewhat. Then you .sort() on the "created" key of the results in "descencing order" and just pull the first array element.
The "reducer" does nothing here. But this is just a way for the server to do the filtering.

How do I separate data subsets in firebase?

I am saving data from multiple locations in my webapp. For example, I am saving employee information from one page and employer group information from another. When I save the data, I am unable to differentiate between the two groups in my firebase view as it is organized by ID given when the data is created. Instead of by ID first, I would like to organize all of my data into two separate sections either 'employee' or 'group'. The following code is repeated for the employee data controller but with 'employee' substituted for the word 'group'.
.controller('GroupsCtrl', ['$scope', 'groupsService', function
( $scope, groupsService, $firebase ) {
$scope.newGroup = {
name: '',
date: ''
};
$scope.data = {};
$scope.data.groups = groupsService.getGroups();
$scope.addGroup = function(newGroup) {
groupsService.addGroup(newGroup);
$scope.newGroup = {
name: '',
date: ''
};
};
$scope.updateGroup = function (id) {
groupsService.updateGroup(id);
};
$scope.removeGroup = function(id) {
groupsService.removeGroup(id);
};
}])
And this is how I would like it to be structured:
{
Groups:[
"-JcFXid1A2G8EM7A_kwc" : {
"name" : "hi",
"date": "02/13/91"
},
"-JcFZP5FNtL4Yj6nja_7" : {
"name" : "hi"
"date": "02/13/91"
},
"-JcFtGoZL7J-CCIjTYcL" : {
"name" : "dfgdfg",
"date": "02/13/91"
}
]
Employees:[
"-JcFXid1A2G8EM7A_kwc" : {
"name" : "hi",
"date": "02/13/91"
}
]
}
Any advice would be great! I am still learning, Thank you!
var ref = new Firebase(FIREBASE_URI + '/groups');
will organize it into the groups sub category.
I figured it out!

Categories

Resources