ObjectExpression.js 25 KB

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