ObjectExpression.js 25 KB

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