ObjectExpression.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663
  1. "use strict";
  2. var assert = require("assert"),
  3. ObjectExpression = require("../../../../lib/pipeline/expressions/ObjectExpression"),
  4. ConstantExpression = require("../../../../lib/pipeline/expressions/ConstantExpression"),
  5. FieldPathExpression = require("../../../../lib/pipeline/expressions/FieldPathExpression"),
  6. AndExpression = require("../../../../lib/pipeline/expressions/AndExpression"),
  7. Variables = require("../../../../lib/pipeline/expressions/Variables");
  8. function assertEqualJson(actual, expected, message){
  9. if(actual.sort) {
  10. actual.sort();
  11. if(expected.sort) {
  12. expected.sort();
  13. }
  14. }
  15. assert.strictEqual(message + ": " + JSON.stringify(actual), message + ": " + JSON.stringify(expected));
  16. }
  17. /// An assertion for `ObjectExpression` instances based on Mongo's `ExpectedResultBase` class
  18. function assertExpectedResult(args) {
  19. {// check for required args
  20. if (args === undefined) throw new TypeError("missing arg: `args` is required");
  21. if (!("expected" in args)) throw new Error("missing arg: `args.expected` is required");
  22. if (!("expectedDependencies" in args)) throw new Error("missing arg: `args.expectedDependencies` is required");
  23. if (!("expectedJsonRepresentation" in args)) throw new Error("missing arg: `args.expectedJsonRepresentation` is required");
  24. }// check for required args
  25. {// base args if none provided
  26. if (args.source === undefined) args.source = {_id:0, a:1, b:2};
  27. if (args.expectedIsSimple === undefined) args.expectedIsSimple = false;
  28. if (args.expression === undefined) args.expression = ObjectExpression.createRoot(); //NOTE: replaces prepareExpression + _expression assignment
  29. }// base args if none provided
  30. // run implementation
  31. var result = {},
  32. variable = new Variables(1, args.source);
  33. args.expression.addToDocument(result, args.source, variable);
  34. assert.deepEqual(result, args.expected);
  35. var dependencies = {};
  36. args.expression.addDependencies(dependencies, [/*FAKING: includePath=true*/]);
  37. //dependencies.sort(), args.expectedDependencies.sort(); // NOTE: this is a minor hack added for munge because I'm pretty sure order doesn't matter for this anyhow
  38. assert.deepEqual(Object.keys(dependencies).sort(), Object.keys(args.expectedDependencies).sort());
  39. assert.deepEqual(args.expression.serialize(true), args.expectedJsonRepresentation);
  40. assert.deepEqual(args.expression.getIsSimple(), args.expectedIsSimple);
  41. }
  42. module.exports = {
  43. "ObjectExpression": {
  44. "constructor()": {
  45. "should not throw Error when constructing without args": function testConstructor(){
  46. assert.doesNotThrow(function(){
  47. ObjectExpression.create();
  48. });
  49. }
  50. },
  51. "#addDependencies":{
  52. "should be able to get dependencies for non-inclusion expressions": function testNonInclusionDependencies(){
  53. /** Dependencies for non inclusion expressions. */
  54. var expr = ObjectExpression.create();
  55. expr.addField("a", new ConstantExpression(5));
  56. var depsTracker = {fields:{}};
  57. assertEqualJson(expr.addDependencies(depsTracker, [/*FAKING: includePath=true*/]), {fields:{"_id":1}}, "Message");
  58. expr.excludeId = true;
  59. assertEqualJson(expr.addDependencies(depsTracker, []), {fields:{"_id":1}});
  60. expr.addField("b", FieldPathExpression.create("c.d"));
  61. //var deps = {};
  62. depsTracker = {fields:{}};
  63. expr.addDependencies(depsTracker, []);
  64. assert.deepEqual(depsTracker, {fields:{"c.d":1}});
  65. expr.excludeId = false;
  66. //deps = {};
  67. depsTracker = {fields:{}}
  68. expr.addDependencies(depsTracker, []);
  69. assert.deepEqual(depsTracker, {fields:{"_id":1,"c.d":1}});
  70. },
  71. "should be able to get dependencies for inclusion expressions": function testInclusionDependencies(){
  72. /** Dependencies for inclusion expressions. */
  73. var expr = ObjectExpression.create();
  74. expr.includePath( "a" );
  75. var depsTracker = {fields:{}};
  76. assertEqualJson(expr.addDependencies(depsTracker, [/*FAKING: includePath=true*/]), {"fields":{"_id":1,"a":1}});
  77. assert.throws(function(){
  78. expr.addDependencies({});
  79. }, Error);
  80. }
  81. },
  82. "#toJSON": {
  83. "should be able to convert to JSON representation and have constants represented by expressions": function testJson(){
  84. /** Serialize to a BSONObj, with constants represented by expressions. */
  85. var expr = ObjectExpression.create(true);
  86. expr.addField("foo.a", new ConstantExpression(5));
  87. assertEqualJson({foo:{a:{$const:5}}}, expr.serialize(true));
  88. }
  89. },
  90. "#optimize": {
  91. "should be able to optimize expression and sub-expressions": function testOptimize(){
  92. /** Optimizing an object expression optimizes its sub expressions. */
  93. var expr = ObjectExpression.createRoot();
  94. // Add inclusion.
  95. expr.includePath( "a" );
  96. // Add non inclusion.
  97. expr.addField( "b", new AndExpression());
  98. expr.optimize();
  99. // Optimizing 'expression' optimizes its non inclusion sub expressions, while inclusion sub expressions are passed through.
  100. assertEqualJson({a:{$const:null}, b:{$const:true}}, expr.serialize(true));
  101. }
  102. },
  103. "#evaluate()": {
  104. "should be able to provide an empty object": function testEmpty(){
  105. /** Empty object spec. */
  106. var expr = ObjectExpression.createRoot();
  107. assertExpectedResult({
  108. expression: expr,
  109. expected: {"_id":0},
  110. expectedDependencies: {"fields":1},
  111. expectedJsonRepresentation: {}
  112. });
  113. },
  114. "should be able to include 'a' field only": function testInclude(){
  115. /** Include 'a' field only. */
  116. var expr = ObjectExpression.createRoot();
  117. expr.includePath( "a" );
  118. assertExpectedResult({
  119. expression: expr,
  120. expected: {"_id":0, "a":1},
  121. expectedDependencies: {"_id":1, "a":1},
  122. expectedJsonRepresentation: {"a":{$const:null}}
  123. });
  124. },
  125. "should NOT be able to include missing 'a' field": function testMissingInclude(){
  126. /** Cannot include missing 'a' field. */
  127. var expr = ObjectExpression.createRoot();
  128. expr.includePath( "a" );
  129. assertExpectedResult({
  130. source: {"_id":0, "b":2},
  131. expression: expr,
  132. expected: {"_id":0},
  133. expectedDependencies: {"_id":1, "a":1},
  134. expectedJsonRepresentation: {"a":{$const:null}}
  135. });
  136. },
  137. "should be able to include '_id' field only": function testIncludeId(){
  138. /** Include '_id' field only. */
  139. var expr = ObjectExpression.createRoot();
  140. expr.includePath( "_id" );
  141. assertExpectedResult({
  142. expression: expr,
  143. expected: {"_id":0},
  144. expectedDependencies: {"_id":1},
  145. expectedJsonRepresentation: {"_id":{$const:null}}
  146. });
  147. },
  148. "should be able to exclude '_id' field": function testExcludeId(){
  149. /** Exclude '_id' field. */
  150. var expr = ObjectExpression.createRoot();
  151. expr.includePath( "b" );
  152. expr.excludeId = true;
  153. assertExpectedResult({
  154. expression: expr,
  155. expected: {"b":2},
  156. expectedDependencies: {"b":1},
  157. expectedJsonRepresentation: {"b":{$const:null}}
  158. });
  159. },
  160. "should be able to include fields in source document order regardless of inclusion order": function testSourceOrder(){
  161. /** Result order based on source document field order, not inclusion spec field order. */
  162. var expr = ObjectExpression.createRoot();
  163. expr.includePath( "b" );
  164. expr.includePath( "a" );
  165. assertExpectedResult({
  166. expression: expr,
  167. get expected() { return this.source; },
  168. expectedDependencies: {"_id":1, "a":1, "b":1},
  169. expectedJsonRepresentation: {"b":{$const:null}, "a":{$const:null}}
  170. });
  171. },
  172. "should be able to include a nested field": function testIncludeNested(){
  173. /** Include a nested field. */
  174. var expr = ObjectExpression.createRoot();
  175. expr.includePath( "a.b" );
  176. assertExpectedResult({
  177. source: {"_id":0, "a":{ "b":5, "c":6}, "z":2 },
  178. expression: expr,
  179. expected: {"_id":0, "a":{ "b":5} },
  180. expectedDependencies: {"_id":1, "a.b":1},
  181. expectedJsonRepresentation: {"a":{ "b":{$const:null}} }
  182. });
  183. },
  184. "should be able to include two nested fields": function testIncludeTwoNested(){
  185. /** Include two nested fields. */
  186. var expr = ObjectExpression.createRoot();
  187. expr.includePath( "a.b" );
  188. expr.includePath( "a.c" );
  189. assertExpectedResult({
  190. source: {"_id":0, "a":{ "b":5, "c":6}, "z":2 },
  191. expression: expr,
  192. expected: {"_id":0, "a":{ "b":5, "c":6} },
  193. expectedDependencies: {"_id":1, "a.b":1, "a.c":1},
  194. expectedJsonRepresentation: {"a":{ "b":{$const:null}, "c":{$const:null}} }
  195. });
  196. },
  197. "should be able to include two fields nested within different parents": function testIncludeTwoParentNested(){
  198. /** Include two fields nested within different parents. */
  199. var expr = ObjectExpression.createRoot();
  200. expr.includePath( "a.b" );
  201. expr.includePath( "c.d" );
  202. assertExpectedResult({
  203. source: {"_id":0, "a":{ "b":5 }, "c":{"d":6} },
  204. expression: expr,
  205. expected: {"_id":0, "a":{ "b":5}, "c":{"d":6} },
  206. expectedDependencies: {"_id":1, "a.b":1, "c.d":1},
  207. expectedJsonRepresentation: {"a":{"b":{$const:null}}, "c":{"d":{$const:null}} }
  208. });
  209. },
  210. "should be able to attempt to include a missing nested field": function testIncludeMissingNested(){
  211. /** Attempt to include a missing nested field. */
  212. var expr = ObjectExpression.createRoot();
  213. expr.includePath( "a.b" );
  214. assertExpectedResult({
  215. source: {"_id":0, "a":{ "c":6}, "z":2 },
  216. expression: expr,
  217. expected: {"_id":0, "a":{} },
  218. expectedDependencies: {"_id":1, "a.b":1},
  219. expectedJsonRepresentation: {"a":{ "b":{$const:null}} }
  220. });
  221. },
  222. "should be able to attempt to include a nested field within a non object": function testIncludeNestedWithinNonObject(){
  223. /** Attempt to include a nested field within a non object. */
  224. var expr = ObjectExpression.createRoot();
  225. expr.includePath( "a.b" );
  226. assertExpectedResult({
  227. source: {"_id":0, "a":2, "z":2},
  228. expression: expr,
  229. expected: {"_id":0},
  230. expectedDependencies: {"_id":1, "a.b":1},
  231. expectedJsonRepresentation: {"a":{ "b":{$const:null}} }
  232. });
  233. },
  234. "should be able to include a nested field within an array": function testIncludeArrayNested(){
  235. /** Include a nested field within an array. */
  236. var expr = ObjectExpression.createRoot();
  237. expr.includePath( "a.b" );
  238. assertExpectedResult({
  239. source: {_id:0,a:[{b:5,c:6},{b:2,c:9},{c:7},[],2],z:1},
  240. expression: expr,
  241. expected: {_id:0,a:[{b:5},{b:2},{}]},
  242. expectedDependencies: {"_id":1, "a.b":1},
  243. expectedJsonRepresentation: {"a":{ "b":{$const:null}} }
  244. });
  245. },
  246. "should NOT include non-root '_id' field implicitly": function testExcludeNonRootId(){
  247. /** Don't include not root '_id' field implicitly. */
  248. var expr = ObjectExpression.createRoot();
  249. expr.includePath( "a.b" );
  250. assertExpectedResult({
  251. source: {"_id":0, "a":{ "_id":1, "b":1} },
  252. expression: expr,
  253. expected: {"_id":0, "a":{ "b":1} },
  254. expectedDependencies: {"_id":1, "a.b":1},
  255. expectedJsonRepresentation: {"a":{ "b":{$const:null}}}
  256. });
  257. },
  258. "should be able to project a computed expression": function testComputed(){
  259. /** Project a computed expression. */
  260. var expr = ObjectExpression.createRoot();
  261. expr.addField("a", new ConstantExpression(5));
  262. assertExpectedResult({
  263. source: {"_id":0},
  264. expression: expr,
  265. expected: {"_id":0, "a":5},
  266. expectedDependencies: {"_id":1},
  267. expectedJsonRepresentation: {"a":{ "$const":5} },
  268. expectedIsSimple: false
  269. });
  270. },
  271. "should be able to project a computed expression replacing an existing field": function testComputedReplacement(){
  272. /** Project a computed expression replacing an existing field. */
  273. var expr = ObjectExpression.createRoot();
  274. expr.addField("a", new ConstantExpression(5));
  275. assertExpectedResult({
  276. source: {"_id":0, "a":99},
  277. expression: expr,
  278. expected: {"_id": 0, "a": 5},
  279. expectedDependencies: {"_id":1},
  280. expectedJsonRepresentation: {"a": {"$const": 5}},
  281. expectedIsSimple: false
  282. });
  283. },
  284. "should NOT be able to project an undefined value": function testComputedUndefined(){
  285. /** An undefined value is not projected.. */
  286. var expr = ObjectExpression.createRoot();
  287. expr.addField("a", new ConstantExpression(undefined));
  288. assertExpectedResult({
  289. source: {"_id":0},
  290. expression: expr,
  291. expected: {"_id":0},
  292. expectedDependencies: {"_id":1},
  293. expectedJsonRepresentation: {a:{$const:undefined}},
  294. expectedIsSimple: false
  295. });
  296. },
  297. "should be able to project a computed expression replacing an existing field with Undefined": function testComputedUndefinedReplacement(){
  298. /** Project a computed expression replacing an existing field with Undefined. */
  299. var expr = ObjectExpression.createRoot();
  300. expr.addField("a", new ConstantExpression(5));
  301. assertExpectedResult({
  302. source: {"_id":0, "a":99},
  303. expression: expr,
  304. expected: {"_id":0, "a":5},
  305. expectedDependencies: {"_id":1},
  306. expectedJsonRepresentation: {"a":{"$const":5}},
  307. expectedIsSimple: false
  308. });
  309. },
  310. "should be able to project a null value": function testComputedNull(){
  311. /** A null value is projected. */
  312. var expr = ObjectExpression.createRoot();
  313. expr.addField("a", new ConstantExpression(null));
  314. assertExpectedResult({
  315. source: {"_id":0},
  316. expression: expr,
  317. expected: {"_id":0, "a":null},
  318. expectedDependencies: {"_id":1},
  319. expectedJsonRepresentation: {"a":{"$const":null}},
  320. expectedIsSimple: false
  321. });
  322. },
  323. "should be able to project a nested value": function testComputedNested(){
  324. /** A nested value is projected. */
  325. var expr = ObjectExpression.createRoot();
  326. expr.addField("a.b", new ConstantExpression(5));
  327. assertExpectedResult({
  328. source: {"_id":0},
  329. expression: expr,
  330. expected: {"_id":0, "a":{"b":5}},
  331. expectedDependencies: {"_id":1},
  332. expectedJsonRepresentation: {"a":{"b":{"$const":5}}},
  333. expectedIsSimple: false
  334. });
  335. },
  336. "should be able to project a field path": function testComputedFieldPath(){
  337. /** A field path is projected. */
  338. var expr = ObjectExpression.createRoot();
  339. expr.addField("a", FieldPathExpression.create("x"));
  340. assertExpectedResult({
  341. source: {"_id":0, "x":4},
  342. expression: expr,
  343. expected: {"_id":0, "a":4},
  344. expectedDependencies: {"_id":1, "x":1},
  345. expectedJsonRepresentation: {"a":"$x"},
  346. expectedIsSimple: false
  347. });
  348. },
  349. "should be able to project a nested field path": function testComputedNestedFieldPath(){
  350. /** A nested field path is projected. */
  351. var expr = ObjectExpression.createRoot();
  352. expr.addField("a.b", FieldPathExpression.create("x.y"));
  353. assertExpectedResult({
  354. source: {"_id":0, "x":{"y":4}},
  355. expression: expr,
  356. expected: {"_id":0, "a":{"b":4}},
  357. expectedDependencies: {"_id":1, "x.y":1},
  358. expectedJsonRepresentation: {"a":{"b":"$x.y"}},
  359. expectedIsSimple: false
  360. });
  361. },
  362. "should NOT project an empty subobject expression for a missing field": function testEmptyNewSubobject(){
  363. /** An empty subobject expression for a missing field is not projected. */
  364. var expr = ObjectExpression.createRoot();
  365. // Create a sub expression returning an empty object.
  366. var subExpr = ObjectExpression.create();
  367. subExpr.addField("b", FieldPathExpression.create("a.b"));
  368. expr.addField( "a", subExpr );
  369. assertExpectedResult({
  370. source: {"_id":0},
  371. expression: expr,
  372. expected: {"_id":0},
  373. expectedDependencies: {"_id":1, 'a.b':1},
  374. expectedJsonRepresentation: {a:{b:"$a.b"}},
  375. expectedIsSimple: false
  376. });
  377. },
  378. "should be able to project a non-empty new subobject": function testNonEmptyNewSubobject(){
  379. /** A non empty subobject expression for a missing field is projected. */
  380. var expr = ObjectExpression.createRoot();
  381. // Create a sub expression returning an empty object.
  382. var subExpr = ObjectExpression.create();
  383. subExpr.addField("b", new ConstantExpression(6));
  384. expr.addField( "a", subExpr );
  385. assertExpectedResult({
  386. source: {"_id":0},
  387. expression: expr,
  388. expected: {"_id":0, "a":{ "b":6} },
  389. expectedDependencies: {"_id":1},
  390. expectedJsonRepresentation: {a:{b:{$const:6}}},
  391. expectedIsSimple: false
  392. });
  393. },
  394. "should be able to project two computed fields within a common parent": function testAdjacentDottedComputedFields(){
  395. /** Two computed fields within a common parent. */
  396. var expr = ObjectExpression.createRoot();
  397. expr.addField("a.b", new ConstantExpression(6));
  398. expr.addField("a.c", new ConstantExpression(7));
  399. assertExpectedResult({
  400. source: {"_id":0},
  401. expression: expr,
  402. expected: {"_id":0, "a":{ "b":6, "c":7} },
  403. expectedDependencies: {"_id":1},
  404. expectedJsonRepresentation: {a:{b:{$const:6},c:{$const:7}}},
  405. expectedIsSimple: false
  406. });
  407. },
  408. "should be able to project two computed fields within a common parent (w/ one case dotted)": function testAdjacentDottedAndNestedComputedFields(){
  409. /** Two computed fields within a common parent, in one case dotted. */
  410. var expr = ObjectExpression.createRoot();
  411. expr.addField("a.b", new ConstantExpression(6));
  412. var subExpr = ObjectExpression.create();
  413. subExpr.addField("c", new ConstantExpression( 7 ) );
  414. expr.addField("a", subExpr);
  415. assertExpectedResult({
  416. source: {"_id":0},
  417. expression: expr,
  418. expected: {"_id":0, "a":{ "b":6, "c":7} },
  419. expectedDependencies: {"_id":1},
  420. expectedJsonRepresentation: {a:{b:{$const:6},c:{$const:7}}},
  421. expectedIsSimple: false
  422. });
  423. },
  424. "should be able to project two computed fields within a common parent (in another case dotted)": function testAdjacentNestedAndDottedComputedFields(){
  425. /** Two computed fields within a common parent, in another case dotted. */
  426. var expr = ObjectExpression.createRoot();
  427. var subExpr = ObjectExpression.create();
  428. subExpr.addField("b", new ConstantExpression(6));
  429. expr.addField("a", subExpr );
  430. expr.addField("a.c", new ConstantExpression(7));
  431. assertExpectedResult({
  432. source: {"_id":0},
  433. expression: expr,
  434. expected: {"_id":0, "a":{ "b":6, "c":7} },
  435. expectedDependencies: {"_id":1},
  436. expectedJsonRepresentation: {a:{b:{$const:6},c:{$const:7}}},
  437. expectedIsSimple: false
  438. });
  439. },
  440. "should be able to project two computed fields within a common parent (nested rather than dotted)": function testAdjacentNestedComputedFields(){
  441. /** Two computed fields within a common parent, nested rather than dotted. */
  442. var expr = ObjectExpression.createRoot();
  443. var subExpr1 = ObjectExpression.create();
  444. subExpr1.addField("b", new ConstantExpression(6));
  445. expr.addField("a", subExpr1);
  446. var subExpr2 = ObjectExpression.create();
  447. subExpr2.addField("c", new ConstantExpression(7));
  448. expr.addField("a", subExpr2);
  449. assertExpectedResult({
  450. source: {"_id":0},
  451. expression: expr,
  452. expected: {"_id":0, "a":{ "b":6, "c":7} },
  453. expectedDependencies: {"_id":1},
  454. expectedJsonRepresentation: {a:{b:{$const:6},c:{$const:7}}},
  455. expectedIsSimple: false
  456. });
  457. },
  458. "should be able to project multiple nested fields out of order without affecting output order": function testAdjacentNestedOrdering(){
  459. /** Field ordering is preserved when nested fields are merged. */
  460. var expr = ObjectExpression.createRoot();
  461. expr.addField("a.b", new ConstantExpression(6));
  462. var subExpr = ObjectExpression.create();
  463. // Add field 'd' then 'c'. Expect the same field ordering in the result doc.
  464. subExpr.addField("d", new ConstantExpression(7));
  465. subExpr.addField("c", new ConstantExpression(8));
  466. expr.addField("a", subExpr);
  467. assertExpectedResult({
  468. source: {"_id":0},
  469. expression: expr,
  470. expected: {"_id":0, "a":{ "b":6, "d":7, "c":8} },
  471. expectedDependencies: {"_id":1},
  472. expectedJsonRepresentation: {a:{b:{$const:6},d:{$const:7},c:{$const:8}}},
  473. expectedIsSimple: false
  474. });
  475. },
  476. "should be able to project adjacent fields two levels deep": function testMultipleNestedFields(){
  477. /** Adjacent fields two levels deep. */
  478. var expr = ObjectExpression.createRoot();
  479. expr.addField("a.b.c", new ConstantExpression(6));
  480. var bSubExpression = ObjectExpression.create();
  481. bSubExpression.addField("d", new ConstantExpression(7));
  482. var aSubExpression = ObjectExpression.create();
  483. aSubExpression.addField("b", bSubExpression);
  484. expr.addField("a", aSubExpression);
  485. assertExpectedResult({
  486. source:{_id:0},
  487. expression: expr,
  488. expected: {"_id":0, "a":{ "b":{ "c":6, "d":7}}},
  489. expectedDependencies:{_id:1},
  490. expectedJsonRepresentation:{"a":{"b":{"c":{$const:6},"d":{$const:7}}}},
  491. expectedIsSimple:false
  492. });
  493. var res = expr.evaluateDocument(new Variables(1, {_id:1}));
  494. },
  495. "should throw an Error if two expressions generate the same field": function testConflictingExpressionFields(){
  496. /** Two expressions cannot generate the same field. */
  497. var expr = ObjectExpression.createRoot();
  498. expr.addField("a", new ConstantExpression(5));
  499. assert.throws(function(){
  500. expr.addField("a", new ConstantExpression(6)); // Duplicate field.
  501. }, Error);
  502. },
  503. "should throw an Error if an expression field conflicts with an inclusion field": function testConflictingInclusionExpressionFields(){
  504. /** An expression field conflicts with an inclusion field. */
  505. var expr = ObjectExpression.createRoot();
  506. expr.includePath("a");
  507. assert.throws(function(){
  508. expr.addField("a", new ConstantExpression(6));
  509. }, Error);
  510. },
  511. "should throw an Error if an inclusion field conflicts with an expression field": function testConflictingExpressionInclusionFields(){
  512. /** An inclusion field conflicts with an expression field. */
  513. var expr = ObjectExpression.createRoot();
  514. expr.addField("a", new ConstantExpression(5));
  515. assert.throws(function(){
  516. expr.includePath("a");
  517. }, Error);
  518. },
  519. "should throw an Error if an object expression conflicts with a constant expression": function testConflictingObjectConstantExpressionFields(){
  520. /** An object expression conflicts with a constant expression. */
  521. var expr = ObjectExpression.createRoot();
  522. var subExpr = ObjectExpression.create();
  523. subExpr.includePath("b");
  524. expr.addField("a", subExpr);
  525. assert.throws(function(){
  526. expr.addField("a.b", new ConstantExpression(6));
  527. }, Error);
  528. },
  529. "should throw an Error if a constant expression conflicts with an object expression": function testConflictingConstantObjectExpressionFields(){
  530. /** A constant expression conflicts with an object expression. */
  531. var expr = ObjectExpression.createRoot();
  532. expr.addField("a.b", new ConstantExpression(6));
  533. var subExpr = ObjectExpression.create();
  534. subExpr.includePath("b");
  535. assert.throws(function(){
  536. expr.addField("a", subExpr);
  537. }, Error);
  538. },
  539. "should throw an Error if two nested expressions cannot generate the same field": function testConflictingNestedFields(){
  540. /** Two nested expressions cannot generate the same field. */
  541. var expr = ObjectExpression.createRoot();
  542. expr.addField("a.b", new ConstantExpression(5));
  543. assert.throws(function(){
  544. expr.addField("a.b", new ConstantExpression(6)); // Duplicate field.
  545. }, Error);
  546. },
  547. "should throw an Error if an expression is created for a subfield of another expression": function testConflictingFieldAndSubfield(){
  548. /** An expression cannot be created for a subfield of another expression. */
  549. var expr = ObjectExpression.createRoot();
  550. expr.addField("a", new ConstantExpression(5));
  551. assert.throws(function(){
  552. expr.addField("a.b", new ConstantExpression(5));
  553. }, Error);
  554. },
  555. "should throw an Error if an expression is created for a nested field of another expression.": function testConflictingFieldAndNestedField(){
  556. /** An expression cannot be created for a nested field of another expression. */
  557. var expr = ObjectExpression.createRoot();
  558. expr.addField("a", new ConstantExpression(5));
  559. var subExpr = ObjectExpression.create();
  560. subExpr.addField("b", new ConstantExpression(5));
  561. assert.throws(function(){
  562. expr.addField("a", subExpr);
  563. }, Error);
  564. },
  565. "should throw an Error if an expression is created for a parent field of another expression": function testConflictingSubfieldAndField(){
  566. /** An expression cannot be created for a parent field of another expression. */
  567. var expr = ObjectExpression.createRoot();
  568. expr.addField("a.b", new ConstantExpression(5));
  569. assert.throws(function(){
  570. expr.addField("a", new ConstantExpression(5));
  571. }, Error);
  572. },
  573. "should throw an Error if an expression is created for a parent of a nested field": function testConflictingNestedFieldAndField(){
  574. /** An expression cannot be created for a parent of a nested field. */
  575. var expr = ObjectExpression.createRoot();
  576. var subExpr = ObjectExpression.create();
  577. subExpr.addField("b", new ConstantExpression(5));
  578. expr.addField("a", subExpr);
  579. assert.throws(function(){
  580. expr.addField("a", new ConstantExpression(5));
  581. }, Error);
  582. },
  583. "should be able to evaluate expressions in general": function testEvaluate(){
  584. /**
  585. * evaluate() does not supply an inclusion document.
  586. * Inclusion spec'd fields are not included.
  587. * (Inclusion specs are not generally expected/allowed in cases where evaluate is called instead of addToDocument.)
  588. */
  589. var expr = ObjectExpression.createRoot();
  590. expr.includePath("a");
  591. expr.addField("b", new ConstantExpression(5));
  592. expr.addField("c", FieldPathExpression.create("a"));
  593. var res = expr.evaluateInternal(new Variables(1, {_id:0, a:1}));
  594. assert.deepEqual({"b":5, "c":1}, res);
  595. }
  596. }
  597. }
  598. };
  599. if (!module.parent)(new(require("mocha"))()).ui("exports").reporter("spec").addFile(__filename).run(process.exit);