ObjectExpression.js 25 KB

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