gdelt-ts-client 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +455 -0
- package/dist/client.d.ts +228 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +478 -0
- package/dist/client.js.map +1 -0
- package/dist/constants/index.d.ts +184 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/constants/index.js +193 -0
- package/dist/constants/index.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/api-parameters.d.ts +201 -0
- package/dist/interfaces/api-parameters.d.ts.map +1 -0
- package/dist/interfaces/api-parameters.js +3 -0
- package/dist/interfaces/api-parameters.js.map +1 -0
- package/dist/interfaces/api-responses.d.ts +258 -0
- package/dist/interfaces/api-responses.d.ts.map +1 -0
- package/dist/interfaces/api-responses.js +6 -0
- package/dist/interfaces/api-responses.js.map +1 -0
- package/dist/types/enhanced-types.d.ts +227 -0
- package/dist/types/enhanced-types.d.ts.map +1 -0
- package/dist/types/enhanced-types.js +89 -0
- package/dist/types/enhanced-types.js.map +1 -0
- package/dist/types/query-builder.d.ts +252 -0
- package/dist/types/query-builder.d.ts.map +1 -0
- package/dist/types/query-builder.js +458 -0
- package/dist/types/query-builder.js.map +1 -0
- package/dist/types/type-guards.d.ts +110 -0
- package/dist/types/type-guards.d.ts.map +1 -0
- package/dist/types/type-guards.js +271 -0
- package/dist/types/type-guards.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhanced query builder with TypeScript template literal types and IntelliSense support
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Fluent query builder for constructing complex GDELT queries with type safety
|
|
6
|
+
*/
|
|
7
|
+
export declare class QueryBuilder {
|
|
8
|
+
private components;
|
|
9
|
+
/**
|
|
10
|
+
* Add a basic search term or phrase
|
|
11
|
+
*/
|
|
12
|
+
search(term: string): this;
|
|
13
|
+
/**
|
|
14
|
+
* Add a phrase search (exact match)
|
|
15
|
+
*/
|
|
16
|
+
phrase(phrase: string): this;
|
|
17
|
+
/**
|
|
18
|
+
* Add multiple terms with OR logic
|
|
19
|
+
*/
|
|
20
|
+
anyOf(...terms: string[]): this;
|
|
21
|
+
/**
|
|
22
|
+
* Add multiple terms with AND logic
|
|
23
|
+
*/
|
|
24
|
+
allOf(...terms: string[]): this;
|
|
25
|
+
/**
|
|
26
|
+
* Exclude a term or phrase
|
|
27
|
+
*/
|
|
28
|
+
not(term: string): this;
|
|
29
|
+
/**
|
|
30
|
+
* Filter by domain
|
|
31
|
+
*/
|
|
32
|
+
fromDomain(domain: string, exact?: boolean): this;
|
|
33
|
+
/**
|
|
34
|
+
* Filter by source country
|
|
35
|
+
*/
|
|
36
|
+
fromCountry(country: string): this;
|
|
37
|
+
/**
|
|
38
|
+
* Filter by source language
|
|
39
|
+
*/
|
|
40
|
+
inLanguage(language: string): this;
|
|
41
|
+
/**
|
|
42
|
+
* Filter by tone (emotional sentiment)
|
|
43
|
+
*/
|
|
44
|
+
withTone(operator: '>' | '<' | '=', value: number): this;
|
|
45
|
+
/**
|
|
46
|
+
* Filter by absolute tone (emotional intensity)
|
|
47
|
+
*/
|
|
48
|
+
withAbsoluteTone(operator: '>' | '<' | '=', value: number): this;
|
|
49
|
+
/**
|
|
50
|
+
* Filter for positive tone articles
|
|
51
|
+
*/
|
|
52
|
+
withPositiveTone(threshold?: number): this;
|
|
53
|
+
/**
|
|
54
|
+
* Filter for negative tone articles
|
|
55
|
+
*/
|
|
56
|
+
withNegativeTone(threshold?: number): this;
|
|
57
|
+
/**
|
|
58
|
+
* Filter for neutral tone articles
|
|
59
|
+
*/
|
|
60
|
+
withNeutralTone(range?: number): this;
|
|
61
|
+
/**
|
|
62
|
+
* Filter for highly emotional articles (regardless of positive/negative)
|
|
63
|
+
*/
|
|
64
|
+
withHighEmotion(threshold?: number): this;
|
|
65
|
+
/**
|
|
66
|
+
* Filter by GDELT theme
|
|
67
|
+
*/
|
|
68
|
+
withTheme(theme: string): this;
|
|
69
|
+
/**
|
|
70
|
+
* Filter for images with specific tags
|
|
71
|
+
*/
|
|
72
|
+
withImageTag(tag: string): this;
|
|
73
|
+
/**
|
|
74
|
+
* Filter for images with web tags
|
|
75
|
+
*/
|
|
76
|
+
withImageWebTag(tag: string): this;
|
|
77
|
+
/**
|
|
78
|
+
* Filter for images with OCR/metadata content
|
|
79
|
+
*/
|
|
80
|
+
withImageOCR(content: string): this;
|
|
81
|
+
/**
|
|
82
|
+
* Filter by image face count
|
|
83
|
+
*/
|
|
84
|
+
withImageFaceCount(operator: '>' | '<' | '=', count: number): this;
|
|
85
|
+
/**
|
|
86
|
+
* Filter by image face tone
|
|
87
|
+
*/
|
|
88
|
+
withImageFaceTone(operator: '>' | '<' | '=', tone: number): this;
|
|
89
|
+
/**
|
|
90
|
+
* Filter by how often image appears on web
|
|
91
|
+
*/
|
|
92
|
+
withImageWebCount(operator: '>' | '<' | '=', count: number): this;
|
|
93
|
+
/**
|
|
94
|
+
* Filter for novel images (rarely seen on web)
|
|
95
|
+
*/
|
|
96
|
+
withNovelImages(threshold?: number): this;
|
|
97
|
+
/**
|
|
98
|
+
* Filter for popular images (frequently seen on web)
|
|
99
|
+
*/
|
|
100
|
+
withPopularImages(threshold?: number): this;
|
|
101
|
+
/**
|
|
102
|
+
* Add proximity constraint (words must appear within distance)
|
|
103
|
+
*/
|
|
104
|
+
withProximity(distance: number, ...words: string[]): this;
|
|
105
|
+
/**
|
|
106
|
+
* Require word to appear minimum number of times
|
|
107
|
+
*/
|
|
108
|
+
withRepeat(count: number, word: string): this;
|
|
109
|
+
/**
|
|
110
|
+
* Add custom query component
|
|
111
|
+
*/
|
|
112
|
+
custom(query: string): this;
|
|
113
|
+
/**
|
|
114
|
+
* Group current components with parentheses
|
|
115
|
+
*/
|
|
116
|
+
group(): this;
|
|
117
|
+
/**
|
|
118
|
+
* Clear all components and start fresh
|
|
119
|
+
*/
|
|
120
|
+
clear(): this;
|
|
121
|
+
/**
|
|
122
|
+
* Build the final query string
|
|
123
|
+
*/
|
|
124
|
+
build(): string;
|
|
125
|
+
/**
|
|
126
|
+
* Get a copy of the current components
|
|
127
|
+
*/
|
|
128
|
+
getComponents(): string[];
|
|
129
|
+
/**
|
|
130
|
+
* Check if the builder has any components
|
|
131
|
+
*/
|
|
132
|
+
isEmpty(): boolean;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Query builder specialized for article searches
|
|
136
|
+
*/
|
|
137
|
+
export declare class ArticleQueryBuilder extends QueryBuilder {
|
|
138
|
+
/**
|
|
139
|
+
* Search for breaking news articles
|
|
140
|
+
*/
|
|
141
|
+
breakingNews(): this;
|
|
142
|
+
/**
|
|
143
|
+
* Search for opinion articles
|
|
144
|
+
*/
|
|
145
|
+
opinions(): this;
|
|
146
|
+
/**
|
|
147
|
+
* Search for local news
|
|
148
|
+
*/
|
|
149
|
+
localNews(location?: string): this;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Query builder specialized for image searches
|
|
153
|
+
*/
|
|
154
|
+
export declare class ImageQueryBuilder extends QueryBuilder {
|
|
155
|
+
/**
|
|
156
|
+
* Search for violent content images
|
|
157
|
+
*/
|
|
158
|
+
violentContent(): this;
|
|
159
|
+
/**
|
|
160
|
+
* Search for medical content images
|
|
161
|
+
*/
|
|
162
|
+
medicalContent(): this;
|
|
163
|
+
/**
|
|
164
|
+
* Search for disaster-related images
|
|
165
|
+
*/
|
|
166
|
+
disasters(): this;
|
|
167
|
+
/**
|
|
168
|
+
* Search for political event images
|
|
169
|
+
*/
|
|
170
|
+
politicalEvents(): this;
|
|
171
|
+
/**
|
|
172
|
+
* Search for happy/positive images
|
|
173
|
+
*/
|
|
174
|
+
positiveImages(): this;
|
|
175
|
+
/**
|
|
176
|
+
* Search for sad/negative images
|
|
177
|
+
*/
|
|
178
|
+
negativeImages(): this;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Create a new general query builder
|
|
182
|
+
*/
|
|
183
|
+
export declare function createQuery(): QueryBuilder;
|
|
184
|
+
/**
|
|
185
|
+
* Create a new article-specialized query builder
|
|
186
|
+
*/
|
|
187
|
+
export declare function createArticleQuery(): ArticleQueryBuilder;
|
|
188
|
+
/**
|
|
189
|
+
* Create a new image-specialized query builder
|
|
190
|
+
*/
|
|
191
|
+
export declare function createImageQuery(): ImageQueryBuilder;
|
|
192
|
+
/**
|
|
193
|
+
* Create a quick phrase query
|
|
194
|
+
*/
|
|
195
|
+
export declare function phrase(text: string): string;
|
|
196
|
+
/**
|
|
197
|
+
* Create a quick OR query
|
|
198
|
+
*/
|
|
199
|
+
export declare function anyOf(...terms: string[]): string;
|
|
200
|
+
/**
|
|
201
|
+
* Create a quick exclusion query
|
|
202
|
+
*/
|
|
203
|
+
export declare function exclude(term: string): string;
|
|
204
|
+
/**
|
|
205
|
+
* Create a domain filter
|
|
206
|
+
*/
|
|
207
|
+
export declare function fromDomain(domain: string, exact?: boolean): string;
|
|
208
|
+
/**
|
|
209
|
+
* Create a country filter
|
|
210
|
+
*/
|
|
211
|
+
export declare function fromCountry(country: string): string;
|
|
212
|
+
/**
|
|
213
|
+
* Create a language filter
|
|
214
|
+
*/
|
|
215
|
+
export declare function inLanguage(language: string): string;
|
|
216
|
+
/**
|
|
217
|
+
* Create a tone filter
|
|
218
|
+
*/
|
|
219
|
+
export declare function withTone(operator: '>' | '<' | '=', value: number): string;
|
|
220
|
+
/**
|
|
221
|
+
* Create a theme filter
|
|
222
|
+
*/
|
|
223
|
+
export declare function withTheme(theme: string): string;
|
|
224
|
+
/**
|
|
225
|
+
* Validate that a query string is well-formed
|
|
226
|
+
*/
|
|
227
|
+
export declare function isValidQuery(query: string): boolean;
|
|
228
|
+
/**
|
|
229
|
+
* Validate that a query has balanced quotes
|
|
230
|
+
*/
|
|
231
|
+
export declare function hasBalancedQuotes(query: string): boolean;
|
|
232
|
+
/**
|
|
233
|
+
* Get query complexity score (approximate)
|
|
234
|
+
*/
|
|
235
|
+
export declare function getQueryComplexity(query: string): number;
|
|
236
|
+
export declare const QueryHelpers: {
|
|
237
|
+
readonly createQuery: typeof createQuery;
|
|
238
|
+
readonly createArticleQuery: typeof createArticleQuery;
|
|
239
|
+
readonly createImageQuery: typeof createImageQuery;
|
|
240
|
+
readonly phrase: typeof phrase;
|
|
241
|
+
readonly anyOf: typeof anyOf;
|
|
242
|
+
readonly exclude: typeof exclude;
|
|
243
|
+
readonly fromDomain: typeof fromDomain;
|
|
244
|
+
readonly fromCountry: typeof fromCountry;
|
|
245
|
+
readonly inLanguage: typeof inLanguage;
|
|
246
|
+
readonly withTone: typeof withTone;
|
|
247
|
+
readonly withTheme: typeof withTheme;
|
|
248
|
+
readonly isValidQuery: typeof isValidQuery;
|
|
249
|
+
readonly hasBalancedQuotes: typeof hasBalancedQuotes;
|
|
250
|
+
readonly getQueryComplexity: typeof getQueryComplexity;
|
|
251
|
+
};
|
|
252
|
+
//# sourceMappingURL=query-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-builder.d.ts","sourceRoot":"","sources":["../../src/types/query-builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Enhanced query builder with TypeScript template literal types and IntelliSense support
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.QueryHelpers = exports.ImageQueryBuilder = exports.ArticleQueryBuilder = exports.QueryBuilder = void 0;
|
|
7
|
+
exports.createQuery = createQuery;
|
|
8
|
+
exports.createArticleQuery = createArticleQuery;
|
|
9
|
+
exports.createImageQuery = createImageQuery;
|
|
10
|
+
exports.phrase = phrase;
|
|
11
|
+
exports.anyOf = anyOf;
|
|
12
|
+
exports.exclude = exclude;
|
|
13
|
+
exports.fromDomain = fromDomain;
|
|
14
|
+
exports.fromCountry = fromCountry;
|
|
15
|
+
exports.inLanguage = inLanguage;
|
|
16
|
+
exports.withTone = withTone;
|
|
17
|
+
exports.withTheme = withTheme;
|
|
18
|
+
exports.isValidQuery = isValidQuery;
|
|
19
|
+
exports.hasBalancedQuotes = hasBalancedQuotes;
|
|
20
|
+
exports.getQueryComplexity = getQueryComplexity;
|
|
21
|
+
/* eslint-disable @typescript-eslint/explicit-member-accessibility, @typescript-eslint/naming-convention, @typescript-eslint/prefer-nullish-coalescing */
|
|
22
|
+
// Import only what we need to avoid circular dependencies
|
|
23
|
+
// ===== QUERY BUILDER CLASS =====
|
|
24
|
+
/**
|
|
25
|
+
* Fluent query builder for constructing complex GDELT queries with type safety
|
|
26
|
+
*/
|
|
27
|
+
class QueryBuilder {
|
|
28
|
+
constructor() {
|
|
29
|
+
this.components = [];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Add a basic search term or phrase
|
|
33
|
+
*/
|
|
34
|
+
search(term) {
|
|
35
|
+
this.components.push(term);
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Add a phrase search (exact match)
|
|
40
|
+
*/
|
|
41
|
+
phrase(phrase) {
|
|
42
|
+
this.components.push(`"${phrase}"`);
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Add multiple terms with OR logic
|
|
47
|
+
*/
|
|
48
|
+
anyOf(...terms) {
|
|
49
|
+
if (terms.length > 1) {
|
|
50
|
+
this.components.push(`(${terms.join(' OR ')})`);
|
|
51
|
+
}
|
|
52
|
+
else if (terms.length === 1 && terms[0]) {
|
|
53
|
+
this.components.push(terms[0]);
|
|
54
|
+
}
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Add multiple terms with AND logic
|
|
59
|
+
*/
|
|
60
|
+
allOf(...terms) {
|
|
61
|
+
terms.forEach(term => this.components.push(term));
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Exclude a term or phrase
|
|
66
|
+
*/
|
|
67
|
+
not(term) {
|
|
68
|
+
this.components.push(`-${term}`);
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Filter by domain
|
|
73
|
+
*/
|
|
74
|
+
fromDomain(domain, exact = false) {
|
|
75
|
+
const operator = exact ? 'domainis' : 'domain';
|
|
76
|
+
this.components.push(`${operator}:${domain}`);
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Filter by source country
|
|
81
|
+
*/
|
|
82
|
+
fromCountry(country) {
|
|
83
|
+
this.components.push(`sourcecountry:${country}`);
|
|
84
|
+
return this;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Filter by source language
|
|
88
|
+
*/
|
|
89
|
+
inLanguage(language) {
|
|
90
|
+
this.components.push(`sourcelang:${language}`);
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Filter by tone (emotional sentiment)
|
|
95
|
+
*/
|
|
96
|
+
withTone(operator, value) {
|
|
97
|
+
this.components.push(`tone${operator}${value}`);
|
|
98
|
+
return this;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Filter by absolute tone (emotional intensity)
|
|
102
|
+
*/
|
|
103
|
+
withAbsoluteTone(operator, value) {
|
|
104
|
+
this.components.push(`toneabs${operator}${value}`);
|
|
105
|
+
return this;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Filter for positive tone articles
|
|
109
|
+
*/
|
|
110
|
+
withPositiveTone(threshold = 2) {
|
|
111
|
+
return this.withTone('>', threshold);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Filter for negative tone articles
|
|
115
|
+
*/
|
|
116
|
+
withNegativeTone(threshold = -2) {
|
|
117
|
+
return this.withTone('<', threshold);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Filter for neutral tone articles
|
|
121
|
+
*/
|
|
122
|
+
withNeutralTone(range = 1) {
|
|
123
|
+
this.components.push(`toneabs<${range}`);
|
|
124
|
+
return this;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Filter for highly emotional articles (regardless of positive/negative)
|
|
128
|
+
*/
|
|
129
|
+
withHighEmotion(threshold = 5) {
|
|
130
|
+
return this.withAbsoluteTone('>', threshold);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Filter by GDELT theme
|
|
134
|
+
*/
|
|
135
|
+
withTheme(theme) {
|
|
136
|
+
this.components.push(`theme:${theme.toUpperCase()}`);
|
|
137
|
+
return this;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Filter for images with specific tags
|
|
141
|
+
*/
|
|
142
|
+
withImageTag(tag) {
|
|
143
|
+
this.components.push(`imagetag:"${tag}"`);
|
|
144
|
+
return this;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Filter for images with web tags
|
|
148
|
+
*/
|
|
149
|
+
withImageWebTag(tag) {
|
|
150
|
+
this.components.push(`imagewebtag:"${tag}"`);
|
|
151
|
+
return this;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Filter for images with OCR/metadata content
|
|
155
|
+
*/
|
|
156
|
+
withImageOCR(content) {
|
|
157
|
+
this.components.push(`imageocrmeta:"${content}"`);
|
|
158
|
+
return this;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Filter by image face count
|
|
162
|
+
*/
|
|
163
|
+
withImageFaceCount(operator, count) {
|
|
164
|
+
this.components.push(`imagenumfaces${operator}${count}`);
|
|
165
|
+
return this;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Filter by image face tone
|
|
169
|
+
*/
|
|
170
|
+
withImageFaceTone(operator, tone) {
|
|
171
|
+
this.components.push(`imagefacetone${operator}${tone}`);
|
|
172
|
+
return this;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Filter by how often image appears on web
|
|
176
|
+
*/
|
|
177
|
+
withImageWebCount(operator, count) {
|
|
178
|
+
this.components.push(`imagewebcount${operator}${count}`);
|
|
179
|
+
return this;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Filter for novel images (rarely seen on web)
|
|
183
|
+
*/
|
|
184
|
+
withNovelImages(threshold = 10) {
|
|
185
|
+
return this.withImageWebCount('<', threshold);
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Filter for popular images (frequently seen on web)
|
|
189
|
+
*/
|
|
190
|
+
withPopularImages(threshold = 100) {
|
|
191
|
+
return this.withImageWebCount('>', threshold);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Add proximity constraint (words must appear within distance)
|
|
195
|
+
*/
|
|
196
|
+
withProximity(distance, ...words) {
|
|
197
|
+
if (words.length >= 2) {
|
|
198
|
+
this.components.push(`near${distance}:"${words.join(' ')}"`);
|
|
199
|
+
}
|
|
200
|
+
return this;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Require word to appear minimum number of times
|
|
204
|
+
*/
|
|
205
|
+
withRepeat(count, word) {
|
|
206
|
+
this.components.push(`repeat${count}:"${word}"`);
|
|
207
|
+
return this;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Add custom query component
|
|
211
|
+
*/
|
|
212
|
+
custom(query) {
|
|
213
|
+
this.components.push(query);
|
|
214
|
+
return this;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Group current components with parentheses
|
|
218
|
+
*/
|
|
219
|
+
group() {
|
|
220
|
+
if (this.components.length > 1) {
|
|
221
|
+
const grouped = `(${this.components.join(' ')})`;
|
|
222
|
+
this.components = [grouped];
|
|
223
|
+
}
|
|
224
|
+
return this;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Clear all components and start fresh
|
|
228
|
+
*/
|
|
229
|
+
clear() {
|
|
230
|
+
this.components = [];
|
|
231
|
+
return this;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Build the final query string
|
|
235
|
+
*/
|
|
236
|
+
build() {
|
|
237
|
+
return this.components.join(' ');
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Get a copy of the current components
|
|
241
|
+
*/
|
|
242
|
+
getComponents() {
|
|
243
|
+
return [...this.components];
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Check if the builder has any components
|
|
247
|
+
*/
|
|
248
|
+
isEmpty() {
|
|
249
|
+
return this.components.length === 0;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
exports.QueryBuilder = QueryBuilder;
|
|
253
|
+
// ===== SPECIALIZED QUERY BUILDERS =====
|
|
254
|
+
/**
|
|
255
|
+
* Query builder specialized for article searches
|
|
256
|
+
*/
|
|
257
|
+
class ArticleQueryBuilder extends QueryBuilder {
|
|
258
|
+
/**
|
|
259
|
+
* Search for breaking news articles
|
|
260
|
+
*/
|
|
261
|
+
breakingNews() {
|
|
262
|
+
return this.anyOf('breaking', 'urgent', 'just in', 'developing');
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Search for opinion articles
|
|
266
|
+
*/
|
|
267
|
+
opinions() {
|
|
268
|
+
return this.anyOf('opinion', 'editorial', 'commentary', 'analysis');
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Search for local news
|
|
272
|
+
*/
|
|
273
|
+
localNews(location) {
|
|
274
|
+
if (location) {
|
|
275
|
+
return this.allOf('local', location);
|
|
276
|
+
}
|
|
277
|
+
return this.search('local');
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
exports.ArticleQueryBuilder = ArticleQueryBuilder;
|
|
281
|
+
/**
|
|
282
|
+
* Query builder specialized for image searches
|
|
283
|
+
*/
|
|
284
|
+
class ImageQueryBuilder extends QueryBuilder {
|
|
285
|
+
/**
|
|
286
|
+
* Search for violent content images
|
|
287
|
+
*/
|
|
288
|
+
violentContent() {
|
|
289
|
+
return this.withImageTag('safesearchviolence');
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Search for medical content images
|
|
293
|
+
*/
|
|
294
|
+
medicalContent() {
|
|
295
|
+
return this.withImageTag('safesearchmedical');
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Search for disaster-related images
|
|
299
|
+
*/
|
|
300
|
+
disasters() {
|
|
301
|
+
return this.anyOf('imagetag:"flood"', 'imagetag:"fire"', 'imagetag:"earthquake"', 'imagetag:"rubble"');
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Search for political event images
|
|
305
|
+
*/
|
|
306
|
+
politicalEvents() {
|
|
307
|
+
return this.anyOf('imagetag:"crowd"', 'imagetag:"protest"', 'imagetag:"rally"', 'imagetag:"podium"');
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Search for happy/positive images
|
|
311
|
+
*/
|
|
312
|
+
positiveImages() {
|
|
313
|
+
return this.withImageFaceTone('>', 1);
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Search for sad/negative images
|
|
317
|
+
*/
|
|
318
|
+
negativeImages() {
|
|
319
|
+
return this.withImageFaceTone('<', -1);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
exports.ImageQueryBuilder = ImageQueryBuilder;
|
|
323
|
+
// ===== QUERY HELPER FUNCTIONS =====
|
|
324
|
+
/**
|
|
325
|
+
* Create a new general query builder
|
|
326
|
+
*/
|
|
327
|
+
function createQuery() {
|
|
328
|
+
return new QueryBuilder();
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Create a new article-specialized query builder
|
|
332
|
+
*/
|
|
333
|
+
function createArticleQuery() {
|
|
334
|
+
return new ArticleQueryBuilder();
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Create a new image-specialized query builder
|
|
338
|
+
*/
|
|
339
|
+
function createImageQuery() {
|
|
340
|
+
return new ImageQueryBuilder();
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Create a quick phrase query
|
|
344
|
+
*/
|
|
345
|
+
function phrase(text) {
|
|
346
|
+
return `"${text}"`;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Create a quick OR query
|
|
350
|
+
*/
|
|
351
|
+
function anyOf(...terms) {
|
|
352
|
+
if (terms.length === 0)
|
|
353
|
+
return '';
|
|
354
|
+
if (terms.length === 1)
|
|
355
|
+
return terms[0] || '';
|
|
356
|
+
return `(${terms.join(' OR ')})`;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Create a quick exclusion query
|
|
360
|
+
*/
|
|
361
|
+
function exclude(term) {
|
|
362
|
+
return `-${term}`;
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Create a domain filter
|
|
366
|
+
*/
|
|
367
|
+
function fromDomain(domain, exact = false) {
|
|
368
|
+
const operator = exact ? 'domainis' : 'domain';
|
|
369
|
+
return `${operator}:${domain}`;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Create a country filter
|
|
373
|
+
*/
|
|
374
|
+
function fromCountry(country) {
|
|
375
|
+
return `sourcecountry:${country}`;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Create a language filter
|
|
379
|
+
*/
|
|
380
|
+
function inLanguage(language) {
|
|
381
|
+
return `sourcelang:${language}`;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Create a tone filter
|
|
385
|
+
*/
|
|
386
|
+
function withTone(operator, value) {
|
|
387
|
+
return `tone${operator}${value}`;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Create a theme filter
|
|
391
|
+
*/
|
|
392
|
+
function withTheme(theme) {
|
|
393
|
+
return `theme:${theme.toUpperCase()}`;
|
|
394
|
+
}
|
|
395
|
+
// ===== QUERY VALIDATION HELPERS =====
|
|
396
|
+
/**
|
|
397
|
+
* Validate that a query string is well-formed
|
|
398
|
+
*/
|
|
399
|
+
function isValidQuery(query) {
|
|
400
|
+
if (!query || query.trim().length === 0) {
|
|
401
|
+
return false;
|
|
402
|
+
}
|
|
403
|
+
// Check for balanced parentheses
|
|
404
|
+
let parenCount = 0;
|
|
405
|
+
for (const char of query) {
|
|
406
|
+
if (char === '(')
|
|
407
|
+
parenCount++;
|
|
408
|
+
if (char === ')')
|
|
409
|
+
parenCount--;
|
|
410
|
+
if (parenCount < 0)
|
|
411
|
+
return false;
|
|
412
|
+
}
|
|
413
|
+
return parenCount === 0;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Validate that a query has balanced quotes
|
|
417
|
+
*/
|
|
418
|
+
function hasBalancedQuotes(query) {
|
|
419
|
+
const quotes = query.match(/"/g);
|
|
420
|
+
return !quotes || quotes.length % 2 === 0;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Get query complexity score (approximate)
|
|
424
|
+
*/
|
|
425
|
+
function getQueryComplexity(query) {
|
|
426
|
+
let complexity = 0;
|
|
427
|
+
// Base complexity
|
|
428
|
+
complexity += query.split(' ').length;
|
|
429
|
+
// Operators add complexity
|
|
430
|
+
complexity += (query.match(/\bOR\b/g) || []).length * 2;
|
|
431
|
+
complexity += (query.match(/\bAND\b/g) || []).length * 1;
|
|
432
|
+
complexity += (query.match(/\bNOT\b/g) || []).length * 1;
|
|
433
|
+
// Special operators add complexity
|
|
434
|
+
const operatorPattern = /\b(?:domain|domainis|sourcecountry|sourcelang|theme|tone|toneabs|imagetag|imagewebtag|imageocrmeta|imagenumfaces|imagefacetone|imagewebcount):/g;
|
|
435
|
+
complexity += (query.match(operatorPattern) || []).length * 3;
|
|
436
|
+
// Proximity and repeat operators
|
|
437
|
+
complexity += (query.match(/\bnear\d+:/g) || []).length * 4;
|
|
438
|
+
complexity += (query.match(/\brepeat\d+:/g) || []).length * 2;
|
|
439
|
+
return complexity;
|
|
440
|
+
}
|
|
441
|
+
// ===== EXPORT EVERYTHING =====
|
|
442
|
+
exports.QueryHelpers = {
|
|
443
|
+
createQuery,
|
|
444
|
+
createArticleQuery,
|
|
445
|
+
createImageQuery,
|
|
446
|
+
phrase,
|
|
447
|
+
anyOf,
|
|
448
|
+
exclude,
|
|
449
|
+
fromDomain,
|
|
450
|
+
fromCountry,
|
|
451
|
+
inLanguage,
|
|
452
|
+
withTone,
|
|
453
|
+
withTheme,
|
|
454
|
+
isValidQuery,
|
|
455
|
+
hasBalancedQuotes,
|
|
456
|
+
getQueryComplexity
|
|
457
|
+
};
|
|
458
|
+
//# sourceMappingURL=query-builder.js.map
|