PageRenderTime 27ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/server/node_modules/mongoose/lib/cast.js

https://gitlab.com/Cactaceae-a/zomato-master
JavaScript | 369 lines | 304 code | 37 blank | 28 comment | 133 complexity | d6fee51db783a7f861eb8720119c5157 MD5 | raw file
  1. 'use strict';
  2. /*!
  3. * Module dependencies.
  4. */
  5. const CastError = require('./error/cast');
  6. const StrictModeError = require('./error/strict');
  7. const Types = require('./schema/index');
  8. const castTextSearch = require('./schema/operators/text');
  9. const get = require('./helpers/get');
  10. const getConstructorName = require('./helpers/getConstructorName');
  11. const getSchemaDiscriminatorByValue = require('./helpers/discriminator/getSchemaDiscriminatorByValue');
  12. const isOperator = require('./helpers/query/isOperator');
  13. const util = require('util');
  14. const isObject = require('./helpers/isObject');
  15. const isMongooseObject = require('./helpers/isMongooseObject');
  16. const ALLOWED_GEOWITHIN_GEOJSON_TYPES = ['Polygon', 'MultiPolygon'];
  17. /**
  18. * Handles internal casting for query filters.
  19. *
  20. * @param {Schema} schema
  21. * @param {Object} obj Object to cast
  22. * @param {Object} options the query options
  23. * @param {Query} context passed to setters
  24. * @api private
  25. */
  26. module.exports = function cast(schema, obj, options, context) {
  27. if (Array.isArray(obj)) {
  28. throw new Error('Query filter must be an object, got an array ', util.inspect(obj));
  29. }
  30. if (obj == null) {
  31. return obj;
  32. }
  33. // bson 1.x has the unfortunate tendency to remove filters that have a top-level
  34. // `_bsontype` property. But we should still allow ObjectIds because
  35. // `Collection#find()` has a special case to support `find(objectid)`.
  36. // Should remove this when we upgrade to bson 4.x. See gh-8222, gh-8268
  37. if (obj.hasOwnProperty('_bsontype') && obj._bsontype !== 'ObjectID') {
  38. delete obj._bsontype;
  39. }
  40. if (schema != null && schema.discriminators != null && obj[schema.options.discriminatorKey] != null) {
  41. schema = getSchemaDiscriminatorByValue(schema, obj[schema.options.discriminatorKey]) || schema;
  42. }
  43. const paths = Object.keys(obj);
  44. let i = paths.length;
  45. let _keys;
  46. let any$conditionals;
  47. let schematype;
  48. let nested;
  49. let path;
  50. let type;
  51. let val;
  52. options = options || {};
  53. while (i--) {
  54. path = paths[i];
  55. val = obj[path];
  56. if (path === '$or' || path === '$nor' || path === '$and') {
  57. if (!Array.isArray(val)) {
  58. throw new CastError('Array', val, path);
  59. }
  60. for (let k = 0; k < val.length; ++k) {
  61. if (val[k] == null || typeof val[k] !== 'object') {
  62. throw new CastError('Object', val[k], path + '.' + k);
  63. }
  64. val[k] = cast(schema, val[k], options, context);
  65. }
  66. } else if (path === '$where') {
  67. type = typeof val;
  68. if (type !== 'string' && type !== 'function') {
  69. throw new Error('Must have a string or function for $where');
  70. }
  71. if (type === 'function') {
  72. obj[path] = val.toString();
  73. }
  74. continue;
  75. } else if (path === '$expr') {
  76. if (typeof val !== 'object' || val == null) {
  77. throw new Error('`$expr` must be an object');
  78. }
  79. continue;
  80. } else if (path === '$elemMatch') {
  81. val = cast(schema, val, options, context);
  82. } else if (path === '$text') {
  83. val = castTextSearch(val, path);
  84. } else {
  85. if (!schema) {
  86. // no casting for Mixed types
  87. continue;
  88. }
  89. schematype = schema.path(path);
  90. // Check for embedded discriminator paths
  91. if (!schematype) {
  92. const split = path.split('.');
  93. let j = split.length;
  94. while (j--) {
  95. const pathFirstHalf = split.slice(0, j).join('.');
  96. const pathLastHalf = split.slice(j).join('.');
  97. const _schematype = schema.path(pathFirstHalf);
  98. const discriminatorKey = get(_schematype, 'schema.options.discriminatorKey');
  99. // gh-6027: if we haven't found the schematype but this path is
  100. // underneath an embedded discriminator and the embedded discriminator
  101. // key is in the query, use the embedded discriminator schema
  102. if (_schematype != null &&
  103. get(_schematype, 'schema.discriminators') != null &&
  104. discriminatorKey != null &&
  105. pathLastHalf !== discriminatorKey) {
  106. const discriminatorVal = get(obj, pathFirstHalf + '.' + discriminatorKey);
  107. if (discriminatorVal != null) {
  108. schematype = _schematype.schema.discriminators[discriminatorVal].
  109. path(pathLastHalf);
  110. }
  111. }
  112. }
  113. }
  114. if (!schematype) {
  115. // Handle potential embedded array queries
  116. const split = path.split('.');
  117. let j = split.length;
  118. let pathFirstHalf;
  119. let pathLastHalf;
  120. let remainingConds;
  121. // Find the part of the var path that is a path of the Schema
  122. while (j--) {
  123. pathFirstHalf = split.slice(0, j).join('.');
  124. schematype = schema.path(pathFirstHalf);
  125. if (schematype) {
  126. break;
  127. }
  128. }
  129. // If a substring of the input path resolves to an actual real path...
  130. if (schematype) {
  131. // Apply the casting; similar code for $elemMatch in schema/array.js
  132. if (schematype.caster && schematype.caster.schema) {
  133. remainingConds = {};
  134. pathLastHalf = split.slice(j).join('.');
  135. remainingConds[pathLastHalf] = val;
  136. obj[path] = cast(schematype.caster.schema, remainingConds, options, context)[pathLastHalf];
  137. } else {
  138. obj[path] = val;
  139. }
  140. continue;
  141. }
  142. if (isObject(val)) {
  143. // handle geo schemas that use object notation
  144. // { loc: { long: Number, lat: Number }
  145. let geo = '';
  146. if (val.$near) {
  147. geo = '$near';
  148. } else if (val.$nearSphere) {
  149. geo = '$nearSphere';
  150. } else if (val.$within) {
  151. geo = '$within';
  152. } else if (val.$geoIntersects) {
  153. geo = '$geoIntersects';
  154. } else if (val.$geoWithin) {
  155. geo = '$geoWithin';
  156. }
  157. if (geo) {
  158. const numbertype = new Types.Number('__QueryCasting__');
  159. let value = val[geo];
  160. if (val.$maxDistance != null) {
  161. val.$maxDistance = numbertype.castForQueryWrapper({
  162. val: val.$maxDistance,
  163. context: context
  164. });
  165. }
  166. if (val.$minDistance != null) {
  167. val.$minDistance = numbertype.castForQueryWrapper({
  168. val: val.$minDistance,
  169. context: context
  170. });
  171. }
  172. if (geo === '$within') {
  173. const withinType = value.$center
  174. || value.$centerSphere
  175. || value.$box
  176. || value.$polygon;
  177. if (!withinType) {
  178. throw new Error('Bad $within parameter: ' + JSON.stringify(val));
  179. }
  180. value = withinType;
  181. } else if (geo === '$near' &&
  182. typeof value.type === 'string' && Array.isArray(value.coordinates)) {
  183. // geojson; cast the coordinates
  184. value = value.coordinates;
  185. } else if ((geo === '$near' || geo === '$nearSphere' || geo === '$geoIntersects') &&
  186. value.$geometry && typeof value.$geometry.type === 'string' &&
  187. Array.isArray(value.$geometry.coordinates)) {
  188. if (value.$maxDistance != null) {
  189. value.$maxDistance = numbertype.castForQueryWrapper({
  190. val: value.$maxDistance,
  191. context: context
  192. });
  193. }
  194. if (value.$minDistance != null) {
  195. value.$minDistance = numbertype.castForQueryWrapper({
  196. val: value.$minDistance,
  197. context: context
  198. });
  199. }
  200. if (isMongooseObject(value.$geometry)) {
  201. value.$geometry = value.$geometry.toObject({
  202. transform: false,
  203. virtuals: false
  204. });
  205. }
  206. value = value.$geometry.coordinates;
  207. } else if (geo === '$geoWithin') {
  208. if (value.$geometry) {
  209. if (isMongooseObject(value.$geometry)) {
  210. value.$geometry = value.$geometry.toObject({ virtuals: false });
  211. }
  212. const geoWithinType = value.$geometry.type;
  213. if (ALLOWED_GEOWITHIN_GEOJSON_TYPES.indexOf(geoWithinType) === -1) {
  214. throw new Error('Invalid geoJSON type for $geoWithin "' +
  215. geoWithinType + '", must be "Polygon" or "MultiPolygon"');
  216. }
  217. value = value.$geometry.coordinates;
  218. } else {
  219. value = value.$box || value.$polygon || value.$center ||
  220. value.$centerSphere;
  221. if (isMongooseObject(value)) {
  222. value = value.toObject({ virtuals: false });
  223. }
  224. }
  225. }
  226. _cast(value, numbertype, context);
  227. continue;
  228. }
  229. }
  230. if (schema.nested[path]) {
  231. continue;
  232. }
  233. if (options.upsert && options.strict) {
  234. if (options.strict === 'throw') {
  235. throw new StrictModeError(path);
  236. }
  237. throw new StrictModeError(path, 'Path "' + path + '" is not in ' +
  238. 'schema, strict mode is `true`, and upsert is `true`.');
  239. } if (options.strictQuery === 'throw') {
  240. throw new StrictModeError(path, 'Path "' + path + '" is not in ' +
  241. 'schema and strictQuery is \'throw\'.');
  242. } else if (options.strictQuery) {
  243. delete obj[path];
  244. }
  245. } else if (val == null) {
  246. continue;
  247. } else if (getConstructorName(val) === 'Object') {
  248. any$conditionals = Object.keys(val).some(isOperator);
  249. if (!any$conditionals) {
  250. obj[path] = schematype.castForQueryWrapper({
  251. val: val,
  252. context: context
  253. });
  254. } else {
  255. const ks = Object.keys(val);
  256. let $cond;
  257. let k = ks.length;
  258. while (k--) {
  259. $cond = ks[k];
  260. nested = val[$cond];
  261. if ($cond === '$not') {
  262. if (nested && schematype) {
  263. _keys = Object.keys(nested);
  264. if (_keys.length && isOperator(_keys[0])) {
  265. for (const key in nested) {
  266. nested[key] = schematype.castForQueryWrapper({
  267. $conditional: key,
  268. val: nested[key],
  269. context: context
  270. });
  271. }
  272. } else {
  273. val[$cond] = schematype.castForQueryWrapper({
  274. $conditional: $cond,
  275. val: nested,
  276. context: context
  277. });
  278. }
  279. continue;
  280. }
  281. } else {
  282. val[$cond] = schematype.castForQueryWrapper({
  283. $conditional: $cond,
  284. val: nested,
  285. context: context
  286. });
  287. }
  288. }
  289. }
  290. } else if (Array.isArray(val) && ['Buffer', 'Array'].indexOf(schematype.instance) === -1) {
  291. const casted = [];
  292. const valuesArray = val;
  293. for (const _val of valuesArray) {
  294. casted.push(schematype.castForQueryWrapper({
  295. val: _val,
  296. context: context
  297. }));
  298. }
  299. obj[path] = { $in: casted };
  300. } else {
  301. obj[path] = schematype.castForQueryWrapper({
  302. val: val,
  303. context: context
  304. });
  305. }
  306. }
  307. }
  308. return obj;
  309. };
  310. function _cast(val, numbertype, context) {
  311. if (Array.isArray(val)) {
  312. val.forEach(function(item, i) {
  313. if (Array.isArray(item) || isObject(item)) {
  314. return _cast(item, numbertype, context);
  315. }
  316. val[i] = numbertype.castForQueryWrapper({ val: item, context: context });
  317. });
  318. } else {
  319. const nearKeys = Object.keys(val);
  320. let nearLen = nearKeys.length;
  321. while (nearLen--) {
  322. const nkey = nearKeys[nearLen];
  323. const item = val[nkey];
  324. if (Array.isArray(item) || isObject(item)) {
  325. _cast(item, numbertype, context);
  326. val[nkey] = item;
  327. } else {
  328. val[nkey] = numbertype.castForQuery({ val: item, context: context });
  329. }
  330. }
  331. }
  332. }