query.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. import { isQueryCommand, isComparisonCommand, QUERY_COMMANDS_LITERAL } from '../commands/query';
  2. import { isLogicCommand, LOGIC_COMMANDS_LITERAL } from '../commands/logic';
  3. import { SYMBOL_UNSET_FIELD_NAME, SYMBOL_GEO_POINT } from '../helper/symbol';
  4. import { getType, isObject, isArray, isRegExp, isDate } from '../utils/type';
  5. import { operatorToString } from '../operator-map';
  6. import { flattenQueryObject, isConversionRequired, encodeInternalDataType } from './common';
  7. import { stringifyByEJSON } from '../utils/utils';
  8. import { Validate } from '../validate';
  9. export class QuerySerializer {
  10. constructor() { }
  11. static encode(query) {
  12. const encoder = new QueryEncoder();
  13. return encoder.encodeQuery(query);
  14. }
  15. static encodeEJSON(query) {
  16. const encoder = new QueryEncoder();
  17. return stringifyByEJSON(encoder.encodeQuery(query));
  18. }
  19. }
  20. class QueryEncoder {
  21. encodeQuery(query, key) {
  22. if (isConversionRequired(query)) {
  23. if (isLogicCommand(query)) {
  24. return this.encodeLogicCommand(query);
  25. }
  26. else if (isQueryCommand(query)) {
  27. return this.encodeQueryCommand(query);
  28. }
  29. else if (isRegExp(query)) {
  30. return { [key]: this.encodeRegExp(query) };
  31. }
  32. else if (isDate(query)) {
  33. return { [key]: query };
  34. }
  35. else {
  36. return { [key]: this.encodeQueryObject(query) };
  37. }
  38. }
  39. else {
  40. if (isObject(query)) {
  41. return this.encodeQueryObject(query);
  42. }
  43. else {
  44. return query;
  45. }
  46. }
  47. }
  48. encodeRegExp(query) {
  49. return {
  50. $regularExpression: {
  51. pattern: query.source,
  52. options: query.flags
  53. }
  54. };
  55. }
  56. encodeLogicCommand(query) {
  57. switch (query.operator) {
  58. case LOGIC_COMMANDS_LITERAL.NOR:
  59. case LOGIC_COMMANDS_LITERAL.AND:
  60. case LOGIC_COMMANDS_LITERAL.OR: {
  61. const $op = operatorToString(query.operator);
  62. const subqueries = query.operands.map((oprand) => this.encodeQuery(oprand, query.fieldName));
  63. return {
  64. [$op]: subqueries
  65. };
  66. }
  67. case LOGIC_COMMANDS_LITERAL.NOT: {
  68. const $op = operatorToString(query.operator);
  69. const operatorExpression = query.operands[0];
  70. if (isRegExp(operatorExpression)) {
  71. return {
  72. [query.fieldName]: {
  73. [$op]: this.encodeRegExp(operatorExpression)
  74. }
  75. };
  76. }
  77. else {
  78. const subqueries = this.encodeQuery(operatorExpression)[query.fieldName];
  79. return {
  80. [query.fieldName]: {
  81. [$op]: subqueries
  82. }
  83. };
  84. }
  85. }
  86. default: {
  87. const $op = operatorToString(query.operator);
  88. if (query.operands.length === 1) {
  89. const subquery = this.encodeQuery(query.operands[0]);
  90. return {
  91. [$op]: subquery
  92. };
  93. }
  94. else {
  95. const subqueries = query.operands.map(this.encodeQuery.bind(this));
  96. return {
  97. [$op]: subqueries
  98. };
  99. }
  100. }
  101. }
  102. }
  103. encodeQueryCommand(query) {
  104. if (isComparisonCommand(query)) {
  105. return this.encodeComparisonCommand(query);
  106. }
  107. else {
  108. return this.encodeComparisonCommand(query);
  109. }
  110. }
  111. encodeComparisonCommand(query) {
  112. if (query.fieldName === SYMBOL_UNSET_FIELD_NAME) {
  113. throw new Error('Cannot encode a comparison command with unset field name');
  114. }
  115. const $op = operatorToString(query.operator);
  116. switch (query.operator) {
  117. case QUERY_COMMANDS_LITERAL.EQ:
  118. case QUERY_COMMANDS_LITERAL.NEQ:
  119. case QUERY_COMMANDS_LITERAL.LT:
  120. case QUERY_COMMANDS_LITERAL.LTE:
  121. case QUERY_COMMANDS_LITERAL.GT:
  122. case QUERY_COMMANDS_LITERAL.GTE:
  123. case QUERY_COMMANDS_LITERAL.ELEM_MATCH:
  124. case QUERY_COMMANDS_LITERAL.EXISTS:
  125. case QUERY_COMMANDS_LITERAL.SIZE:
  126. case QUERY_COMMANDS_LITERAL.MOD: {
  127. return {
  128. [query.fieldName]: {
  129. [$op]: encodeInternalDataType(query.operands[0])
  130. }
  131. };
  132. }
  133. case QUERY_COMMANDS_LITERAL.IN:
  134. case QUERY_COMMANDS_LITERAL.NIN:
  135. case QUERY_COMMANDS_LITERAL.ALL: {
  136. return {
  137. [query.fieldName]: {
  138. [$op]: encodeInternalDataType(query.operands)
  139. }
  140. };
  141. }
  142. case QUERY_COMMANDS_LITERAL.GEO_NEAR: {
  143. const options = query.operands[0];
  144. return {
  145. [query.fieldName]: {
  146. $nearSphere: {
  147. $geometry: options.geometry.toJSON(),
  148. $maxDistance: options.maxDistance,
  149. $minDistance: options.minDistance
  150. }
  151. }
  152. };
  153. }
  154. case QUERY_COMMANDS_LITERAL.GEO_WITHIN: {
  155. const options = query.operands[0];
  156. if (options.centerSphere) {
  157. Validate.isCentersPhere(options.centerSphere);
  158. const centerSphere = options.centerSphere;
  159. if (centerSphere[0]._internalType === SYMBOL_GEO_POINT) {
  160. return {
  161. [query.fieldName]: {
  162. $geoWithin: {
  163. $centerSphere: [centerSphere[0].toJSON().coordinates, centerSphere[1]]
  164. }
  165. }
  166. };
  167. }
  168. return {
  169. [query.fieldName]: {
  170. $geoWithin: {
  171. $centerSphere: options.centerSphere
  172. }
  173. }
  174. };
  175. }
  176. return {
  177. [query.fieldName]: {
  178. $geoWithin: {
  179. $geometry: options.geometry.toJSON()
  180. }
  181. }
  182. };
  183. }
  184. case QUERY_COMMANDS_LITERAL.GEO_INTERSECTS: {
  185. const options = query.operands[0];
  186. return {
  187. [query.fieldName]: {
  188. $geoIntersects: {
  189. $geometry: options.geometry.toJSON()
  190. }
  191. }
  192. };
  193. }
  194. default: {
  195. return {
  196. [query.fieldName]: {
  197. [$op]: encodeInternalDataType(query.operands[0])
  198. }
  199. };
  200. }
  201. }
  202. }
  203. encodeQueryObject(query) {
  204. const flattened = flattenQueryObject(query);
  205. for (const key in flattened) {
  206. const val = flattened[key];
  207. if (isLogicCommand(val)) {
  208. flattened[key] = val._setFieldName(key);
  209. const condition = this.encodeLogicCommand(flattened[key]);
  210. this.mergeConditionAfterEncode(flattened, condition, key);
  211. }
  212. else if (isComparisonCommand(val)) {
  213. flattened[key] = val._setFieldName(key);
  214. const condition = this.encodeComparisonCommand(flattened[key]);
  215. this.mergeConditionAfterEncode(flattened, condition, key);
  216. }
  217. else if (isConversionRequired(val)) {
  218. flattened[key] = encodeInternalDataType(val);
  219. }
  220. }
  221. return flattened;
  222. }
  223. mergeConditionAfterEncode(query, condition, key) {
  224. if (!condition[key]) {
  225. delete query[key];
  226. }
  227. for (const conditionKey in condition) {
  228. if (query[conditionKey]) {
  229. if (isArray(query[conditionKey])) {
  230. query[conditionKey] = query[conditionKey].concat(condition[conditionKey]);
  231. }
  232. else if (isObject(query[conditionKey])) {
  233. if (isObject(condition[conditionKey])) {
  234. Object.assign(query, condition);
  235. }
  236. else {
  237. console.warn(`unmergable condition, query is object but condition is ${getType(condition)}, can only overwrite`, condition, key);
  238. query[conditionKey] = condition[conditionKey];
  239. }
  240. }
  241. else {
  242. console.warn(`to-merge query is of type ${getType(query)}, can only overwrite`, query, condition, key);
  243. query[conditionKey] = condition[conditionKey];
  244. }
  245. }
  246. else {
  247. query[conditionKey] = condition[conditionKey];
  248. }
  249. }
  250. }
  251. }