Skip to content

Commit e86db00

Browse files
authored
feat: add text manipulators (#10)
* feat: add text setters * fix: add text manipulators
1 parent 26e5b2c commit e86db00

File tree

4 files changed

+136
-36
lines changed

4 files changed

+136
-36
lines changed

README.md

+9-5
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ $ yarn add advanced-search-query
2424
import parseAdvancedSearchQuery from 'advanced-search-query'
2525

2626
// Perform parsing
27-
const str = 'to:me -from:[email protected] foobar1 -foobar2'
28-
const parsedSearchQuery = parseAdvancedSearchQuery(str)
27+
const input = 'to:me -from:[email protected] foobar1 -foobar2'
28+
const parsedSearchQuery = parseAdvancedSearchQuery(input)
2929

3030
// [ { text: 'foorbar1', isNegated: false }, { text: 'foobar2', isNegated: true } ]
3131
parsedSearchQuery.getTexts()
@@ -46,12 +46,16 @@ parsedSearchQuery.toString()
4646
parsedSearchQuery.removeKeyword('from', true).toString()
4747

4848
// `to:me from:[email protected] foobar1 -foobar2`
49-
parsedSearchQuery.addEntry('from', '[email protected]', false).toString()
49+
parsedSearchQuery.addKeyword('from', '[email protected]', false).toString()
5050

5151
// `from:[email protected] foobar1 -foobar2`
52-
parsedSearchQuery.removeEntry('to', 'me', false).toString()
52+
parsedSearchQuery.removeKeyword('to', 'me', false).toString()
5353

54-
/* clone operation instantiates a new version by copying over data. */
54+
// `from:[email protected] -foobar2`
55+
parsedSearchQuery.removeText('foobar1').toString()
56+
57+
// `from:[email protected] foobar1 -foobar2`
58+
parsedSearchQuery.addText('foobar1', false).toString()
5559

5660
// `from:[email protected] foobar1 -foobar2`
5761
parsedSearchQuery.clone().toString()

src/index.ts

+46-2
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export class AdvancedSearchQuery {
5757
}
5858

5959
getKeywords(): Record<string, Omit<Keyword, 'name'>[]> {
60-
const keywords = groupBy(this.keywords, 'name')
60+
const keywords: Record<string, Keyword[]> = groupBy(this.keywords, 'name')
6161

6262
return Object.entries(keywords)
6363
.map(([name, keywords]): [string, Omit<Keyword, 'name'>[]] => {
@@ -122,13 +122,22 @@ export class AdvancedSearchQuery {
122122
return parsedQuery
123123
}
124124

125-
addKeyword(name: string, value: Value, isNegated: boolean) {
125+
addKeyword(name: string, value: Value, isNegated: boolean = false) {
126126
this.keywords.push({
127127
name,
128128
value,
129129
isNegated,
130130
})
131131
this.isDirty = true
132+
133+
return this
134+
}
135+
136+
removeKeywords() {
137+
this.keywords = []
138+
this.isDirty = true
139+
140+
return this
132141
}
133142

134143
removeKeyword(name: string, value?: Value, isNegated?: boolean) {
@@ -153,6 +162,41 @@ export class AdvancedSearchQuery {
153162
return this
154163
}
155164

165+
addText(value: Value, isNegated: boolean = false) {
166+
this.texts.push({
167+
value,
168+
isNegated,
169+
})
170+
this.isDirty = true
171+
172+
return this
173+
}
174+
175+
removeText(value: Value, isNegated?: boolean) {
176+
const texts = this.texts.filter((text) => {
177+
if (value !== text.value) {
178+
return true
179+
}
180+
181+
if (typeof isNegated !== 'undefined' && isNegated !== text.isNegated) {
182+
return true
183+
}
184+
185+
return false
186+
})
187+
this.isDirty = texts.length !== this.texts.length
188+
this.texts = texts
189+
190+
return this
191+
}
192+
193+
removeTexts() {
194+
this.texts = []
195+
this.isDirty = true
196+
197+
return this
198+
}
199+
156200
clone() {
157201
return new AdvancedSearchQuery(this.keywords.slice(0), this.texts.slice(0))
158202
}

tests/index.test.ts

+61-10
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ describe('searchString', () => {
5555
])
5656
})
5757

58-
test('multiple getText() segments', () => {
58+
test('multiple texts', () => {
5959
const input = 'to:me foobar zoobar'
6060
const parsed = parseAdvancedSearchQuery(input)
6161

@@ -104,7 +104,7 @@ describe('searchString', () => {
104104
])
105105
})
106106

107-
test('isNegated getText()', () => {
107+
test('negated texts', () => {
108108
const input = 'hello -big -fat is:condition world'
109109
const parsed = parseAdvancedSearchQuery(input)
110110

@@ -122,13 +122,13 @@ describe('searchString', () => {
122122

123123
test('complex use case', () => {
124124
const input =
125-
'op1:value op1:value2 op2:"multi, \'word\', value" sometext -op3:value more text'
125+
'op1:value op1:value2 op2:"multi, \'word\', value" sometext -op3:value more -text'
126126
const parsed = parseAdvancedSearchQuery(input)
127127

128128
expect(parsed.getTexts()).toEqual([
129129
{ value: 'sometext', isNegated: false },
130130
{ value: 'more', isNegated: false },
131-
{ value: 'text', isNegated: false },
131+
{ value: 'text', isNegated: true },
132132
])
133133
expect(getNumberOfKeywords(parsed)).toEqual(3)
134134
expect(parsed.getKeyword('op1')).toEqual([
@@ -162,24 +162,34 @@ describe('searchString', () => {
162162
op3: [{ value: 'value', isNegated: true }],
163163
})
164164
expect(parsed.toString()).toEqual(
165-
'op1:value,value2 op2:"multi, \'word\', value" -op3:value sometext more text'
165+
'op1:value,value2 op2:"multi, \'word\', value" -op3:value sometext more -text'
166166
)
167167
parsed.removeKeyword('op1')
168168
expect(parsed.toString()).toEqual(
169-
'op2:"multi, \'word\', value" -op3:value sometext more text'
169+
'op2:"multi, \'word\', value" -op3:value sometext more -text'
170170
)
171171
// Check once more to see if cached copy returns correctly.
172172
expect(parsed.toString()).toEqual(
173-
'op2:"multi, \'word\', value" -op3:value sometext more text'
173+
'op2:"multi, \'word\', value" -op3:value sometext more -text'
174174
)
175175
parsed.removeKeyword('op3', undefined, false)
176176
expect(parsed.toString()).toEqual(
177-
'op2:"multi, \'word\', value" -op3:value sometext more text'
177+
'op2:"multi, \'word\', value" -op3:value sometext more -text'
178178
)
179179
parsed.removeKeyword('op3', 'value')
180180
expect(parsed.toString()).toEqual(
181-
'op2:"multi, \'word\', value" sometext more text'
181+
'op2:"multi, \'word\', value" sometext more -text'
182182
)
183+
parsed.removeText('sometext')
184+
expect(parsed.toString()).toEqual('op2:"multi, \'word\', value" more -text')
185+
parsed.removeText('text', false)
186+
expect(parsed.toString()).toEqual('op2:"multi, \'word\', value" more -text')
187+
parsed.removeText('text', true)
188+
expect(parsed.toString()).toEqual('op2:"multi, \'word\', value" more')
189+
parsed.removeTexts()
190+
expect(parsed.toString()).toEqual('op2:"multi, \'word\', value"')
191+
parsed.removeKeywords()
192+
expect(parsed.toString()).toEqual('')
183193
})
184194

185195
test('several quoted strings', () => {
@@ -358,7 +368,7 @@ describe('searchString', () => {
358368
expect(parsed.toObject().keywords.foo.include).toEqual(['bar'])
359369
})
360370

361-
test('removeKeyword should remove only one case', () => {
371+
test('removeKeyword should remove all occurences', () => {
362372
const input = '-foo:bar,baz,bar,bar,bar'
363373
const parsed = parseAdvancedSearchQuery(input)
364374

@@ -388,4 +398,45 @@ describe('searchString', () => {
388398
expect(parsed.toObject().keywords.foo.include).toEqual(['bar'])
389399
expect(parsed.isDirty).toEqual(false)
390400
})
401+
402+
test('removeText simple case', () => {
403+
const input = 'bar baz'
404+
const parsed = parseAdvancedSearchQuery(input)
405+
406+
expect(parsed.toObject().text.include).toEqual(['bar', 'baz'])
407+
parsed.removeText('baz')
408+
expect(parsed.toObject().text.include).toEqual(['bar'])
409+
})
410+
411+
test('removeText should remove all occurences', () => {
412+
const input = 'bar baz bar bar bar'
413+
const parsed = parseAdvancedSearchQuery(input)
414+
415+
expect(parsed.toObject().text.include).toEqual([
416+
'bar',
417+
'baz',
418+
'bar',
419+
'bar',
420+
'bar',
421+
])
422+
423+
parsed.removeText('bar')
424+
425+
expect(parsed.toObject().text.include).toEqual(['baz'])
426+
})
427+
428+
test('removeText should be noop if entry is not found', () => {
429+
const input = 'test -notest'
430+
const parsed = parseAdvancedSearchQuery(input)
431+
432+
expect(parsed.toObject().text.include).toEqual(['test'])
433+
expect(parsed.toObject().text.exclude).toEqual(['notest'])
434+
expect(parsed.toString()).toEqual('test -notest')
435+
expect(parsed.isDirty).toEqual(false)
436+
437+
parsed.removeText('test', true)
438+
439+
expect(parsed.toObject().text.include).toEqual(['test'])
440+
expect(parsed.isDirty).toEqual(false)
441+
})
391442
})

tsconfig.json

+20-19
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
1-
{
2-
"compilerOptions": {
3-
"target": "es6",
4-
"module": "commonjs",
5-
"strict": true,
6-
"outDir": "dist/node/",
7-
"sourceMap": true,
8-
"declaration": true,
9-
"types": ["jest"],
10-
"skipLibCheck": true,
11-
"esModuleInterop": true
12-
},
13-
"include": [
14-
"*.ts",
15-
"./src/**/*",
16-
"./tests/**/*",
17-
"./node_modules/@types/**/*"
18-
]
19-
}
1+
{
2+
"compilerOptions": {
3+
"lib": ["es2019"],
4+
"target": "es6",
5+
"module": "commonjs",
6+
"strict": true,
7+
"outDir": "dist/node/",
8+
"sourceMap": true,
9+
"declaration": true,
10+
"types": ["jest"],
11+
"skipLibCheck": true,
12+
"esModuleInterop": true
13+
},
14+
"include": [
15+
"*.ts",
16+
"./src/**/*",
17+
"./tests/**/*",
18+
"./node_modules/@types/**/*"
19+
]
20+
}

0 commit comments

Comments
 (0)