Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Commit

Permalink
Only keep the first element of multivalue field response. (#1026)
Browse files Browse the repository at this point in the history
* update

* update

* update

* update

* update

* update

* update

* update

* address comments, add doc

* update
  • Loading branch information
penghuo authored Feb 3, 2021
1 parent d7cfad6 commit e67522a
Show file tree
Hide file tree
Showing 7 changed files with 497 additions and 184 deletions.
13 changes: 13 additions & 0 deletions docs/experiment/ppl/general/datatypes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -406,3 +406,16 @@ PPL query::
|-----------+-------------|
| 1 | Seattle |
+-----------+-------------+

Example 3: Selecting Field of Array Value
-----------------------------------------

Select deeper level for object fields of array value which returns the first element in the array. For example, because inner field ``accounts.id`` has three values instead of a tuple in this document, the first entry is returned.::

od> source = people | fields accounts, accounts.id;
fetched rows / total rows = 1/1
+------------+---------------+
| accounts | accounts.id |
|------------+---------------|
| {'id': 1} | 1 |
+------------+---------------+
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*
*/

package com.amazon.opendistroforelasticsearch.sql.elasticsearch.data.utils;

import java.util.Iterator;
import java.util.Map;
import org.apache.commons.lang3.tuple.Pair;

/**
* Regardless the underling data format, the {@link Content} define the data in abstract manner.
* which could be parsed by ElasticsearchExprValueFactory.
* There are two major use cases:
* 1. Represent the JSON data retrieve from Elasticsearch search response.
* 2. Represent the Object data extract from the Elasticsearch aggregation response.
*/
public interface Content {

/**
* Is null value.
*/
boolean isNull();

/**
* Is number value.
*/
boolean isNumber();

/**
* Is string value.
*/
boolean isString();

/**
* Get integer value.
*/
Integer intValue();

/**
* Get long value.
*/
Long longValue();

/**
* Get short value.
*/
Short shortValue();

/**
* Get byte value.
*/
Byte byteValue();

/**
* Get float value.
*/
Float floatValue();

/**
* Get double value.
*/
Double doubleValue();

/**
* Get string value.
*/
String stringValue();

/**
* Get boolean value.
*/
Boolean booleanValue();

/**
* Get map of {@link Content} value.
*/
Iterator<Map.Entry<String, Content>> map();

/**
* Get array of {@link Content} value.
*/
Iterator<? extends Content> array();

/**
* Get geo point value.
*/
Pair<Double, Double> geoValue();

/**
* Get {@link Object} value.
*/
Object objectValue();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*
*/

package com.amazon.opendistroforelasticsearch.sql.elasticsearch.data.utils;

import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.collect.Iterators;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.tuple.Pair;

/**
* The Implementation of Content to represent {@link JsonNode}.
*/
@RequiredArgsConstructor
public class ElasticsearchJsonContent implements Content {

private final JsonNode value;

@Override
public Integer intValue() {
return value().intValue();
}

@Override
public Long longValue() {
return value().longValue();
}

@Override
public Short shortValue() {
return value().shortValue();
}

@Override
public Byte byteValue() {
return (byte) value().shortValue();
}

@Override
public Float floatValue() {
return value().floatValue();
}

@Override
public Double doubleValue() {
return value().doubleValue();
}

@Override
public String stringValue() {
return value().asText();
}

@Override
public Boolean booleanValue() {
return value().booleanValue();
}

@Override
public Iterator<Map.Entry<String, Content>> map() {
LinkedHashMap<String, Content> map = new LinkedHashMap<>();
final JsonNode mapValue = value();
mapValue
.fieldNames()
.forEachRemaining(
field -> map.put(field, new ElasticsearchJsonContent(mapValue.get(field))));
return map.entrySet().iterator();
}

@Override
public Iterator<? extends Content> array() {
return Iterators.transform(value.elements(), ElasticsearchJsonContent::new);
}

@Override
public boolean isNull() {
return value == null || value.isNull();
}

@Override
public boolean isNumber() {
return value().isNumber();
}

@Override
public boolean isString() {
return value().isTextual();
}

@Override
public Object objectValue() {
return value();
}

@Override
public Pair<Double, Double> geoValue() {
return Pair.of(value().get("lat").doubleValue(), value().get("lon").doubleValue());
}

/**
* Return the first element if is Elasticsearch Array.
* https://www.elastic.co/guide/en/elasticsearch/reference/current/array.html.
*/
private JsonNode value() {
return value.isArray() ? value.get(0) : value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*
*/

package com.amazon.opendistroforelasticsearch.sql.elasticsearch.data.utils;

import java.util.AbstractMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.tuple.Pair;

/**
* The Implementation of Content to represent {@link Object}.
*/
@RequiredArgsConstructor
public class ObjectContent implements Content {

private final Object value;

@Override
public Integer intValue() {
return parseNumberValue(value, Integer::valueOf, Number::intValue);
}

@Override
public Long longValue() {
return parseNumberValue(value, Long::valueOf, Number::longValue);
}

@Override
public Short shortValue() {
return parseNumberValue(value, Short::valueOf, Number::shortValue);
}

@Override
public Byte byteValue() {
return parseNumberValue(value, Byte::valueOf, Number::byteValue);
}

@Override
public Float floatValue() {
return parseNumberValue(value, Float::valueOf, Number::floatValue);
}

@Override
public Double doubleValue() {
return parseNumberValue(value, Double::valueOf, Number::doubleValue);
}

@Override
public String stringValue() {
return (String) value;
}

@Override
public Boolean booleanValue() {
return (Boolean) value;
}

@Override
public Object objectValue() {
return value;
}

@SuppressWarnings("unchecked")
@Override
public Iterator<Map.Entry<String, Content>> map() {
return ((Map<String, Object>) value).entrySet().stream()
.map(entry -> (Map.Entry<String, Content>) new AbstractMap.SimpleEntry<String, Content>(
entry.getKey(),
new ObjectContent(entry.getValue())))
.iterator();
}

@SuppressWarnings("unchecked")
@Override
public Iterator<? extends Content> array() {
return ((List<Object>) value).stream().map(ObjectContent::new).iterator();
}

@Override
public boolean isNull() {
return value == null;
}

@Override
public boolean isNumber() {
return value instanceof Number;
}

@Override
public boolean isString() {
return value instanceof String;
}

@Override
public Pair<Double, Double> geoValue() {
final String[] split = ((String) value).split(",");
return Pair.of(Double.valueOf(split[0]), Double.valueOf(split[1]));
}

private <T> T parseNumberValue(Object value, Function<String, T> stringTFunction,
Function<Number, T> numberTFunction) {
if (value instanceof String) {
return stringTFunction.apply((String) value);
} else {
return numberTFunction.apply((Number) value);
}
}
}
Loading

0 comments on commit e67522a

Please sign in to comment.