In the code below, does aObj remain in memory after garbage collection? And what is the easiest way I can test and see this?
var a = function(arg) {
this.argument = arg;
}
var aObj = new a({ prop1: 1, prop2: 2 });
var b = aObj.argument;
aObj = null;
No, it does not. After the aObj = null line, there are no remaining references to the object it used to contain. It has a reference to argument, but argument doesn't have a reference to it, and so after you've released the only reference to the object (in aObj), the object is eligible for garbage collection.
Let's stop the world just before the aObj = null line and see what's in memory (leaving out some details):
+−−−−−−−−−−−−−−−+
a−−−−−−−−>| (function) |
+−−−−−−−−−−−−−−−+
| (...) | +−−−−−−−−−−+
| prototype |−−−−+−−>| (object) |
+−−−−−−−−−−−−−−−+ | +−−−−−−−−−−+
| | (...) |
| +−−−−−−−−−−+
|
+−−−−−−−−−−−−−−−+ |
+−−>| (object) | |
| +−−−−−−−−−−−−−−−+ |
| | [[Prototype]] |−−−−+ +−−−−−−−−−−+
| | argument |−−−−+−−>| (object) |
| +−−−−−−−−−−−−−−−+ | +−−−−−−−−−−+
| | | prop1: 1 |
aObj−−+ | | prop2: 2 |
| +−−−−−−−−−−+
b−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+
Now, we do the aObj = null line, and get:
+−−−−−−−−−−−−−−−+
a−−−−−−−−>| (function) |
+−−−−−−−−−−−−−−−+
| (...) | +−−−−−−−−−−+
| prototype |−−−−+−−>| (object) |
+−−−−−−−−−−−−−−−+ | +−−−−−−−−−−+
| | (...) |
| +−−−−−−−−−−+
|
+−−−−−−−−−−−−−−−+ |
| (object) | |
+−−−−−−−−−−−−−−−+ |
| [[Prototype]] |−−−−+ +−−−−−−−−−−+
| argument |−−−−+−−>| (object) |
+−−−−−−−−−−−−−−−+ | +−−−−−−−−−−+
| | prop1: 1 |
aObj: null | | prop2: 2 |
| +−−−−−−−−−−+
b−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+
As you can see, nothing has a reference to that object anymore.
And what is the easiest way I can test and see this?
Chrome has a pretty advanced memory profiler which can, amongst other things, show you the number of objects from a given constructor that are still in memory. More on their dev tools site here.
Related
I have the following simplified code:
var obj = {
key1 : {
aliases: ["alias1", "alias2"],
prop1: someVal,
prop2: someOtherVal
}
}
var objHashMap = {};
for(var key in obj){
objHashMap[key] = obj[key];
objHashMap[obj[key].aliases[0]] = obj[key];
objHashMap[obj[key].aliases[1]] = obj[key];
}
Now objHashMap has 3 entries, and all entries points at:
{
aliases: ["alias1", "alias2"],
prop1: someVal,
prop2: someOtherVal
}
My question is weather all 3 points to the same object, or points to 3 different copies of the object?
objHashMap.key1 will have a copy of the value in obj.key. That value is a reference to an object. Both copies of that value refer to the same object. The you use the value of obj.key1.aliases[0] and obj.key1.aliases[1] to create two additional properties which also have copies of the reference, referring to the same object.
After this:
var obj = {
key1 : {
aliases: ["alias1", "alias2"],
prop1: someVal,
prop2: someOtherVal
}
}
...we have this in memory:
+----------------+
obj: ref-1654-->| (object) |
+----------------+ +-------------------+
| key1: ref-8754 |---->| (object) |
+----------------+ +-------------------+ +-------------+
| aliases: ref-6549 |---->| (array) |
| prop1: ??? | +-------------+
| prop2: ??? | | 0: "alias1" |
+-------------------+ | 1: "alias2" |
+-------------+
That is, we have a variable, obj, which contains a value that tells the JavaScript engine where an object is elsewhere in memory; I've shown that as ref-1654 above. Think of an object reference as a number that has meaning only to the JavaScript engine, like an index into a big array of memory. (The actual value of an object reference is something we never see.)
That object has a property, key1, which again has a value pointing to an object elsewhere in memory. That object, in turn, has aliases with another reference to an object (this time an array) in memory. The values of prop1 and prop2 in the object with aliases are unknown (they come from the variables someVal and someOtherVal, which you haven't defined for us).
Then this line adds another variable pointing to another object:
var objHashMap = {};
+----------------+
objHashMap: ref-8132-->| (object) |
+----------------+
| |
+----------------+
Your for-in loop only runs once, for the key "key1". After the first line:
objHashMap[key] = obj[key];
we have:
+----------------+
obj: ref-1654-->| (object) |
+----------------+
| key1: ref-8754 |---------+
+----------------+ |
|
|
| +-------------------+
+-->| (object) |
| +-------------------+ +-------------+
| | aliases: ref-6549 |---->| (array) |
+----------------+ | | prop1: ??? | +-------------+
objHashMap: ref-8132-->| (object) | | | prop2: ??? | | 0: "alias1" |
+----------------+ | +-------------------+ | 1: "alias2" |
| key1: ref-8754 |--+ +-------------+
+----------------+
Note how the key1 property in the new object contains the same value as the key1 property in the original object. They point to the same object.
Then you do:
objHashMap[obj[key].aliases[0]] = obj[key];
...which is to say
objHashMap[obj.key1.aliases[0]] = obj[key];
...since key contains "key1", which is to say
objHashMap["alias1"] = obj[key];
...because obj.key1.aliases[0] is "alias1". That gives us:
+----------------+
obj: ref-1654-->| (object) |
+----------------+
| key1: ref-8754 |-----------+
+----------------+ |
|
|
| +-------------------+
++-->| (object) |
|| +-------------------+ +-------------+
|| | aliases: ref-6549 |---->| (array) |
+------------------+ || | prop1: ??? | +-------------+
objHashMap: ref-8132-->| (object) | || | prop2: ??? | | 0: "alias1" |
+------------------+ || +-------------------+ | 1: "alias2" |
| key1: ref-8754 |--+| +-------------+
| alias1: ref-8754 |---+
+------------------+
Then again for this line:
objHashMap[obj[key].aliases[1]] = obj[key];
...which is:
objHashMap["alias2"] = obj[key];
...because obj.key1.aliases[1] is "alias2". So we end up with:
+----------------+
obj: ref-1654-->| (object) |
+----------------+
| key1: ref-8754 |-----------+
+----------------+ |
|
|
| +-------------------+
+++->| (object) |
||| +-------------------+ +-------------+
||| | aliases: ref-6549 |---->| (array) |
+------------------+ ||| | prop1: ??? | +-------------+
objHashMap: ref-8132-->| (object) | ||| | prop2: ??? | | 0: "alias1" |
+------------------+ ||| +-------------------+ | 1: "alias2" |
| key1: ref-8754 |--+|| +-------------+
| alias1: ref-8754 |---+|
| alias2: ref-8754 |----+
+------------------+
Yes, these point to the same object. Let me take an example to demonstrate the same:
var obj = {
key1 : {
aliases: [1,2],
prop1: 3,
prop2: 4
}
}
//initialize another object which will contain the same values.
var objHashMap = {};
for(var key in obj){
objHashMap[key] = obj[key];
objHashMap[obj[key].aliases[0]] = obj[key];
objHashMap[obj[key].aliases[1]] = obj[key];
}
//change the value of a key in objHashMap.
objHashMap["key1"].prop1 = 600
//now observe the value changed in other keys as well.
console.log(objHashMap[1].prop1);
console.log(objHashMap[2].prop1);
console.log(obj);
I have two tables, metadata and view_events. Both metadata and view_events have config_id and config_type columns. I'm trying to select all view_events for a given user email, distinct by config_id and config_type, ordered by timestamp, desc, and limited to the 10 most recent. The following knex.js code isn't working but hopefully expresses what I'm trying to achieve:
return dbClient<AuthenticatedUserIndexRow>(METADATA_TABLE_NAME)
.select([
`${METADATA_TABLE_NAME}.${METADATA_COLUMNS.CONFIG_ID}`,
`${METADATA_TABLE_NAME}.${METADATA_COLUMNS.CONFIG_TYPE}`,
`${METADATA_TABLE_NAME}.${METADATA_COLUMNS.DESCRIPTION}`,
`${VIEW_EVENTS_TABLE_NAME}.${VIEW_EVENTS_COLUMNS.TIMESTAMP}`,
])
.innerJoin<AuthenticatedUserIndexRow>(VIEW_EVENTS_TABLE_NAME, function innerJoinOnViewEvents() {
this.on(
`${METADATA_TABLE_NAME}.${METADATA_COLUMNS.STORAGE_ID}`,
'=',
`${VIEW_EVENTS_TABLE_NAME}.${VIEW_EVENTS_COLUMNS.CONFIG_STORAGE_ID}`,
)
.andOn(
`${VIEW_EVENTS_TABLE_NAME}.${VIEW_EVENTS_COLUMNS.USER_EMAIL}`,
'=',
rawSql('?', [authUserEmail]),
)
.andOn(`${METADATA_TABLE_NAME}.${METADATA_COLUMNS.DELETED}`, '=', rawSql('?', [false]));
})
.distinct([
`${METADATA_TABLE_NAME}.${METADATA_COLUMNS.CONFIG_TYPE}`,
`${METADATA_TABLE_NAME}.${METADATA_COLUMNS.CONFIG_ID}`,
])
.limit(EVENT_LIMIT)
.orderBy(VIEW_EVENTS_COLUMNS.TIMESTAMP, 'desc');
For example, given the following tables:
view_events
+-------------+-----------+--------------------------+----------------------+
| config_type | config_id | timestamp | email |
+-------------+-----------+--------------------------+----------------------+
| a | foo | 2020-01-23T03:08:14.618Z | john.smith#gmail.com |
| a | foo | 2020-01-23T03:08:14.500Z | jane.doe#gmail.com |
| a | foo | 2020-01-23T03:08:13.618Z | john.smith#gmail.com |
| a | bar | 2020-01-23T03:08:12.618Z | john.smith#gmail.com |
| a | foo | 2020-01-23T03:08:11.618Z | john.smith#gmail.com |
| b | foo | 2020-01-23T03:08:10.618Z | john.smith#gmail.com |
| a | baz | 2020-01-23T03:08:09.618Z | john.smith#gmail.com |
| a | foo | 2020-01-23T03:08:08.618Z | john.smith#gmail.com |
+-------------+-----------+--------------------------+----------------------+
metadata
+-------------+-----------+---------------------------+
| config_type | config_id | description |
+-------------+-----------+---------------------------+
| a | foo | Type a config with id foo |
| a | bar | Type a config with id bar |
| b | foo | Type b config with id foo |
| a | baz | Type a config with id baz |
+-------------+-----------+---------------------------+
I am trying to obtain the following output (given an authUserEmail of john.smith#gmail.com):
+-------------+-----------+---------------------------+
| config_type | config_id | description |
+-------------+-----------+---------------------------+
| a | foo | Type a config with id foo |
| a | bar | Type a config with id foo |
| b | foo | Type b config with id foo |
| a | baz | Type a config with id baz |
+-------------+-----------+---------------------------+
I'm not a SQL expert, but am generally aware that the use of SELECT and DISTINCT together here doesn't work. What's the correct approach?
Does the following roughly work for you? I did using with as so we could grab the 10 most recent configs (max(timestamp)..group by config) and then remove the timestamp column in the final projection. Note the final records may not appear in exact timestamp order as you did not want timestamp in your final output, but they will be the 10 most recent. I haven't added the DELETED column but imagine you will re-add that based on the code in your question.
knex.with('ordered_items', (qb) =>
qb.table('metadata')
.innerJoin('view_events', function() {
this.on('metadata.config_id', '=', 'view_events.config_id')
.andOn('metadata.config_type', '=', 'view_events.config_type')
})
.where({'view_events.email': 'john.smith#gmail.com'})
.select(['metadata.config_type', 'metadata.config_id',
'metadata.description'])
.max('view_events.timestamp', {as: 'max_ts'})
.groupBy(['metadata.config_id', 'metadata.config_type', 'metadata.description'])
.orderBy('max_ts', 'desc')
.limit(10))
.table('ordered_items')
.select(['config_type', 'config_id', 'description'])
My input and output:
sqlite> select * from metadata;
a|foo|Type a config with id foo
a|bar|Type a config with id bar
b|foo|Type b config with id foo
a|baz|Type a config with id baz
sqlite> select * from view_events;
a|foo|2020-01-23T03:08:14.618Z|john.smith#gmail.com
a|foo|2020-01-23T03:08:14.500Z|jane.doe#gmail.com
a|foo|2020-01-23T03:08:13.618Z|john.smith#gmail.com
a|bar|2020-01-23T03:08:12.618Z|john.smith#gmail.com
a|foo|2020-01-23T03:08:11.618Z|john.smith#gmail.com
b|foo|2020-01-23T03:08:10.618Z|john.smith#gmail.com
a|baz|2020-01-23T03:08:09.618Z|john.smith#gmail.com
a|foo|2020-01-23T03:08:08.618Z|john.smith#gmail.com
[ { config_type: 'a',
config_id: 'foo',
description: 'Type a config with id foo' },
{ config_type: 'a',
config_id: 'bar',
description: 'Type a config with id bar' },
{ config_type: 'b',
config_id: 'foo',
description: 'Type b config with id foo' },
{ config_type: 'a',
config_id: 'baz',
description: 'Type a config with id baz' } ]
I have the following simplified code:
var obj = {
key1 : {
aliases: ["alias1", "alias2"],
prop1: someVal,
prop2: someOtherVal
}
}
var objHashMap = {};
for(var key in obj){
objHashMap[key] = obj[key];
objHashMap[obj[key].aliases[0]] = obj[key];
objHashMap[obj[key].aliases[1]] = obj[key];
}
Now objHashMap has 3 entries, and all entries points at:
{
aliases: ["alias1", "alias2"],
prop1: someVal,
prop2: someOtherVal
}
My question is weather all 3 points to the same object, or points to 3 different copies of the object?
objHashMap.key1 will have a copy of the value in obj.key. That value is a reference to an object. Both copies of that value refer to the same object. The you use the value of obj.key1.aliases[0] and obj.key1.aliases[1] to create two additional properties which also have copies of the reference, referring to the same object.
After this:
var obj = {
key1 : {
aliases: ["alias1", "alias2"],
prop1: someVal,
prop2: someOtherVal
}
}
...we have this in memory:
+----------------+
obj: ref-1654-->| (object) |
+----------------+ +-------------------+
| key1: ref-8754 |---->| (object) |
+----------------+ +-------------------+ +-------------+
| aliases: ref-6549 |---->| (array) |
| prop1: ??? | +-------------+
| prop2: ??? | | 0: "alias1" |
+-------------------+ | 1: "alias2" |
+-------------+
That is, we have a variable, obj, which contains a value that tells the JavaScript engine where an object is elsewhere in memory; I've shown that as ref-1654 above. Think of an object reference as a number that has meaning only to the JavaScript engine, like an index into a big array of memory. (The actual value of an object reference is something we never see.)
That object has a property, key1, which again has a value pointing to an object elsewhere in memory. That object, in turn, has aliases with another reference to an object (this time an array) in memory. The values of prop1 and prop2 in the object with aliases are unknown (they come from the variables someVal and someOtherVal, which you haven't defined for us).
Then this line adds another variable pointing to another object:
var objHashMap = {};
+----------------+
objHashMap: ref-8132-->| (object) |
+----------------+
| |
+----------------+
Your for-in loop only runs once, for the key "key1". After the first line:
objHashMap[key] = obj[key];
we have:
+----------------+
obj: ref-1654-->| (object) |
+----------------+
| key1: ref-8754 |---------+
+----------------+ |
|
|
| +-------------------+
+-->| (object) |
| +-------------------+ +-------------+
| | aliases: ref-6549 |---->| (array) |
+----------------+ | | prop1: ??? | +-------------+
objHashMap: ref-8132-->| (object) | | | prop2: ??? | | 0: "alias1" |
+----------------+ | +-------------------+ | 1: "alias2" |
| key1: ref-8754 |--+ +-------------+
+----------------+
Note how the key1 property in the new object contains the same value as the key1 property in the original object. They point to the same object.
Then you do:
objHashMap[obj[key].aliases[0]] = obj[key];
...which is to say
objHashMap[obj.key1.aliases[0]] = obj[key];
...since key contains "key1", which is to say
objHashMap["alias1"] = obj[key];
...because obj.key1.aliases[0] is "alias1". That gives us:
+----------------+
obj: ref-1654-->| (object) |
+----------------+
| key1: ref-8754 |-----------+
+----------------+ |
|
|
| +-------------------+
++-->| (object) |
|| +-------------------+ +-------------+
|| | aliases: ref-6549 |---->| (array) |
+------------------+ || | prop1: ??? | +-------------+
objHashMap: ref-8132-->| (object) | || | prop2: ??? | | 0: "alias1" |
+------------------+ || +-------------------+ | 1: "alias2" |
| key1: ref-8754 |--+| +-------------+
| alias1: ref-8754 |---+
+------------------+
Then again for this line:
objHashMap[obj[key].aliases[1]] = obj[key];
...which is:
objHashMap["alias2"] = obj[key];
...because obj.key1.aliases[1] is "alias2". So we end up with:
+----------------+
obj: ref-1654-->| (object) |
+----------------+
| key1: ref-8754 |-----------+
+----------------+ |
|
|
| +-------------------+
+++->| (object) |
||| +-------------------+ +-------------+
||| | aliases: ref-6549 |---->| (array) |
+------------------+ ||| | prop1: ??? | +-------------+
objHashMap: ref-8132-->| (object) | ||| | prop2: ??? | | 0: "alias1" |
+------------------+ ||| +-------------------+ | 1: "alias2" |
| key1: ref-8754 |--+|| +-------------+
| alias1: ref-8754 |---+|
| alias2: ref-8754 |----+
+------------------+
Yes, these point to the same object. Let me take an example to demonstrate the same:
var obj = {
key1 : {
aliases: [1,2],
prop1: 3,
prop2: 4
}
}
//initialize another object which will contain the same values.
var objHashMap = {};
for(var key in obj){
objHashMap[key] = obj[key];
objHashMap[obj[key].aliases[0]] = obj[key];
objHashMap[obj[key].aliases[1]] = obj[key];
}
//change the value of a key in objHashMap.
objHashMap["key1"].prop1 = 600
//now observe the value changed in other keys as well.
console.log(objHashMap[1].prop1);
console.log(objHashMap[2].prop1);
console.log(obj);
I know there are similar questions but they haven't help me.
I've got an error when trying to use .find() method on model: product2
(.query() method is working )
I was trying to find out how to get more verbose output from database, but with failure.
{
"error": "E_UNKNOWN",
"status": 500,
"summary": "Encountered an unexpected error",
"raw": {
"code": "ER_BAD_FIELD_ERROR",
"errno": 1054,
"sqlState": "42S22",
"index": 0
}
}%
my model "Product2.js"
module.exports = {
connection:'someMysqlServer',
migration:'safe',
tableName:'NUTR_DATA',
attributes:{
id:{
columnName:'Nutr_No',
primaryKey:true,
type:'string'
}
}
};
routes.js
module.exports.routes{
'GET /select':'Product2Controller.select'
};
Product2Controller.js
module.exports = {
select:function(req,res){
Product2.find({limit:10}).exec(function(err, results) {
if (err){res.serverError(err)}
else{res.json(results)};
});
}
};
database schema
+---------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------+---------------+------+-----+---------+-------+
| NDB_No | varchar(5) | NO | PRI | NULL | |
| Nutr_No | varchar(3) | NO | PRI | NULL | |
| Nutr_Val | decimal(13,3) | NO | | NULL | |
| Num_Data_Ptr | decimal(10,0) | NO | | NULL | |
| Std_Error | decimal(11,3) | YES | | NULL | |
| Src_Cd | varchar(2) | NO | | NULL | |
| Deriv_cd | varchar(4) | YES | | NULL | |
| Ref_NDB_No | varchar(5) | YES | | NULL | |
| Add_Nutr_Mark | varchar(1) | YES | | NULL | |
| Num_Studies | int(11) | YES | | NULL | |
| Min | decimal(13,3) | YES | | NULL | |
| Max | decimal(13,3) | YES | | NULL | |
| DF | int(11) | YES | | NULL | |
| Low_EB | decimal(13,3) | YES | | NULL | |
| Up_EB | decimal(13,3) | YES | | NULL | |
| Stat_cmd | varchar(10) | YES | | NULL | |
| AddMod_Date | varchar(10) | YES | | NULL | |
| CC | varchar(1) | YES | | NULL | |
+---------------+---------------+------+-----+---------+-------+
Any ideas as to what is going wrong?
Thank you for your responses. The problem was not setted
autoCreatedAt: false,
autoUpdatedAt: false
i hadn't see that my ide actually give me more verbose output then curl. Maybe it would be better if errors would be placed under calming picture of boat and info about lifted server :P
The likely problem is that you're defining your own primary key field, but Waterline is still atuomatically adding its own id field. You can turn off this behavior by adding autoPK: false to your model configuration. Docs for autoPK are here.
I'm working on a Backbone application and I'm not sure if the way what I'm trying to do is the correct way.
I have an application view and inside that application view I'm trying to append a collection view, and each view in that collection is a collection too.
Let me explain that graphically.
----------------------------------------------------------------------
| |
| Application view |
| |
| ------------------------------------------------------------- |
| | Windows Collection view | |
| | | |
| | -------------------------- -------------------------- | |
| | | Tabs collection view | | Tabs collection view | | |
| | | | | | | |
| | | ---------- ---------- | | ---------- ---------- | | |
| | | |Tab view| |Tab view| | | |Tab view| |Tab view| | | |
| | | ---------- ---------- | | ---------- ---------- | | |
| | | | | | | |
| | | ---------- ---------- | | ---------- ---------- | | |
| | | |Tab view| |Tab view| | | |Tab view| |Tab view| | | |
| | | ---------- ---------- | | ---------- ---------- | | |
| | |------------------------| |------------------------| | |
| | | |
| | -------------------------- -------------------------- | |
| | | Tabs collection view | | Tabs collection view | | |
| | | | | | | |
| | | ---------- ---------- | | ---------- ---------- | | |
| | | |Tab view| |Tab view| | | |Tab view| |Tab view| | | |
| | | ---------- ---------- | | ---------- ---------- | | |
| | | | | | | |
| | | ---------- ---------- | | ---------- ---------- | | |
| | | |Tab view| |Tab view| | | |Tab view| |Tab view| | | |
| | | ---------- ---------- | | ---------- ---------- | | |
| | |------------------------| |------------------------| | |
| | | |
| ------------------------------------------------------------- |
| |
| |
----------------------------------------------------------------------
Currently I'm loading the application view from the initialize method in my Backbone router. That view loads the Windows collection view.
The main problem is that I'm not sure if I'm on the right way.
The second problem is that I'm not really sure how to load each Tabs collection view from my Windows Collecion view.
PS: Just to make things even clearer, I'm trying to replicate Firefox's panorama view: http://i.i.com.com/cnwk.1d/i/tim//2010/08/24/firefox-panorama.jpg
I would highly recommend using Marionette.js to structure your application.
It already has collection views built in which makes rendering easy. Your application seems to be a perfect use case. You will get a lot of boilerplate code for free.
I'm just posting this here so others can see how I solved the problem
A working demo of the solution can be found here (original fiddle).
As you can see from the link, the work is done thanks to Marionette's CompositeView which lets recursively render collections.
var TreeView = Backbone.Marionette.CompositeView.extend({
initialize: function(){
if(this.model.tabs){
this.template = "#window-template";
}else{
this.template = "#tab-template";
}
this.collection = this.model.tabs;
},
appendHtml: function(cv, iv){
cv.$("#tabs").append(iv.el);
},
onRender: function() {
if(_.isUndefined(this.collection)){
this.$("#tabs").remove();
}
}
});
The small trick I'm using in the initialize (the if/else with the template asignation) works the following way:
I get the current model and check if it has a "tabs" key. If it does have it, it means that the current model is a Window Data Model, so I need to use the window-template, else use the tab-template
The rest is pretty much plain Backbone structure.