ObjectExpression.js 25 KB

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