Skip to content

Commit c57032f

Browse files
authored
Allow list of IPs in geoip ingest processor (#49573)
* Allow list of IPs in geoip ingest processor This change lets you use array of IPs in addition to string in geoip processor source field. It will set array containing geoip data for each element in source, unless first_only parameter option is enabled, then only first found will be returned. Closes #46193
1 parent 356d1a2 commit c57032f

File tree

4 files changed

+241
-34
lines changed

4 files changed

+241
-34
lines changed

docs/reference/ingest/processors/geoip.asciidoc

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ uncompressed. The `ingest-geoip` config directory is located at `$ES_CONFIG/inge
2525
| `database_file` | no | GeoLite2-City.mmdb | The database filename in the geoip config directory. The ingest-geoip module ships with the GeoLite2-City.mmdb, GeoLite2-Country.mmdb and GeoLite2-ASN.mmdb files.
2626
| `properties` | no | [`continent_name`, `country_iso_code`, `region_iso_code`, `region_name`, `city_name`, `location`] * | Controls what properties are added to the `target_field` based on the geoip lookup.
2727
| `ignore_missing` | no | `false` | If `true` and `field` does not exist, the processor quietly exits without modifying the document
28+
| `first_only` | no | `true` | If `true` only first found geoip data will be returned, even if `field` contains array
2829
|======
2930

3031
*Depends on what is available in `database_file`:

modules/ingest-geoip/src/main/java/org/elasticsearch/ingest/geoip/GeoIpProcessor.java

+57-22
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import java.net.InetAddress;
4242
import java.security.AccessController;
4343
import java.security.PrivilegedAction;
44+
import java.util.ArrayList;
4445
import java.util.Arrays;
4546
import java.util.Collections;
4647
import java.util.EnumSet;
@@ -68,6 +69,7 @@ public final class GeoIpProcessor extends AbstractProcessor {
6869
private final Set<Property> properties;
6970
private final boolean ignoreMissing;
7071
private final GeoIpCache cache;
72+
private final boolean firstOnly;
7173

7274
/**
7375
* Construct a geo-IP processor.
@@ -79,22 +81,25 @@ public final class GeoIpProcessor extends AbstractProcessor {
7981
* @param properties the properties; ideally this is lazily-loaded once on first use
8082
* @param ignoreMissing true if documents with a missing value for the field should be ignored
8183
* @param cache a geo-IP cache
84+
* @param firstOnly true if only first result should be returned in case of array
8285
*/
8386
GeoIpProcessor(
84-
final String tag,
85-
final String field,
86-
final DatabaseReaderLazyLoader lazyLoader,
87-
final String targetField,
88-
final Set<Property> properties,
89-
final boolean ignoreMissing,
90-
final GeoIpCache cache) {
87+
final String tag,
88+
final String field,
89+
final DatabaseReaderLazyLoader lazyLoader,
90+
final String targetField,
91+
final Set<Property> properties,
92+
final boolean ignoreMissing,
93+
final GeoIpCache cache,
94+
boolean firstOnly) {
9195
super(tag);
9296
this.field = field;
9397
this.targetField = targetField;
9498
this.lazyLoader = lazyLoader;
9599
this.properties = properties;
96100
this.ignoreMissing = ignoreMissing;
97101
this.cache = cache;
102+
this.firstOnly = firstOnly;
98103
}
99104

100105
boolean isIgnoreMissing() {
@@ -103,19 +108,51 @@ boolean isIgnoreMissing() {
103108

104109
@Override
105110
public IngestDocument execute(IngestDocument ingestDocument) throws IOException {
106-
String ip = ingestDocument.getFieldValue(field, String.class, ignoreMissing);
111+
Object ip = ingestDocument.getFieldValue(field, Object.class, ignoreMissing);
107112

108113
if (ip == null && ignoreMissing) {
109114
return ingestDocument;
110115
} else if (ip == null) {
111116
throw new IllegalArgumentException("field [" + field + "] is null, cannot extract geoip information.");
112117
}
113118

114-
final InetAddress ipAddress = InetAddresses.forString(ip);
119+
if (ip instanceof String) {
120+
Map<String, Object> geoData = getGeoData((String) ip);
121+
if (geoData.isEmpty() == false) {
122+
ingestDocument.setFieldValue(targetField, geoData);
123+
}
124+
} else if (ip instanceof List) {
125+
boolean match = false;
126+
List<Map<String, Object>> geoDataList = new ArrayList<>(((List) ip).size());
127+
for (Object ipAddr : (List) ip) {
128+
if (ipAddr instanceof String == false) {
129+
throw new IllegalArgumentException("array in field [" + field + "] should only contain strings");
130+
}
131+
Map<String, Object> geoData = getGeoData((String) ipAddr);
132+
if (geoData.isEmpty()) {
133+
geoDataList.add(null);
134+
continue;
135+
}
136+
if (firstOnly) {
137+
ingestDocument.setFieldValue(targetField, geoData);
138+
return ingestDocument;
139+
}
140+
match = true;
141+
geoDataList.add(geoData);
142+
}
143+
if (match) {
144+
ingestDocument.setFieldValue(targetField, geoDataList);
145+
}
146+
} else {
147+
throw new IllegalArgumentException("field [" + field + "] should contain only string or array of strings");
148+
}
149+
return ingestDocument;
150+
}
115151

116-
Map<String, Object> geoData;
152+
private Map<String, Object> getGeoData(String ip) throws IOException {
117153
String databaseType = lazyLoader.getDatabaseType();
118-
154+
final InetAddress ipAddress = InetAddresses.forString(ip);
155+
Map<String, Object> geoData;
119156
if (databaseType.endsWith(CITY_DB_SUFFIX)) {
120157
try {
121158
geoData = retrieveCityGeoData(ipAddress);
@@ -136,12 +173,9 @@ public IngestDocument execute(IngestDocument ingestDocument) throws IOException
136173
}
137174
} else {
138175
throw new ElasticsearchParseException("Unsupported database type [" + lazyLoader.getDatabaseType()
139-
+ "]", new IllegalStateException());
140-
}
141-
if (geoData.isEmpty() == false) {
142-
ingestDocument.setFieldValue(targetField, geoData);
176+
+ "]", new IllegalStateException());
143177
}
144-
return ingestDocument;
178+
return geoData;
145179
}
146180

147181
@Override
@@ -360,14 +394,15 @@ public Factory(Map<String, DatabaseReaderLazyLoader> databaseReaders, GeoIpCache
360394

361395
@Override
362396
public GeoIpProcessor create(
363-
final Map<String, Processor.Factory> registry,
364-
final String processorTag,
365-
final Map<String, Object> config) throws IOException {
397+
final Map<String, Processor.Factory> registry,
398+
final String processorTag,
399+
final Map<String, Object> config) throws IOException {
366400
String ipField = readStringProperty(TYPE, processorTag, config, "field");
367401
String targetField = readStringProperty(TYPE, processorTag, config, "target_field", "geoip");
368402
String databaseFile = readStringProperty(TYPE, processorTag, config, "database_file", "GeoLite2-City.mmdb");
369403
List<String> propertyNames = readOptionalList(TYPE, processorTag, config, "properties");
370404
boolean ignoreMissing = readBooleanProperty(TYPE, processorTag, config, "ignore_missing", false);
405+
boolean firstOnly = readBooleanProperty(TYPE, processorTag, config, "first_only", true);
371406

372407
DatabaseReaderLazyLoader lazyLoader = databaseReaders.get(databaseFile);
373408
if (lazyLoader == null) {
@@ -397,11 +432,11 @@ public GeoIpProcessor create(
397432
properties = DEFAULT_ASN_PROPERTIES;
398433
} else {
399434
throw newConfigurationException(TYPE, processorTag, "database_file", "Unsupported database type ["
400-
+ databaseType + "]");
435+
+ databaseType + "]");
401436
}
402437
}
403438

404-
return new GeoIpProcessor(processorTag, ipField, lazyLoader, targetField, properties, ignoreMissing, cache);
439+
return new GeoIpProcessor(processorTag, ipField, lazyLoader, targetField, properties, ignoreMissing, cache, firstOnly);
405440
}
406441
}
407442

@@ -460,7 +495,7 @@ public static Property parseProperty(String databaseType, String value) {
460495
return property;
461496
} catch (IllegalArgumentException e) {
462497
throw new IllegalArgumentException("illegal property value [" + value + "]. valid values are " +
463-
Arrays.toString(validProperties.toArray()));
498+
Arrays.toString(validProperties.toArray()));
464499
}
465500
}
466501
}

0 commit comments

Comments
 (0)