UNPKG

8.14 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.parseWhere = exports.parseMeasurement = exports.Measurement = exports.Expression = void 0;
4const grammar_1 = require("./grammar");
5function regexHasFlags(re) {
6 if (typeof re.flags !== "undefined") {
7 return re.flags.length > 0;
8 }
9 return !re.toString().endsWith("/");
10}
11/**
12 * Expression is used to build filtering expressions, like those used in WHERE
13 * clauses. It can be used for fluent and safe building of queries using
14 * untrusted input.
15 *
16 * @example
17 * e => e
18 * .field('host').equals.value('ares.peet.io')
19 * .or
20 * .field('host').matches(/example\.com$/)
21 * .or
22 * .expr(e => e
23 * .field('country').equals.value('US')
24 * .and
25 * .field('state').equals.value('WA'));
26 *
27 * // Generates:
28 * // "host" = 'ares.peet.io' OR "host" ~= /example\.com$/ OR \
29 * // ("county" = 'US' AND "state" = 'WA')
30 */
31class Expression {
32 constructor() {
33 this._query = [];
34 }
35 /**
36 * Inserts a tag reference into the expression; the name will be
37 * automatically escaped.
38 * @param name
39 * @return
40 */
41 tag(name) {
42 this.field(name);
43 return this;
44 }
45 /**
46 * Inserts a field reference into the expression; the name will be
47 * automatically escaped.
48 * @param name
49 * @return
50 */
51 field(name) {
52 this._query.push(grammar_1.escape.quoted(name));
53 return this;
54 }
55 /**
56 * Inserts a subexpression; invokes the function with a new expression
57 * that can be chained on.
58 * @param fn
59 * @return
60 * @example
61 * e.field('a').equals.value('b')
62 * .or.expr(e =>
63 * e.field('b').equals.value('b')
64 * .and.field('a').equals.value('c'))
65 * .toString()
66 * // "a" = 'b' OR ("b" = 'b' AND "a" = 'c')
67 */
68 exp(fn) {
69 this._query.push("(" + fn(new Expression()).toString() + ")");
70 return this;
71 }
72 /**
73 * Value chains on a value to the expression.
74 *
75 * - Numbers will be inserted verbatim
76 * - Strings will be escaped and inserted
77 * - Booleans will be inserted correctly
78 * - Dates will be formatted and inserted correctly, including INanoDates.
79 * - Regular expressions will be inserted correctly, however an error will
80 * be thrown if they contain flags, as regex flags do not work in Influx
81 * - Otherwise we'll try to call `.toString()` on the value, throwing
82 * if we cannot do so.
83 *
84 * @param value
85 * @return
86 */
87 value(value) {
88 switch (typeof value) {
89 case "number":
90 this._query.push(value.toString());
91 return this;
92 case "string":
93 this._query.push(grammar_1.escape.stringLit(value));
94 return this;
95 case "boolean":
96 this._query.push(value ? "TRUE" : "FALSE");
97 return this;
98 default:
99 if (value instanceof Date) {
100 this._query.push(grammar_1.formatDate(value));
101 return this;
102 }
103 if (value instanceof RegExp) {
104 if (regexHasFlags(value)) {
105 throw new Error("Attempted to query using a regex with flags, " +
106 "but Influx doesn't support flags in queries.");
107 }
108 this._query.push("/" + value.source + "/");
109 return this;
110 }
111 if (value && typeof value.toString === "function") {
112 this._query.push(value.toString());
113 return this;
114 }
115 throw new Error("node-influx doesn't know how to encode the provided value into a " +
116 "query. If you think this is a bug, open an issue here: https://git.io/influx-err");
117 }
118 }
119 /**
120 * Chains on an AND clause to the expression.
121 */
122 get and() {
123 this._query.push("AND");
124 return this;
125 }
126 /**
127 * Chains on an OR clause to the expression.
128 */
129 get or() {
130 this._query.push("OR");
131 return this;
132 }
133 /**
134 * Chains on a `+` operator to the expression.
135 */
136 get plus() {
137 this._query.push("+");
138 return this;
139 }
140 /**
141 * Chains on a `*` operator to the expression.
142 */
143 get times() {
144 this._query.push("*");
145 return this;
146 }
147 /**
148 * Chains on a `-` operator to the expression.
149 */
150 get minus() {
151 this._query.push("-");
152 return this;
153 }
154 /**
155 * Chains on a `/` operator to the expression.
156 */
157 get div() {
158 this._query.push("/");
159 return this;
160 }
161 /**
162 * Chains on a `=` conditional to the expression.
163 */
164 get equals() {
165 this._query.push("=");
166 return this;
167 }
168 /**
169 * Chains on a `=~` conditional to the expression to match regexes.
170 */
171 get matches() {
172 this._query.push("=~");
173 return this;
174 }
175 /**
176 * Chains on a `!`` conditional to the expression to match regexes.
177 */
178 get doesntMatch() {
179 this._query.push("!~");
180 return this;
181 }
182 /**
183 * Chains on a `!=` conditional to the expression.
184 */
185 get notEqual() {
186 this._query.push("!=");
187 return this;
188 }
189 /**
190 * Chains on a `>` conditional to the expression.
191 */
192 get gt() {
193 this._query.push(">");
194 return this;
195 }
196 /**
197 * Chains on a `>=` conditional to the expression.
198 */
199 get gte() {
200 this._query.push(">=");
201 return this;
202 }
203 /**
204 * Chains on a `<` conditional to the expression.
205 */
206 get lt() {
207 this._query.push("<");
208 return this;
209 }
210 /**
211 * Chains on a `<=` conditional to the expression.
212 */
213 get lte() {
214 this._query.push("<=");
215 return this;
216 }
217 /**
218 * Converts the expression into its InfluxQL representation.
219 * @return
220 */
221 toString() {
222 return this._query.join(" ");
223 }
224}
225exports.Expression = Expression;
226/**
227 * Measurement creates a reference to a particular measurement. You can
228 * reference it solely by its name, but you can also specify the retention
229 * policy and database it lives under.
230 *
231 * @example
232 * m.name('my_measurement') // "my_measurement"
233 * m.name('my_measurement').policy('one_day') // "one_day"."my_measurement"
234 * m.name('my_measurement').policy('one_day').db('mydb') // "mydb"."one_day"."my_measurement"
235 */
236class Measurement {
237 constructor() {
238 this._parts = [null, null, null];
239 }
240 /**
241 * Sets the measurement name.
242 * @param name
243 * @return
244 */
245 name(name) {
246 this._parts[2] = name;
247 return this;
248 }
249 /**
250 * Sets the retention policy name.
251 * @param retentionPolicy
252 * @return
253 */
254 policy(retentionPolicy) {
255 this._parts[1] = retentionPolicy;
256 return this;
257 }
258 /**
259 * Sets the database name.
260 * @param db
261 * @return
262 */
263 db(db) {
264 this._parts[0] = db;
265 return this;
266 }
267 /**
268 * Converts the measurement into its InfluxQL representation.
269 * @return
270 * @throws {Error} if a measurement name is not provided
271 */
272 toString() {
273 if (!this._parts[2]) {
274 throw new Error(`You must specify a measurement name to query! Got \`${this._parts[2]}\``);
275 }
276 return this._parts
277 .filter((p) => Boolean(p))
278 .map((p) => grammar_1.escape.quoted(p))
279 .join(".");
280 }
281}
282exports.Measurement = Measurement;
283function parseMeasurement(q) {
284 if (typeof q.measurement === "function") {
285 return q.measurement(new Measurement()).toString();
286 }
287 return q.measurement;
288}
289exports.parseMeasurement = parseMeasurement;
290function parseWhere(q) {
291 if (typeof q.where === "function") {
292 return q.where(new Expression()).toString();
293 }
294 return q.where;
295}
296exports.parseWhere = parseWhere;