[SERVER-5379] js increment operators (ob.prop += ob2.prop) do not work, after assigment like this ob = ob2 Created: 23/Mar/12  Updated: 08/Mar/13  Resolved: 07/Mar/13

Status: Closed
Project: Core Server
Component/s: JavaScript
Affects Version/s: 2.0.3, 2.1.0
Fix Version/s: None

Type: Bug Priority: Major - P3
Reporter: Azat Khuzhin Assignee: Unassigned
Resolution: Duplicate Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

I think in all environment, but my is "Linux azat 2.6.38custom1.0 #1 SMP Mon May 23 17:17:08 MSD 2011 x86_64 GNU/Linux"


Issue Links:
Duplicate
is duplicated by SERVER-5417 js add property do not work at emit() Closed
is duplicated by SERVER-8624 Remove read-only constraint on Reduce... Closed
Related
related to DOCS-981 Document JavaScript interface changes... Closed
Operating System: ALL
Participants:

 Description   

/**
 * js increment operators (ob.prop += ob2.prop) do not work, after assigment like this ob = ob2
 * 
 * Affected versions:	r2.1.0-1580-g0c1a6bd (v8)
 * 				v8 engine: Version 3.9.23
 * 
 * 				2.0.3-1 (spider monkey)
 */
 
db.eval(function() {
	var ob2 = null;
	var ob1 = {foo: 1};
	ob2 = ob1;
	// if uncomment, than works
// 	ob2 = {};
// 	for (i in ob1) {
// 		ob2[i] = ob1[i];
// 	}
 
	ob2.foo += ob1.foo;
	assert(2 == ob2.foo, "increment #2, must be: 2");
	assert(ob2.foo == (1 + ob1.foo), "increment #1, must be: " + ob2.foo);
	return ob2.foo;
});



 Comments   
Comment by Ben Becker [ 07/Mar/13 ]

The arguments passed to the reduce function are now mutable, as of SERVER-8624 in v2.4.0-rc2. The results in the second example are now:

	"results" : [
		{
			"_id" : 1,
			"value" : {
				"foo" : 3
			}
		}
	],

Comment by Ian Beaver [ 12/Jan/13 ]

I have a bunch of M/R code written against 2.2 using spidermonkey, and this issue totally bit me when I moved to 2.3.1 with v8. This needs to be documented and at the very least throw an exception so people are aware of the change. It took me a while to figure out why I was getting empty results in the new environment with identical data.

Comment by Azat Khuzhin [ 03/May/12 ]

But also, why it is marked as read-only?

Comment by Azat Khuzhin [ 03/May/12 ]

It is true, I have messages in log
But I agree with you - throw an exception is a good idea

Comment by Antoine Girbal [ 30/Apr/12 ]

the problem in the 2nd example is that the values passed to reduce() are optimized by being marked read-only.
By using the 1st value as the basis for result, it cannot be modified, and you can see in mongod log:
cannot write property foo to read-only object
cannot write property foo to read-only object

It is true that it can be deceiving that it is just a log line, we should probably throw an exception when trying to write instead of failing silently.

Comment by Azat Khuzhin [ 27/Mar/12 ]

BTW the output is:

> printjson(res);
{
        "results" : [
                {
                        "_id" : 1,
                        "value" : {
                                "foo" : 1
                        }
                }
        ],
        "timeMillis" : 0,
        "counts" : {
                "input" : 2,
                "emit" : 2,
                "reduce" : 1,
                "output" : 1
        },
        "ok" : 1
}

"reduce: 1"

Comment by Azat Khuzhin [ 27/Mar/12 ]

The second example, must have 1 at all steps ( reduce() must be called once ), in collection just two rows with "count: 1"
So it means that there is still a bug

I can`t reopen issues

Comment by Antoine Girbal [ 23/Mar/12 ]

please reopen if you think there is still a bug.
For more general info on JS / mapreduce, please post on mongodb-user group

Comment by Antoine Girbal [ 23/Mar/12 ]

Note that in your 2nd example, it is probably a different issue.

			var a = result.foo + 1;
			var b = oldValue + vals[i].foo;

It is not sure that vals[i].foo will always be 1.
The reduce function may be called several times over intermediate results, so even if all your original "foo" values are 1, it can be more than 1 in some reduce steps.

Comment by Antoine Girbal [ 23/Mar/12 ]

the example works as expected.

	ob2 = ob1;
// now both variable point to the same object
 
	ob2.foo += ob1.foo;
// now "foo" is 2 for both ob2.foo and ob1.foo
 
	assert(2 == ob2.foo, "increment #2, must be: 2");
	assert(ob2.foo == (1 + ob1.foo), "increment #1, must be: " + ob2.foo);
// this fails since (1 + ob1.foo) is 3

Comment by Azat Khuzhin [ 23/Mar/12 ]

Code that above, is must work so, but this one, doesn`t

/**
 * js increment operators (ob.prop += ob2.prop) do not work, after assigment like this ob = ob2
 * 
 * @see laso mongodb-bug-increment.js
 * 
 * It is a duplicate of "mongodb-bug-increment.js", but it just for be shure that this code will works!
 * 
 * Affected versions:	r2.1.0-1580-g0c1a6bd (v8)
 * 				v8 engine: Version 3.9.23
 * 
 * 				2.0.3-1 (spider monkey)
 */
 
var testCollName = 'such-collection-can-t-exist';
var testColl = db.getCollection(testCollName);
testColl.insert({key: 1, foo: 1});
testColl.insert({key: 1, foo: 1});
 
res = db.runCommand({
	mapreduce: testCollName,
	map: function() {
		emit(this.key, {foo: this.foo});
	},
	reduce: function(k, vals) {
		var result = vals[0];
		for (i in vals) {
			var oldValue = result.foo;
			result.foo += vals[i].foo;
 
			var a = result.foo + 1;
			var b = oldValue + vals[i].foo;
			assert(a == b);
		}
		return result;
	},
	out: {inline: 1}, // the same with "replace" and others
});
printjson(res);
 
testColl.drop();

Generated at Thu Feb 08 03:08:42 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.