[influxdb] Fixes issue 8798 and 8697 problems storing integer types (#8831)

* Update documentation with changed Influx2 RC port
* Fix problem with non decimal numeric types

Improve documentation with more explicit information about Influx types used
Implement toString to InfluxPoint to allow some trace info to be useful in case it's needed

Fixes #8697
Fixes #8798

Signed-off-by: Joan Pujol <joanpujol@gmail.com>
This commit is contained in:
Joan Pujol 2020-10-22 10:26:21 +02:00 committed by GitHub
parent ed6d68ffd7
commit c3666581b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 51 additions and 36 deletions

View File

@ -1,14 +1,19 @@
# InfluxDB (0.9 and newer) Persistence # InfluxDB (0.9 and newer) Persistence
This service allows you to persist and query states using the [InfluxDB](https://www.influxdata.com/products/influxdb-overview/) and [InfluxDB 2.0](https://v2.docs.influxdata.com/v2.0/) time series database. The persisted values can be queried from within openHAB. There also are nice tools on the web for visualizing InfluxDB time series, such as [Grafana](http://grafana.org/). This service allows you to persist and query states using the [InfluxDB](https://www.influxdata.com/products/influxdb-overview/) and [InfluxDB 2.0](https://v2.docs.influxdata.com/v2.0/) time series database. The persisted values can be queried from within openHAB.
There also are nice tools on the web for visualizing InfluxDB time series, such as [Grafana](http://grafana.org/) and new Influx DB 2.0 version introduces [powerful data processing features.](https://docs.influxdata.com/influxdb/v2.0/process-data/get-started/)
## Database Structure ## Database Structure
- This service allows you to persist and query states using the time series database. - This service allows you to persist and query states using the time series database.
- The states of an item are persisted in *measurements* points with names equal to the name of the item, or the alias, if one is provided. In both variants, a *tag* named "item" is added, containing the item name. - The states of an item are persisted in *measurements* points with names equal to the name of the item, or the alias, if one is provided. In both variants, a *tag* named "item" is added, containing the item name.
All values are stored in a *field* called "value" using integers or doubles if possible,`OnOffType` and `OpenClosedType` values are stored using 0 or 1. All values are stored in a *field* called "value" using the following types:
- If configured extra tags for item category, label or type can be added fore each point. - **float** for DecimalType and QuantityType
- **integer** for `OnOffType` and `OpenClosedType` (values are stored using 0 or 1) and `DateTimeType` (milliseconds since 1970-01-01T00:00:00Z)
- **string** for the rest of types
- If configured, extra tags for item category, label or type can be added fore each point.
Some example entries for an item with the name "speedtest" without any further configuration would look like this: Some example entries for an item with the name "speedtest" without any further configuration would look like this:

View File

@ -69,9 +69,9 @@ public class InfluxDBStateConvertUtils {
} else if (state instanceof PointType) { } else if (state instanceof PointType) {
value = point2String((PointType) state); value = point2String((PointType) state);
} else if (state instanceof DecimalType) { } else if (state instanceof DecimalType) {
value = convertBigDecimalToNum(((DecimalType) state).toBigDecimal()); value = ((DecimalType) state).toBigDecimal();
} else if (state instanceof QuantityType<?>) { } else if (state instanceof QuantityType<?>) {
value = convertBigDecimalToNum(((QuantityType<?>) state).toBigDecimal()); value = ((QuantityType<?>) state).toBigDecimal();
} else if (state instanceof OnOffType) { } else if (state instanceof OnOffType) {
value = state == OnOffType.ON ? DIGITAL_VALUE_ON : DIGITAL_VALUE_OFF; value = state == OnOffType.ON ? DIGITAL_VALUE_ON : DIGITAL_VALUE_OFF;
} else if (state instanceof OpenClosedType) { } else if (state instanceof OpenClosedType) {
@ -166,21 +166,4 @@ public class InfluxDBStateConvertUtils {
} }
return buf.toString(); // latitude, longitude, altitude return buf.toString(); // latitude, longitude, altitude
} }
/**
* This method returns an integer if possible if not a double is returned. This is an optimization
* for influxdb because integers have less overhead.
*
* @param value the BigDecimal to be converted
* @return A double if possible else a double is returned.
*/
private static Object convertBigDecimalToNum(BigDecimal value) {
Object convertedValue;
if (value.scale() == 0) {
convertedValue = value.toBigInteger();
} else {
convertedValue = value.doubleValue();
}
return convertedValue;
}
} }

View File

@ -88,4 +88,10 @@ public class InfluxPoint {
return new InfluxPoint(this); return new InfluxPoint(this);
} }
} }
@Override
public String toString() {
return "InfluxPoint{" + "measurementName='" + measurementName + '\'' + ", time=" + time + ", value=" + value
+ ", tags=" + tags + '}';
}
} }

View File

@ -27,7 +27,7 @@
<parameter name="url" type="text" required="true" groupName="connection"> <parameter name="url" type="text" required="true" groupName="connection">
<context>url</context> <context>url</context>
<label>Database URL</label> <label>Database URL</label>
<description>The database URL, e.g. http://127.0.0.1:8086 or http://127.0.0.1:9999</description> <description>The database URL, e.g. http://127.0.0.1:8086</description>
<default>http://127.0.0.1:8086</default> <default>http://127.0.0.1:8086</default>
</parameter> </parameter>

View File

@ -32,7 +32,7 @@ public class ConfigurationTestHelper {
public static Map<String, @Nullable Object> createValidConfigurationParameters() { public static Map<String, @Nullable Object> createValidConfigurationParameters() {
Map<String, @Nullable Object> config = new HashMap<>(); Map<String, @Nullable Object> config = new HashMap<>();
config.put(URL_PARAM, "http://localhost:9999"); config.put(URL_PARAM, "http://localhost:8086");
config.put(VERSION_PARAM, InfluxDBVersion.V2.name()); config.put(VERSION_PARAM, InfluxDBVersion.V2.name());
config.put(TOKEN_PARAM, "sampletoken"); config.put(TOKEN_PARAM, "sampletoken");
config.put(DATABASE_PARAM, "openhab"); config.put(DATABASE_PARAM, "openhab");

View File

@ -13,7 +13,8 @@
package org.openhab.persistence.influxdb.internal; package org.openhab.persistence.influxdb.internal;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.Instant; import java.time.Instant;
@ -22,6 +23,8 @@ import java.time.ZonedDateTime;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.openhab.core.library.items.ContactItem; import org.openhab.core.library.items.ContactItem;
import org.openhab.core.library.items.DateTimeItem; import org.openhab.core.library.items.DateTimeItem;
import org.openhab.core.library.items.NumberItem; import org.openhab.core.library.items.NumberItem;
@ -40,7 +43,13 @@ public class InfluxDBStateConvertUtilsTest {
@Test @Test
public void convertDecimalState() { public void convertDecimalState() {
DecimalType decimalType = new DecimalType(new BigDecimal("1.12")); DecimalType decimalType = new DecimalType(new BigDecimal("1.12"));
assertThat((Double) InfluxDBStateConvertUtils.stateToObject(decimalType), closeTo(1.12, 0.01)); assertThat(InfluxDBStateConvertUtils.stateToObject(decimalType), is(new BigDecimal("1.12")));
}
@Test
public void convertIntegerDecimalState() {
DecimalType decimalType = new DecimalType(12L);
assertThat(InfluxDBStateConvertUtils.stateToObject(decimalType), is(new BigDecimal("12")));
} }
@Test @Test
@ -57,9 +66,10 @@ public class InfluxDBStateConvertUtilsTest {
assertThat(InfluxDBStateConvertUtils.stateToObject(type), equalTo(nowInMillis)); assertThat(InfluxDBStateConvertUtils.stateToObject(type), equalTo(nowInMillis));
} }
@Test @ParameterizedTest
public void convertDecimalToState() { @ValueSource(strings = { "1.12", "25" })
BigDecimal val = new BigDecimal("1.12"); public void convertDecimalToState(String number) {
BigDecimal val = new BigDecimal(number);
NumberItem item = new NumberItem("name"); NumberItem item = new NumberItem("name");
assertThat(InfluxDBStateConvertUtils.objectToState(val, item), equalTo(new DecimalType(val))); assertThat(InfluxDBStateConvertUtils.objectToState(val, item), equalTo(new DecimalType(val)));
} }

View File

@ -22,9 +22,12 @@ import org.openhab.core.library.types.DecimalType;
@NonNullByDefault @NonNullByDefault
public class ItemTestHelper { public class ItemTestHelper {
public static NumberItem createNumberItem(String name, int value) { public static NumberItem createNumberItem(String name, Number value) {
NumberItem numberItem = new NumberItem(name); NumberItem numberItem = new NumberItem(name);
numberItem.setState(new DecimalType(value)); if (value instanceof Integer || value instanceof Long)
numberItem.setState(new DecimalType(value.longValue()));
else
numberItem.setState(new DecimalType(value.doubleValue()));
return numberItem; return numberItem;
} }
} }

View File

@ -16,8 +16,9 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import java.math.BigInteger; import java.math.BigDecimal;
import java.util.Map; import java.util.Map;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.DefaultLocation; import org.eclipse.jdt.annotation.DefaultLocation;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
@ -25,6 +26,8 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import org.openhab.core.items.Metadata; import org.openhab.core.items.Metadata;
@ -62,14 +65,19 @@ public class ItemToStorePointCreatorTest {
metadataRegistry = null; metadataRegistry = null;
} }
@Test @ParameterizedTest
public void convertBasicItem() { @MethodSource
NumberItem item = ItemTestHelper.createNumberItem("myitem", 5); public void convertBasicItem(Number number) {
NumberItem item = ItemTestHelper.createNumberItem("myitem", number);
InfluxPoint point = instance.convert(item, null); InfluxPoint point = instance.convert(item, null);
assertThat(point.getMeasurementName(), equalTo(item.getName())); assertThat(point.getMeasurementName(), equalTo(item.getName()));
assertThat("Must Store item name", point.getTags(), hasEntry("item", item.getName())); assertThat("Must Store item name", point.getTags(), hasEntry("item", item.getName()));
assertThat(point.getValue(), equalTo(new BigInteger("5"))); assertThat(point.getValue(), equalTo(new BigDecimal(number.toString())));
}
private static Stream<Number> convertBasicItem() {
return Stream.of(5, 5.5, 5L);
} }
@Test @Test