Skip to content

Commit 1caca05

Browse files
retooanthonycorbacho
authored andcommitted
Match#capture: flatten multiple values to the same key (#68)
* Match#capture: flatten multiple values to the same key * Match#capture: remove unused code of flattened change
1 parent ccc8379 commit 1caca05

File tree

2 files changed

+86
-18
lines changed

2 files changed

+86
-18
lines changed

src/main/java/io/thekraken/grok/api/Match.java

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@
1616
package io.thekraken.grok.api;
1717

1818

19+
import static java.lang.String.format;
20+
1921
import java.util.ArrayList;
22+
import java.util.Collections;
2023
import java.util.Iterator;
2124
import java.util.List;
2225
import java.util.Map;
@@ -26,6 +29,7 @@
2629

2730
import com.google.gson.Gson;
2831
import com.google.gson.GsonBuilder;
32+
import io.thekraken.grok.api.exception.GrokException;
2933

3034
/**
3135
* {@code Match} is a representation in {@code Grok} world of your log.
@@ -141,9 +145,32 @@ public String getSubject() {
141145
/**
142146
* Match to the <tt>subject</tt> the <tt>regex</tt> and save the matched element into a map.
143147
*
148+
* Multiple values for the same key are stored as list.
149+
*
144150
*/
145-
@SuppressWarnings("unchecked")
146151
public void captures() {
152+
captures(false);
153+
154+
}
155+
156+
/**
157+
* Match to the <tt>subject</tt> the <tt>regex</tt> and save the matched element into a map
158+
*
159+
* Multiple values to the same key are flattened to one value: the sole non-null value will be captured.
160+
* Should there be multiple non-null values a RuntimeException is being thrown.
161+
*
162+
* This can be used in cases like: (foo (.*:message) bar|bar (.*:message) foo) where the regexp guarantees that only
163+
* one value will be captured.
164+
*
165+
* See also {@link #captures} which returns multiple values of the same key as list.
166+
*
167+
*/
168+
public void capturesFlattened() {
169+
captures(true);
170+
}
171+
172+
@SuppressWarnings("unchecked")
173+
private void captures(boolean flattened ) {
147174
if (match == null) {
148175
return;
149176
}
@@ -187,24 +214,36 @@ public void captures() {
187214
}
188215

189216
if (capture.containsKey(key)) {
190-
Object currentValue = capture.get(key);
191-
if(currentValue instanceof List) {
192-
((List<Object>) currentValue).add(value);
193-
} else {
194-
List<Object> list = new ArrayList<Object>();
195-
list.add(currentValue);
196-
list.add(value);
197-
capture.put(key, list);
198-
}
217+
Object currentValue = capture.get(key);
218+
219+
if (flattened) {
220+
if (currentValue == null && value != null) {
221+
capture.put(key, value);
222+
} if (currentValue != null && value != null) {
223+
throw new RuntimeException(
224+
format("key '%s' has multiple non-null values, this is not allowed in flattened mode, values:'%s', '%s'",
225+
key,
226+
currentValue,
227+
value));
228+
}
229+
} else {
230+
if (currentValue instanceof List) {
231+
((List<Object>) currentValue).add(value);
232+
} else {
233+
List<Object> list = new ArrayList<Object>();
234+
list.add(currentValue);
235+
list.add(value);
236+
capture.put(key, list);
237+
}
238+
}
199239
} else {
200-
capture.put(key, value);
240+
capture.put(key, value);
201241
}
202242

203243
it.remove(); // avoids a ConcurrentModificationException
204244
}
205245
}
206246

207-
208247
/**
209248
* remove from the string the quote and double quote.
210249
*
@@ -294,4 +333,5 @@ private void cleanMap() {
294333
public Boolean isNull() {
295334
return this.match == null;
296335
}
336+
297337
}

src/test/java/io/thekraken/grok/api/CaptureTest.java

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
package io.thekraken.grok.api;
22

3-
import io.thekraken.grok.api.exception.GrokException;
4-
import org.junit.BeforeClass;
5-
import org.junit.FixMethodOrder;
6-
import org.junit.Test;
7-
import org.junit.runners.MethodSorters;
8-
3+
import static org.hamcrest.CoreMatchers.containsString;
4+
import static org.hamcrest.MatcherAssert.assertThat;
95
import static org.junit.Assert.assertEquals;
106
import static org.junit.Assert.assertNull;
117
import static org.junit.Assert.assertTrue;
8+
import static org.junit.Assert.fail;
129

1310
import java.util.List;
1411

12+
import org.junit.BeforeClass;
13+
import org.junit.FixMethodOrder;
14+
import org.junit.Test;
15+
import org.junit.runners.MethodSorters;
16+
17+
import io.thekraken.grok.api.exception.GrokException;
18+
1519
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
1620
public class CaptureTest {
1721

@@ -116,4 +120,28 @@ public void test007_captureDuplicateName() throws GrokException {
116120
assertEquals(((List<Object>) (m.toMap().get("id"))).get(1),"456");
117121
}
118122

123+
@SuppressWarnings("unchecked")
124+
@Test
125+
public void test008_flattenDuplicateKeys() throws GrokException {
126+
grok.compile("(?:foo %{INT:id} bar|bar %{INT:id} foo)");
127+
Match m = grok.match("foo 123 bar");
128+
m.capturesFlattened();
129+
assertEquals(m.toMap().size(), 1);
130+
assertEquals(m.toMap().get("id"), "123");
131+
Match m2 = grok.match("bar 123 foo");
132+
m2.capturesFlattened();
133+
assertEquals(m2.toMap().size(), 1);
134+
assertEquals(m2.toMap().get("id"), "123");
135+
136+
grok.compile("%{INT:id} %{INT:id}");
137+
Match m3 = grok.match("123 456");
138+
139+
try {
140+
m3.capturesFlattened();
141+
fail("should report error due tu ambiguity");
142+
} catch (RuntimeException e) {
143+
assertThat(e.getMessage(), containsString("has multiple non-null values, this is not allowed in flattened mode"));
144+
}
145+
}
146+
119147
}

0 commit comments

Comments
 (0)