Coder Social home page Coder Social logo

influxdata / influxdb-client-php Goto Github PK

View Code? Open in Web Editor NEW
147.0 15.0 47.0 4.41 MB

InfluxDB (v2+) Client Library for PHP

Home Page: https://influxdata.github.io/influxdb-client-php/

License: MIT License

Dockerfile 0.01% Makefile 0.04% PHP 99.83% Shell 0.12%
influxdb php

influxdb-client-php's Introduction

influxdb-client-php

CircleCI codecov Packagist Version License GitHub issues GitHub pull requests PHP from Packagist Slack Status

This repository contains the reference PHP client for the InfluxDB 2.x.

Note: Use this client library with InfluxDB 2.x and InfluxDB 1.8+ (see details). For connecting to InfluxDB 1.7 or earlier instances, use the influxdb-php client library.

Documentation

This section contains links to the client library documentation.

Installation

The client is not hard coupled to HTTP client library like Guzzle, Buzz or something else. The client uses general abstractions (PSR-7 - HTTP messages, PSR-17 - HTTP factories, PSR-18 - HTTP client) which give you freedom to use your favorite one.

Install the library

The InfluxDB 2 client is bundled and hosted on https://packagist.org/ and can be installed with composer:

composer require influxdata/influxdb-client-php guzzlehttp/guzzle

Usage

Creating a client

Use InfluxDB2\Client to create a client connected to a running InfluxDB 2 instance.

$client = new InfluxDB2\Client([
    "url" => "http://localhost:8086",
    "token" => "my-token",
    "bucket" => "my-bucket",
    "org" => "my-org",
    "precision" => InfluxDB2\Model\WritePrecision::NS
]);

Client Options

Option Description Note Type Default
url InfluxDB server API url (e.g. http://localhost:8086) required String none
token Token to use for the authorization required String none
bucket Default destination bucket for writes String none
org Default destination organization for writes String none
precision Default precision for the unix timestamps within the body line-protocol String none
allow_redirects Enable HTTP redirects bool true
debug Enable verbose logging of http requests bool false
logFile Default output for logs bool php://output
httpClient Configured HTTP client to use for communication with InfluxDB Psr\Http\Client\ClientInterface none
verifySSL Turn on/off SSL certificate verification. Set to false to disable certificate verification. ⚠️ required Guzzle HTTP client bool true
timeout Describing the number of seconds to wait while trying to connect to a server. Use 0 to wait indefinitely. ⚠️ required Guzzle HTTP client int 10
proxy specify an HTTP proxy, or an array to specify different proxies for different protocols. ⚠️ required Guzzle HTTP client string none

Custom HTTP client

The following code shows how to use and configure cURL HTTP client:

Install dependencies via composer
composer require influxdata/influxdb-client-php nyholm/psr7 php-http/curl-client
Configure cURL client
$curlOptions = [
    CURLOPT_CONNECTTIMEOUT => 30, // The number of seconds to wait while trying to connect.
];
$curlClient = new Http\Client\Curl\Client(
    Http\Discovery\Psr17FactoryDiscovery::findRequestFactory(),
    Http\Discovery\Psr17FactoryDiscovery::findStreamFactory(),
    $curlOptions
);
Initialize InfluxDB client
$client = new Client([
    "url" => "http://localhost:8086",
    "token" => "my-token",
    "bucket" => "my-bucket",
    "org" => "my-org",
    "httpClient" => $curlClient
]);

Queries

The result retrieved by QueryApi could be formatted as a:

  1. Raw query response
  2. Flux data structure: FluxTable, FluxColumn and FluxRecord
  3. Stream of FluxRecord

Query raw

Synchronously executes the Flux query and return result as unprocessed String

$this->client = new Client([
    "url" => "http://localhost:8086",
    "token" => "my-token",
    "bucket" => "my-bucket",
    "precision" => WritePrecision::NS,
    "org" => "my-org",
    "debug" => false
]);

$this->queryApi = $this->client->createQueryApi();

$result = $this->queryApi->queryRaw(
            'from(bucket:"my-bucket") |> range(start: 1970-01-01T00:00:00.000000001Z) |> last()');

Synchronous query

Synchronously executes the Flux query and return result as a Array of FluxTables

$this->client = new Client([
    "url" => "http://localhost:8086",
    "token" => "my-token",
    "bucket" => "my-bucket",
    "precision" => WritePrecision::NS,
    "org" => "my-org",
    "debug" => false
]);

$this->queryApi = $this->client->createQueryApi();

$result = $this->queryApi->query(
            'from(bucket:"my-bucket") |> range(start: 1970-01-01T00:00:00.000000001Z) |> last()');

This can then easily be encoded to JSON with json_encode

header('Content-type:application/json;charset=utf-8');
echo json_encode( $result, JSON_PRETTY_PRINT ) ;

Query stream

Synchronously executes the Flux query and return stream of FluxRecord

$this->client = new Client([
    "url" => "http://localhost:8086",
    "token" => "my-token",
    "bucket" => "my-bucket",
    "precision" => WritePrecision::NS,
    "org" => "my-org",
    "debug" => false
]);

$this->queryApi = $this->client->createQueryApi();

$parser = $this->queryApi->queryStream(
            'from(bucket:"my-bucket") |> range(start: 1970-01-01T00:00:00.000000001Z) |> last()');

foreach ($parser->each() as $record)
{
    ...
}

Parameterized queries

InfluxDB Cloud supports Parameterized Queries that let you dynamically change values in a query using the InfluxDB API. Parameterized queries make Flux queries more reusable and can also be used to help prevent injection attacks.

InfluxDB Cloud inserts the params object into the Flux query as a Flux record named params. Use dot or bracket notation to access parameters in the params record in your Flux query. Parameterized Flux queries support only int , float, and string data types. To convert the supported data types into other Flux basic data types, use Flux type conversion functions.

Parameterized query example:

⚠️ Parameterized Queries are supported only in InfluxDB Cloud, currently there is no support in InfluxDB OSS.

<?php
require __DIR__ . '/../vendor/autoload.php';

use InfluxDB2\Client;
use InfluxDB2\Model\Query;
use InfluxDB2\Point;
use InfluxDB2\WriteType as WriteType;

$url = "https://us-west-2-1.aws.cloud2.influxdata.com";
$organization = 'my-org';
$bucket = 'my-bucket';
$token = 'my-token';

$client = new Client([
    "url" => $url,
    "token" => $token,
    "bucket" => $bucket,
    "org" => $organization,
    "precision" => InfluxDB2\Model\WritePrecision::NS,
    "debug" => false
]);

$writeApi = $client->createWriteApi(["writeType" => WriteType::SYNCHRONOUS]);
$queryApi = $client->createQueryApi();

$today = new DateTime("now");
$yesterday = $today->sub(new DateInterval("P1D"));

$p = new Point("temperature");
$p->addTag("location", "north")->addField("value", 60)->time($yesterday);
$writeApi->write($p);
$writeApi->close();

//
// Query range start parameter using duration
//
$parameterizedQuery = "from(bucket: params.bucketParam) |> range(start: duration(v: params.startParam))";
$query = new Query();
$query->setQuery($parameterizedQuery);
$query->setParams(["bucketParam" => "my-bucket", "startParam" => "-1d"]);
$tables = $queryApi->query($query);

foreach ($tables as $table) {
    foreach ($table->records as $record) {
        var_export($record->values);
    }
}

//
// Query range start parameter using DateTime
//
$parameterizedQuery = "from(bucket: params.bucketParam) |> range(start: time(v: params.startParam))";
$query->setParams(["bucketParam" => "my-bucket", "startParam" => $yesterday]);
$query->setQuery($parameterizedQuery);
$tables = $queryApi->query($query);

foreach ($tables as $table) {
    foreach ($table->records as $record) {
        var_export($record->values);
    }
}

$client->close();

Writing data

The WriteApi supports synchronous and batching writes into InfluxDB 2.x. In default api uses synchronous write. To enable batching you can use WriteOption.

$client = new InfluxDB2\Client(["url" => "http://localhost:8086", "token" => "my-token",
    "bucket" => "my-bucket",
    "org" => "my-org",
    "precision" => InfluxDB2\Model\WritePrecision::NS
]);
$write_api = $client->createWriteApi();
$write_api->write('h2o,location=west value=33i 15');

Batching

The writes are processed in batches which are configurable by WriteOptions:

Property Description Default Value
writeType type of write SYNCHRONOUS / BATCHING / SYNCHRONOUS
batchSize the number of data point to collect in batch 10
retryInterval the number of milliseconds to retry unsuccessful write. The retry interval is "exponentially" used when the InfluxDB server does not specify "Retry-After" header. 5000
jitterInterval the number of milliseconds before the data is written increased by a random amount 0
maxRetries the number of max retries when write fails 5
maxRetryDelay maximum delay when retrying write in milliseconds 125000
maxRetryTime maximum total retry timeout in milliseconds 180000
exponentialBase the base for the exponential retry delay, the next delay is computed using random exponential backoff as a random value within the interval retryInterval * exponentialBase^(attempts-1) and retryInterval * exponentialBase^(attempts). Example for retryInterval=5000, exponentialBase=2, maxRetryDelay=125000, total=5 Retry delays are random distributed values within the ranges of [5000-10000, 10000-20000, 20000-40000, 40000-80000, 80000-125000] 2
use InfluxDB2\Client;
use InfluxDB2\WriteType as WriteType;

$client = new Client(["url" => "http://localhost:8086", "token" => "my-token",
    "bucket" => "my-bucket",
    "org" => "my-org",
    "precision" => InfluxDB2\Model\WritePrecision::NS
]);

$writeApi = $client->createWriteApi(
    ["writeType" => WriteType::BATCHING, 'batchSize' => 1000]);

foreach (range(1, 10000) as $number) {
    $writeApi->write("mem,host=aws_europe,type=batch value=1i $number");
}

// flush remaining data
$writeApi->close();

Time precision

Configure default time precision:

$client = new InfluxDB2\Client(["url" => "http://localhost:8086", "token" => "my-token",
    "bucket" => "my-bucket",
    "org" => "my-org",
    "precision" => \InfluxDB2\Model\WritePrecision::NS
]);

Configure precision per write:

$client = new InfluxDB2\Client([
    "url" => "http://localhost:8086",
    "token" => "my-token",
    "bucket" => "my-bucket",
    "org" => "my-org",
]);

$writeApi = $client->createWriteApi();
$writeApi->write('h2o,location=west value=33i 15', \InfluxDB2\Model\WritePrecision::MS);

Allowed values for precision are:

  • WritePrecision::NS for nanosecond
  • WritePrecision::US for microsecond
  • WritePrecision::MS for millisecond
  • WritePrecision::S for second

Configure destination

Default bucket and organization destination are configured via InfluxDB2\Client:

$client = new InfluxDB2\Client([
    "url" => "http://localhost:8086",
    "token" => "my-token",
    "bucket" => "my-bucket",
]);

but there is also possibility to override configuration per write:

$client = new InfluxDB2\Client(["url" => "http://localhost:8086", "token" => "my-token"]);

$writeApi = $client->createWriteApi();
$writeApi->write('h2o,location=west value=33i 15', \InfluxDB2\Model\WritePrecision::MS, "production-bucket", "customer-1");

Data format

The data could be written as:

  1. string that is formatted as a InfluxDB's line protocol
  2. array with keys: name, tags, fields and time
  3. Data Point structure
  4. Array of above items
$client = new InfluxDB2\Client([
    "url" => "http://localhost:8086",
    "token" => "my-token",
    "bucket" => "my-bucket",
    "org" => "my-org",
    "precision" => InfluxDB2\Model\WritePrecision::US
]);

$writeApi = $client->createWriteApi();

//data in Point structure
$point=InfluxDB2\Point::measurement("h2o")
    ->addTag("location", "europe")
    ->addField("level",2)
    ->time(microtime(true));

$writeApi->write($point);

//data in array structure
$dataArray = ['name' => 'cpu', 
    'tags' => ['host' => 'server_nl', 'region' => 'us'],
    'fields' => ['internal' => 5, 'external' => 6],
    'time' => microtime(true)];

$writeApi->write($dataArray);

//write lineprotocol
$writeApi->write('h2o,location=west value=33i 15');

Default Tags

Sometimes is useful to store same information in every measurement e.g. hostname, location, customer. The client is able to use static value, app settings or env variable as a tag value.

The expressions:

  • California Miner - static value
  • ${env.hostname} - environment property
Via API
$this->client = new Client([
    "url" => "http://localhost:8086",
    "token" => "my-token",
    "bucket" => "my-bucket",
    "precision" => WritePrecision::NS,
    "org" => "my-org",
    "tags" => ['id' => '132-987-655', 
        'hostname' => '${env.Hostname}']
]);

$writeApi = $this->client->createWriteApi(null, ['data_center' => '${env.data_center}']);
    
$writeApi->pointSettings->addDefaultTag('customer', 'California Miner');

$point = Point::measurement('h2o')
            ->addTag('location', 'europe')
            ->addField('level', 2);

$this->writeApi->write($point);

Advanced Usage

Check the server status

Server availability can be checked using the $client->ping(); method. That is equivalent of the influx ping.

InfluxDB 1.8 API compatibility

InfluxDB 1.8.0 introduced forward compatibility APIs for InfluxDB 2.x. This allow you to easily move from InfluxDB 1.x to InfluxDB 2.x Cloud or open source.

The following forward compatible APIs are available:

API Endpoint Description
QueryApi.php /api/v2/query Query data in InfluxDB 1.8.0+ using the InfluxDB 2.x API and Flux (endpoint should be enabled by flux-enabled option)
WriteApi.php /api/v2/write Write data to InfluxDB 1.8.0+ using the InfluxDB 2.x API
HealthApi.php /health Check the health of your InfluxDB instance

For detail info see InfluxDB 1.8 example.

InfluxDB 2.x management API

InfluxDB 2.x API client is generated using influxdb-clients-apigen. Sources are in InfluxDB2\Service\ and InfluxDB2\Model\ packages.

The following example shows how to use OrganizationService and BucketService to create a new bucket.

require __DIR__ . '/../vendor/autoload.php';

use InfluxDB2\Client;
use InfluxDB2\Model\BucketRetentionRules;
use InfluxDB2\Model\Organization;
use InfluxDB2\Model\PostBucketRequest;
use InfluxDB2\Service\BucketsService;
use InfluxDB2\Service\OrganizationsService;

$organization = 'my-org';
$bucket = 'my-bucket';
$token = 'my-token';

$client = new Client([
    "url" => "http://localhost:8086",
    "token" => $token,
    "bucket" => $bucket,
    "org" => $organization,
    "precision" => InfluxDB2\Model\WritePrecision::S
]);

function findMyOrg($client): ?Organization
{
    /** @var OrganizationsService $orgService */
    $orgService = $client->createService(OrganizationsService::class);
    $orgs = $orgService->getOrgs()->getOrgs();
    foreach ($orgs as $org) {
        if ($org->getName() == $client->options["org"]) {
            return $org;
        }
    }
    return null;
}

$bucketsService = $client->createService(BucketsService::class);

$rule = new BucketRetentionRules();
$rule->setEverySeconds(3600);

$bucketName = "example-bucket-" . microtime();
$bucketRequest = new PostBucketRequest();
$bucketRequest->setName($bucketName)
    ->setRetentionRules([$rule])
    ->setOrgId(findMyOrg($client)->getId());

//create bucket
$respBucket = $bucketsService->postBuckets($bucketRequest);
print $respBucket;

$client->close();

Writing via UDP

Sending via UDP will be useful in cases when the execution time is critical to avoid potential delays (even timeouts) in sending metrics to the InfluxDB while are problems with the database or network connectivity.
As is known, sending via UDP occurs without waiting for a response, unlike TCP (HTTP).

UDP Writer Requirements:

  1. Installed ext-sockets
  2. Since Influxdb 2.0+ does not support UDP protocol natively you need to install and configure Telegraf plugin: https://docs.influxdata.com/telegraf/v1.16/plugins/#socket_listener
  3. Extra config option passed to client: udpPort. Optionally you can specify udpHost, otherwise udpHost will parsed from url option
  4. Extra config option passed to client: ipVersion. Optionally you can specify the ip version, defaults to IPv4
$client = new InfluxDB2\Client(["url" => "http://localhost:8086", "token" => "my-token",
    "bucket" => "my-bucket",
    "org" => "my-org",
    "precision" => InfluxDB2\Model\WritePrecision::NS,
    "udpPort" => 8094,
    "ipVersion" => 6,
]);
$writer = $client->createUdpWriter();
$writer->write('h2o,location=west value=33i 15');
$writer->close();

Delete data

The DefaultService.php supports deletes points from an InfluxDB bucket.

<?php
/**
 * Shows how to delete data from InfluxDB by client
 */
use InfluxDB2\Client;
use InfluxDB2\Model\DeletePredicateRequest;
use InfluxDB2\Service\DeleteService;

$url = 'http://localhost:8086';
$token = 'my-token';
$org = 'my-org';
$bucket = 'my-bucket';

$client = new Client([
    "url" => $url,
    "token" => $token,
    "bucket" => $bucket,
    "org" => $org,
    "precision" => InfluxDB2\Model\WritePrecision::S
]);

//
// Delete data by measurement and tag value
//
/** @var DeleteService $service */
$service = $client->createService(DeleteService::class);

$predicate = new DeletePredicateRequest();
$predicate->setStart(DateTime::createFromFormat('Y', '2020'));
$predicate->setStop(new DateTime());
$predicate->setPredicate("_measurement=\"mem\" AND host=\"host1\"");

$service->postDelete($predicate, null, $org, $bucket);

$client->close();

For more details see DeleteDataExample.php.

Proxy and redirects

You can configure InfluxDB PHP client behind a proxy in two ways:

1. Using environment variable

Set environment variable HTTP_PROXY or HTTPS_PROXY based on the scheme of your server url. For more info see Guzzle docs - environment Variables.

2. Configure client to use proxy via Options

You can pass a proxy configuration when creating the client:

$client = new InfluxDB2\Client([
  "url" => "http://localhost:8086", 
  "token" => "my-token",
  "bucket" => "my-bucket",
  "org" => "my-org",
  "proxy" => "http://192.168.16.1:10",
]);

For more info see Guzzle docs - proxy.

Redirects

Client automatically follows HTTP redirects. You can configure redirects behaviour by a allow_redirects configuration:

$client = new InfluxDB2\Client([
  "url" => "http://localhost:8086", 
  "token" => "my-token",
  "bucket" => "my-bucket",
  "org" => "my-org",
  "allow_redirects" => false,
]);

For more info see Redirect Plugin docs - allow_redirects

Local tests

Run once to install dependencies:

make deps

Run unit & intergration tests:

make test

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/influxdata/influxdb-client-php.

License

The gem is available as open source under the terms of the MIT License.

influxdb-client-php's People

Contributors

alanaasmaa avatar alexbergsland avatar bednar avatar bellardia avatar cstuder avatar dependabot[bot] avatar ecsv avatar glushkovds avatar hotfix31 avatar jerodev avatar kelseiv avatar kevin0x90 avatar machour avatar michaelahojna avatar mkrauser avatar nicop-s avatar powersj avatar rawkode avatar rhajek avatar rianfuro avatar robquistnl avatar rolincova avatar smillerdev avatar sokil avatar tysonkamp avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

influxdb-client-php's Issues

Time not inserted correctly

Hi, I recently upgraded from old influx-php to new InfluxDB2. I try to simply insert some test values using an array structure:

  $conf = [
      "url" => $influxdbhost,
      "port" => 8086,
      "bucket" => $influxdbname,
      "org" => $influxdborg,
      "token" => $influxdbtoken,
      "precision" => \InfluxDB2\Model\WritePrecision::NS
  ];
  $influx=new InfluxDB2\Client($conf);
  $data = [
      'name' => 'elo',
      'tags' => $tags,
      'fields' => $fields,
      'time' => microtime(true)
  ];

  $write_api = $influx->createWriteApi();
  $result=$write_api->write($data);

Data is written, but the _time inserted in the database is something like: 1970-02-22T20:37:32.472Z, it's like microtime (which gets correct value when I try to convert it to Human date) is not processed correctly by influx. Like influx got messed up with seconds / microseconds or nanoseconds.

I tried to change the precision parameter with no good luck, Influx client returns an error.

When I try to insert same values but with line protocol syntax, passing the string without time parameter, it works as expected.

Specifications:

  • Client Version: 1.15.2
  • InfluxDB Version: 2.1.1
  • Platform: Debian Linux 10
  • PHP Version: 7.3.31

Support for InfluxQL (1.X style queries)

Proposal:
Would it be possible to add support for the "old-style" InfluxQL queries?

Current behavior:
I am testing the following query:

SELECT count/518400 from (SELECT count("status") FROM "system" WHERE status = \'running\' ) WHERE time > now()-60d AND count > 129600 GROUP BY "host"'

In PHP, my code looks something like the following:

// Load the InfluxDB library
use InfluxDB2\Client;
use InfluxDB2\Model\WritePrecision;
use InfluxDB2\Query\Builder;

$client = new Client([
    "url" => "my_URL:8086",
    "token" => "my_token",
    "bucket" => "my_bucket",
    "org" => "my_org",
    "precision" => WritePrecision::NS,
]);
$queryApi = $client->createQueryApi();

$queryQL = 'SELECT count/518400 from (SELECT count("status") FROM "system" WHERE status = \'running\' ) WHERE time > now()-60d AND count > 129600 GROUP BY "host"';

// Execute the query
$result = $queryApi->query($queryQL);

Desired behavior:
I would expect an output array. Instead, this throws the following error
AH01071: Got error 'PHP message: PHP Fatal error: Uncaught InfluxDB2\\ApiException: [400] Error connecting to the API (http://my_url:8086/api/v2/query?org=my_org)(: compilation failed: error @1:21-1:90: expected comma in property list, got IDENT\n\nerror @1:21-1:90: expected comma in property list, got LPAREN\n\nerror @1:39-1:89: invalid expression @1:87-1:88: '\n\nerror @1:39-1:89: unexpected token for property key: LPAREN (()) in /public_html/vendor/influxdata/influxdb-client-php/src/InfluxDB2/DefaultApi.php:157\nStack trace:\n#0 /public_html/vendor/influxdata/influxdb-client-php/src/InfluxDB2/DefaultApi.php(214): InfluxDB2\\DefaultApi->sendRequest()\n#1 /public_html/vendor/influxdata/influxdb-client-php/src/InfluxDB2/DefaultApi.php(79): InfluxDB2\\DefaultApi->request()\n#2 /public_html/vendor/influxdata/influxdb-client-php/src/InfluxDB2/QueryApi.php(115): InfluxDB2\\DefaultApi->post()\n#3 /public_html/vendor...'

Alternatives considered:
I tried "converting" the query to Flux as best as I could, but it returns an empty array:

$queryFlux = 'from(bucket: "my_bucket")
|> range(start: -60d)
|> filter(fn: (r) => r["status"] == "running")
|> count(column: "status")
|> filter(fn: (r) => r["_value"] > 129600)
|> map(fn: (r) => ({r with _value: r["_value"] / 518400}))
|> group(columns: ["host"])';

// Execute the query
$result = $queryApi->query($queryFlux);

Use case:
I have a lot of queries that I've built in Grafana that would be nice to more easily copy over, rather than re-formulate to the Flux queries. Plus would make it a lot easer to debug with a graphical UI such as Grafana that I can plug the queries into.

\n in a field value breaks the FluxCsvParser

Steps to reproduce:

  1. Inject "\n" in a field
  2. Query that field (stream, raw or query have the same behavior)

Expected behavior:

The response should escape weird characters in a field and not detect them as end of line.

Actual behavior:

The parser detects "\n" as end of line:

            // Break when a new line is found
            if ($byte === "\n") {
                break;
            }

We end up with a row that has less array values than the headers. Triggering an exception on parseRecord():

$strVal = $csv[$fluxColumn->index + 1];

Specifications:

  • Client Version: 50738a7
  • InfluxDB Version: Influx Cloud as of Nov. 3rd

Parser error

I am getting next error when I try to get data from my bucket:

{
    "message": "Undefined offset: 1",
    "exception": "ErrorException",
    "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\influxdata\\influxdb-client-php\\src\\InfluxDB2\\FluxCsvParser.php",
    "line": 78,
    "trace": [
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\influxdata\\influxdb-client-php\\src\\InfluxDB2\\FluxCsvParser.php",
            "line": 78,
            "function": "handleError",
            "class": "Illuminate\\Foundation\\Bootstrap\\HandleExceptions",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\influxdata\\influxdb-client-php\\src\\InfluxDB2\\FluxCsvParser.php",
            "line": 62,
            "function": "each",
            "class": "InfluxDB2\\FluxCsvParser",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\influxdata\\influxdb-client-php\\src\\InfluxDB2\\QueryApi.php",
            "line": 65,
            "function": "parse",
            "class": "InfluxDB2\\FluxCsvParser",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\app\\InfluxDB\\MyInfluxDB.php",
            "line": 31,
            "function": "query",
            "class": "InfluxDB2\\QueryApi",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\app\\Http\\Controllers\\ReadingsController.php",
            "line": 35,
            "function": "getLastReadings",
            "class": "App\\InfluxDB\\MyInfluxDB",
            "type": "->"
        },
        {
            "function": "getLastReadings",
            "class": "App\\Http\\Controllers\\ReadingsController",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Controller.php",
            "line": 54,
            "function": "call_user_func_array"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\ControllerDispatcher.php",
            "line": 45,
            "function": "callAction",
            "class": "Illuminate\\Routing\\Controller",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Route.php",
            "line": 219,
            "function": "dispatch",
            "class": "Illuminate\\Routing\\ControllerDispatcher",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Route.php",
            "line": 176,
            "function": "runController",
            "class": "Illuminate\\Routing\\Route",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
            "line": 680,
            "function": "run",
            "class": "Illuminate\\Routing\\Route",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 130,
            "function": "Illuminate\\Routing\\{closure}",
            "class": "Illuminate\\Routing\\Router",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Middleware\\SubstituteBindings.php",
            "line": 41,
            "function": "Illuminate\\Pipeline\\{closure}",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 171,
            "function": "handle",
            "class": "Illuminate\\Routing\\Middleware\\SubstituteBindings",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Middleware\\ThrottleRequests.php",
            "line": 59,
            "function": "Illuminate\\Pipeline\\{closure}",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 171,
            "function": "handle",
            "class": "Illuminate\\Routing\\Middleware\\ThrottleRequests",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Auth\\Middleware\\Authenticate.php",
            "line": 43,
            "function": "Illuminate\\Pipeline\\{closure}",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 171,
            "function": "handle",
            "class": "Illuminate\\Auth\\Middleware\\Authenticate",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 105,
            "function": "Illuminate\\Pipeline\\{closure}",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
            "line": 682,
            "function": "then",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
            "line": 657,
            "function": "runRouteWithinStack",
            "class": "Illuminate\\Routing\\Router",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
            "line": 623,
            "function": "runRoute",
            "class": "Illuminate\\Routing\\Router",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
            "line": 612,
            "function": "dispatchToRoute",
            "class": "Illuminate\\Routing\\Router",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php",
            "line": 176,
            "function": "dispatch",
            "class": "Illuminate\\Routing\\Router",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 130,
            "function": "Illuminate\\Foundation\\Http\\{closure}",
            "class": "Illuminate\\Foundation\\Http\\Kernel",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\app\\Http\\Middleware\\Localization.php",
            "line": 25,
            "function": "Illuminate\\Pipeline\\{closure}",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 171,
            "function": "handle",
            "class": "App\\Http\\Middleware\\Localization",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest.php",
            "line": 21,
            "function": "Illuminate\\Pipeline\\{closure}",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 171,
            "function": "handle",
            "class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest.php",
            "line": 21,
            "function": "Illuminate\\Pipeline\\{closure}",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 171,
            "function": "handle",
            "class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize.php",
            "line": 27,
            "function": "Illuminate\\Pipeline\\{closure}",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 171,
            "function": "handle",
            "class": "Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\CheckForMaintenanceMode.php",
            "line": 62,
            "function": "Illuminate\\Pipeline\\{closure}",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 171,
            "function": "handle",
            "class": "Illuminate\\Foundation\\Http\\Middleware\\CheckForMaintenanceMode",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\fideloper\\proxy\\src\\TrustProxies.php",
            "line": 57,
            "function": "Illuminate\\Pipeline\\{closure}",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 171,
            "function": "handle",
            "class": "Fideloper\\Proxy\\TrustProxies",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 105,
            "function": "Illuminate\\Pipeline\\{closure}",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php",
            "line": 151,
            "function": "then",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php",
            "line": 116,
            "function": "sendRequestThroughRouter",
            "class": "Illuminate\\Foundation\\Http\\Kernel",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\public\\index.php",
            "line": 55,
            "function": "handle",
            "class": "Illuminate\\Foundation\\Http\\Kernel",
            "type": "->"
        },
        {
            "file": "E:\\Vazni dokumenti\\Projekti\\IT Fabrik\\SenseCap\\sensecap-api\\server.php",
            "line": 21,
            "function": "require_once"
        }
    ]
}

Raw result is next:

#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,dateTime:RFC3339,double,string,string,string,string,string
#group,false,false,true,true,false,false,true,true,true,true,true
#default,_result,,,,,,,,,,
,result,table,_start,_stop,_time,_value,_field,_measurement,channel,device_eui,sensor_eui
,,0,2020-03-27T15:17:00.64917903Z,2020-03-27T16:17:00.64917903Z,2020-03-27T15:17:43.775791768Z,23.4,value,4097,1,2CF7F1201470029C,vs
,,0,2020-03-27T15:17:00.64917903Z,2020-03-27T16:17:00.64917903Z,2020-03-27T15:27:45.894980937Z,22.5,value,4097,1,2CF7F1201470029C,vs
,,0,2020-03-27T15:17:00.64917903Z,2020-03-27T16:17:00.64917903Z,2020-03-27T15:37:50.278735828Z,21.4,value,4097,1,2CF7F1201470029C,vs
,,0,2020-03-27T15:17:00.64917903Z,2020-03-27T16:17:00.64917903Z,2020-03-27T15:47:51.766261407Z,20.3,value,4097,1,2CF7F1201470029C,vs
,,0,2020-03-27T15:17:00.64917903Z,2020-03-27T16:17:00.64917903Z,2020-03-27T15:57:57.475875729Z,18.9,value,4097,1,2CF7F1201470029C,vs
,,0,2020-03-27T15:17:00.64917903Z,2020-03-27T16:17:00.64917903Z,2020-03-27T16:07:59.413550698Z,17.8,value,4097,1,2CF7F1201470029C,vs

#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,dateTime:RFC3339,double,string,string,string,string,string
#group,false,false,true,true,false,false,true,true,true,true,true
#default,_result,,,,,,,,,,
,result,table,_start,_stop,_time,_value,_field,_measurement,channel,device_eui,sensor_eui
,,1,2020-03-27T15:17:00.64917903Z,2020-03-27T16:17:00.64917903Z,2020-03-27T15:43:49.398558429Z,12.3,value,4102,1,2CF7F12014700247,vs

#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,dateTime:RFC3339,double,string,string,string,string,string
#group,false,false,true,true,false,false,true,true,true,true,true
#default,_result,,,,,,,,,,
,result,table,_start,_stop,_time,_value,_field,_measurement,channel,device_eui,sensor_eui
,,2,2020-03-27T15:17:00.64917903Z,2020-03-27T16:17:00.64917903Z,2020-03-27T15:17:43.772929338Z,43.1,value,4098,1,2CF7F1201470029C,vs
,,2,2020-03-27T15:17:00.64917903Z,2020-03-27T16:17:00.64917903Z,2020-03-27T15:27:45.897608471Z,49,value,4098,1,2CF7F1201470029C,vs
,,2,2020-03-27T15:17:00.64917903Z,2020-03-27T16:17:00.64917903Z,2020-03-27T15:37:50.27543528Z,48.2,value,4098,1,2CF7F1201470029C,vs
,,2,2020-03-27T15:17:00.64917903Z,2020-03-27T16:17:00.64917903Z,2020-03-27T15:47:51.769665096Z,44.7,value,4098,1,2CF7F1201470029C,vs
,,2,2020-03-27T15:17:00.64917903Z,2020-03-27T16:17:00.64917903Z,2020-03-27T15:57:57.47339818Z,48.5,value,4098,1,2CF7F1201470029C,vs
,,2,2020-03-27T15:17:00.64917903Z,2020-03-27T16:17:00.64917903Z,2020-03-27T16:07:59.40946618Z,52.3,value,4098,1,2CF7F1201470029C,vs

#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,dateTime:RFC3339,double,string,string,string,string,string
#group,false,false,true,true,false,false,true,true,true,true,true
#default,_result,,,,,,,,,,
,result,table,_start,_stop,_time,_value,_field,_measurement,channel,device_eui,sensor_eui
,,3,2020-03-27T15:17:00.64917903Z,2020-03-27T16:17:00.64917903Z,2020-03-27T15:43:49.39609423Z,30.7,value,4103,1,2CF7F12014700247,vs

Write API Field type

Hello,

I didn't find in the documentation write API how to set the field's type (Boolean, Bytes, Duration, String, Time, Float, Integer, UIntegers or Null).

Thanks for your help.

`InfluxDB2\FluxRecord->getValue()` can spit out a PHP “Undefined Index” Notice

The Problem

When executing a query that yields no _value, calling getValue() on a InfluxDB2\FluxRecord spits out a PHP Notice:

Undefined index: _value in vendor/influxdata/influxdb-client-php/src/InfluxDB2/FluxRecord.php on line 45

This should be suppressed, as a returned record might not contain this field

Steps to reproduce:

Execute a Flux query that has a map of drops the column _value or uses a pivot such as the example one given in the docs

from(bucket:"test")
  |> range(start: 1970-01-01T00:00:00.000000000Z)
  |> pivot(
    rowKey:["_time"],
    columnKey: ["_field"],
    valueColumn: "_value"
  )

Executing the above query and calling getValue() on each record of the stream will give a notice

$this->client = new Client([…]);
$this->queryApi = $this->client->createQueryApi();
$this->influxParser = $influxQueryApi->queryStream($fluxQuery);

foreach ($this->influxParser->each() as /* \InfluxDB2\FluxRecord */ $record) {
  echo $record->getValue();
}

Expected behavior:

No PHP Notice to be shown on screen.

Actual behavior:

A PHP Notice is shown on screen.

Specifications:

  • Client Version: all (tested with 1.15.2)
  • InfluxDB Version: all
  • Platform: all

Wrong time order

After wuery like this from(bucket: "parking_app/autogen") |> range(start: -48h ) |> filter(fn: (r) => r._measurement == "device_uplink" and r._field == "rssi")

query return:

2020-05-07T11:37:09.946Z RSSI2 is -44
2020-05-09T06:37:41.453Z RSSI2 is -71
2020-05-07T11:52:46.025Z RSSI2 is -50
2020-05-08T12:55:02.237Z RSSI2 is -67
2020-05-07T12:55:17.485Z RSSI2 is -47
2020-05-08T14:49:42.114Z RSSI2 is -63
2020-05-08T17:54:50.922Z RSSI2 is -53
2020-05-07T17:54:50.285Z RSSI2 is -49

Its not in a right time order.

Database look like this:
image

New query return format

Proposal:
Add the following new format for query result:
Array with the following columns:

time | tag_1 .... | tag_x | field_1 ... | field_y

Current behavior:
Currently output formats are only unprocessed string and FluxRecord.
These formats are not easy to exploit directly and need some extra treatments to be used.

Desired behavior:
A query result as an array with columns corresponding to the desired ones.

Alternatives considered:
None.

Use case:
It helps users (simplify and accelerate development) coming from others databases and from previous InfluxDB php clients.

Point::isNullOrEmptyString -- trim() expects parameter 1 to be string, array given

Hello! I installed you library. And found such error in my logs.

2021-06-23 10:23:39.933384 [109.229.133.164][3961211][][error][php][04215332889720b803140fe794379980][/login/] trim() expects parameter 1 to be string, array given (/srv/builds/www-45061/htdocs/protected/vendor/influxdata/influxdb-client-php/src/InfluxDB2/Point.php:263)
Stack trace:
#0 /srv/builds/www-45061/htdocs/protected/vendor/influxdata/influxdb-client-php/src/InfluxDB2/Point.php(127): InfluxDB2\Point->appendTags()
#1 /srv/builds/www-45061/htdocs/protected/vendor/influxdata/influxdb-client-php/src/InfluxDB2/WritePayloadSerializer.php(31): InfluxDB2\Point->toLineProtocol()
#2 /srv/builds/www-45061/htdocs/protected/vendor/influxdata/influxdb-client-php/src/InfluxDB2/UdpWriter.php(57): generatePayload()

I think you need to change condition in this method private function isNullOrEmptyString($str) from (!isset($str) || trim($str) === '') to (!is_string($str) || trim($str) === '')

Php7.4 compatibility

Php7.4 brought with it this rfc, so it now emits E_WARNINGs for invalid array access.

As far as I can see, this is the only part of the code that's affected by this:

public function __construct(array $writeOptions = null)
{
//initialize with default values
$this->writeType = $writeOptions["writeType"] ?: WriteType::SYNCHRONOUS;
$this->batchSize = $writeOptions["batchSize"] ?: self::DEFAULT_BATCH_SIZE;
$this->flushInterval = $writeOptions["flushInterval"] ?: self::DEFAULT_FLUSH_INTERVAL;
}

Which makes using the default options throw under php7.4 if your environment reports on E_WARNING, which is typical for development environments.

Depend on PSR-18 instead of Guzzle.

Proposal:
When working on projects that use many different external APIs, depending on a concrete version of one http library can become a problem. It would help a lot if influx-client-php could depend on PSR-18 instead so that the concrete version of the library could vary more.

Current behavior:
Today the library depends on a given Guzzle version.

Desired behavior:
The guzzle dependencies switched to depending on http/common instead. See Mailguns PHP lib for an example.

Use case:
When working on projects that use many different external APIs, depending on a concrete version of one http library can become a problem. We are still running Guzzle 6.x and may have to do busywork just to use influxdb-client-php.

The retry strategy should be also applied for connection errors

The connection errors such as Connection Refused, Connection Reset should be considered as retryable.

In theory the whole implementation of retry:

if ($code == null || !($code == 429 || $code == 503) || $attempts > $this->writeOptions->maxRetries) {
could be replaced by RetryMiddleware.

integrate on codeIgniter 4

Hello,
i want integrate influxDB on codeIgniter 4 so how i replace
"require DIR . '/vendor/autoload.php';" with namespace?

UdpWriter does not support IPv6

Proposal:
I would like to be able to use the UdpWriter not only with IPv4 but also with IPv6.

Current behavior:
Currently only IPv4 is supported due to the nature of socket_create

Desired behavior:
I would like to have a way to configure the client to use either IPv4 (maybe default) or IPv6.

Alternatives considered:
Alternative could be some kind of auto detection if IPv6 is available but i think this might be a bit too much.

Use case:
I want to use the UdpWriter in an infrastructure environment where IPv6 is used.

Deprecation warning when using PHP 8.1+

Steps to reproduce:
Stdout contains lots of Deprecated warning when running latest PHP 8.1+. We need to fix openapi generator templates.

Deprecated: Return type of InfluxDB2\Model\Dialect::offsetExists($offset) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/circleci/project/src/InfluxDB2/Model/Dialect.php on line 446
Deprecated: Return type of InfluxDB2\Model\Dialect::offsetGet($offset) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/circleci/project/src/InfluxDB2/Model/Dialect.php on line 458
Deprecated: Return type of InfluxDB2\Model\Dialect::offsetSet($offset, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/circleci/project/src/InfluxDB2/Model/Dialect.php on line 471
Deprecated: Return type of InfluxDB2\Model\Dialect::offsetUnset($offset) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/circleci/project/src/InfluxDB2/Model/Dialect.php on line 487
Deprecated: Return type of InfluxDB2\Model\HealthCheck::offsetExists($offset) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/circleci/project/src/InfluxDB2/Model/HealthCheck.php on line 425
Deprecated: Return type of InfluxDB2\Model\HealthCheck::offsetGet($offset) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/circleci/project/src/InfluxDB2/Model/HealthCheck.php on line 437
Deprecated: Return type of InfluxDB2\Model\HealthCheck::offsetSet($offset, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/circleci/project/src/InfluxDB2/Model/HealthCheck.php on line 450
Deprecated: Return type of InfluxDB2\Model\HealthCheck::offsetUnset($offset) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/circleci/project/src/InfluxDB2/Model/HealthCheck.php on line 466
.......

Specifications:

  • Client Version:
  • InfluxDB Version:
  • Platform: PHP-8.1

Wrong documentation for deleting data

Steps to reproduce:
List the minimal actions needed to reproduce the behavior.

  1. Run the code from DeleteDataExample.php

Expected behavior:
Data is deleted.

Actual behavior:
DefaultService doesn't have a deletePost method.

Specifications:

  • Client Version: 2.1.0
  • InfluxDB Version: OSS 2.x (shouldn't be relevant though)
  • Platform: PHP 7.4

I've ended up using DeleteService::postDelete(), but that presented another problem which I'm going to report in a new issue (you have to pass the org and bucket names to the method, otherwise you get a 400 Bad Request that org/orgID/bucket/bucketID are missing).

Edit: Apparently the deletePost() method has disappeared in 2.0.0.

php v7.1 problem

Steps to reproduce:
List the minimal actions needed to reproduce the behavior.

  1. Use Dockerfile to build
  2. there is error in build process:

image

Expected behavior:
Make cointainer

Actual behavior:
Problem with building cointainer. If changed version in Dockerfile to 7.2, there isn't problem.

Specifications:

  • Platform: Docker Compose version v2.2.3

deletePostAsync() creates an error: curl_multi_close(): CURLOPT_FILE resource has gone away, resetting to default

Steps to reproduce:

  1. Call multiple times deletePostAsync()

My code currently is:

$predicate = new DeletePredicateRequest();
$predicate->setStart(new DateTime(date('c', $first_timestamp)));
$predicate->setStop(new DateTime());
$predicate->setPredicate('_measurement="' . $table_name . '" AND sensor_id="' . $sensor_id . '"');
$service->deletePostAsync($predicate, null, $influxdb->options['org'], $influxdb->options['bucket']);

Expected behavior:
No error is thrown.

Actual behavior:
Error from curl:
curl_multi_close(): CURLOPT_FILE resource has gone away, resetting to default

Specifications:

  • Client Version: 1.13.0
  • InfluxDB Version: 2.0.6
  • Platform: Ubuntu 20.04.2 LTS

SECURITY: Missing support for prepared statements

This library must be considered insecure because queries with parameters can only be created by concatenate strings. This is an easy way to introduce security problems through injections

The correct way to do this is to use prepared (like) statements where the query contains placeholders. And the actual parameters are given as key-value store or simple lists. So it is not possible to have incorrectly sanitized parameters which break the query and allow operations not intended by the author

fixDatetimeNanos() doesn't manage date time without nano precision

Steps to reproduce:
List the minimal actions needed to reproduce the behavior.

  1. Create a task using postTasks() through TasksService
  2. The date time returned by postTasksWithHttpInfo() is in second precision

Expected behavior:
Describe what you expected to happen.

When the date time returned by postTasksWithHttpInfo() is in second, fixDatetimeNanos() doesn't crash.

fixDatetimeNanos() shall check the size returned by explode() before accessing the array elements.

static function fixDatetimeNanos(string $date) : string {
    $dateParts = explode(".", $date);
    $nanosZ = $dateParts[1];
    $posZ = strpos($nanosZ, 'Z');
    if (strlen($nanosZ) > 9) {
        $converted = $dateParts[0] . "." . substr($nanosZ, 0, 8);
        if ($posZ > 0) {
            $converted .= "Z";
        }
        return $converted;
    } else {
        return $date;
    }
}

Actual behavior:
Describe What actually happened.

Here is the PHP log for postTasks():

Notice: Undefined offset: 1 in InfluxDB2\ObjectSerializer::fixDatetimeNanos() (line 340 of /var/www/html/vendor/influxdata/influxdb-client-php/src/InfluxDB2/ObjectSerializer.php).
InfluxDB2\ObjectSerializer::fixDatetimeNanos('2021-09-27T07:31:07Z') (Line: 272)
InfluxDB2\ObjectSerializer::deserialize('2021-09-27T07:31:07Z', '\DateTime', NULL) (Line: 323)
InfluxDB2\ObjectSerializer::deserialize(Object, '\InfluxDB2\Model\Task', Array) (Line: 4607)
InfluxDB2\Service\TasksService->postTasksWithHttpInfo(Object, NULL) (Line: 4549)
InfluxDB2\Service\TasksService->postTasks(Object) (Line: 99)

Specifications:

  • Client Version: 2.2.0
  • InfluxDB Version: 2.0.8
  • Platform: Docker (Drupal 9)

Code 500 Influx CLient on VPS

Hello guys i have a problem using the PHP_Client.
On my Xampp Local Machine (Windows 10) everthing works great. But if i put the exact same files on my Linux VPS the Server gives me a 500 Code:

Can someone maybe help me ?

Here ist my Code:

<?php

require __DIR__ . '\..\vendor\autoload.php';


use InfluxDB2\Client;
use InfluxDB2\Model\WritePrecision;
use InfluxDB2\Point;
echo getdata("Jürgen ", "M_AC_Power");
function getdata($kunde,$wert)
{
# You can generate a Token from the "Tokens Tab" in the UI
    $token = '';
    $org = 'JH';
    $bucket = 'PV-Daten';
    $client = new Client([
        "url" => "http://95.111.224.215:8086",
        "token" => $token,
    ]);


    $query = '

from(bucket: "PV-Daten")
  |> range(start: -5s)
  |> filter(fn: (r) => r["name"] == "'.$kunde.'")
  |> filter(fn: (r) => r["_field"] == "'.$wert.'")
  |> last()

  ';

    $tables = $client->createQueryApi()->query($query, $org);


    if (isset($tables[0])) {
        $data = ($tables[0]);
        $data = $data->records;
        $data = ($data[0]);

        return ($data->getValue());
    } else {
        return ("fail");
    }

}

Self signed SSL certificate

Hello,

I am using a self signed SSL certificate, unfortunately that results in an exception
Uncaught GuzzleHttp\Exception\RequestException: cURL error 60: SSL certificate problem: self signed certificate

I could not find an option to disable that verification.

DeleteService::postDelete() requires passing org and bucket explicitly

This is sort-of related to #87, where I discovered that the data-deleting API presented in the README does not exist.

Steps to reproduce:
List the minimal actions needed to reproduce the behavior.

$service = $client->createService(DeleteService::class);
$predicate = new DeletePredicateRequest();
// Prepare the predicate...

// Notice I did not specify `$org` nor `$bucket`.
$service->postDelete($predicate);

Expected behavior:
Perhaps it could take the org and bucket from the Client?

Actual behavior:

In DeleteService.php line 136:

  [400] Client error: `POST http://localhost:8086/api/v2/delete` resulted in a `400 Bad Request` response:
  {"code":"invalid","message":"Please provide either orgID or org"}

Specifications:

  • Client Version: 2.1.0
  • InfluxDB Version: OSS 2.0
  • Platform: PHP 7.4

Add method to retrieve column from table

Proposal:
Add a method to the InfluxTable object to return a column with a specified label, eg: $table->getColumn(string $column_label)

Current behavior:
Currently the table columns are available in a simple array and getting a specific column requires a manual search through the array.

Desired behavior:
Make it easier to find a column of a specific label! This search logic could be encapsulated in a simple helper method on the InfluxTable class. The method would return InfluxColumn|None.

Alternatives considered:
Designing a reusable helper method myself. But it seems sensible to include this in the InfluxTable class if it would be useful for others.

This is my current implementation to pull out the defaultValue of the result column from an InfluxTable:

  // Execute query.
  /** @var \InfluxDB2\FluxTable[] $result */
  $result = $query_api->query($final_query);

  // Collect all the data.
  $all_data = [];
  foreach ($result as $table) {

    // *** Column search right here! ****
    $result_key = array_search('result', array_column($table->columns, 'label'));
    $name = $table->columns[$result_key]->defaultValue;

    foreach ($table->records as $record) {
      $datetime = new \DateTime($record->getTime());
      $formatted = $datetime->format('n/j');
      $all_data[$name][$formatted][$record->getField()] = $record->getValue();
    }
  }

Use case:
My Flux query returns two tables, each identified with a different name via yield(name: "table_name"). When parsing the result of this query I need to know the table name which is stored in the table's result column.

Would you be open to adding methods to the InfluxTable class? I noticed that the PHP implementation is identical to the Python implementation of the InfluxTable, and am unsure if these all need to be kept in sync. I hope that is not the case, but alas. It's my understanding that the API services are auto-generated from the openapi spec, but these other classes are not?

Alternatively, perhaps there is a better way to accomplish what I am trying to do, in which case I would love to hear any suggestions :-)

memory leak when repeatedly connecting while using HTTPS

Steps to reproduce:

<?php
require_once('vendor/autoload.php');

use InfluxDB2\Client;
use InfluxDB2\Model\WritePrecision;
use InfluxDB2\Point;

$token = 'token';
$org = 'Home';
$bucket = 'default';

while (1) {
  $client = new Client([
    "url" => "https://influx:8086",
    "token" => $token,
  ]);


  $dataArray = ['name' => 'test',
    'fields' => array('xxx' => 123),
  ];

  $writeApi = $client->createWriteApi();
  $writeApi->write($dataArray, WritePrecision::MS, $bucket, $org);

  $writeApi->close();
  unset($writeApi);
  unset($dataArray);
  $client->close();
  unset($client);
}

Expected behavior:
Memory usage doesn't increase that heavily.

Actual behavior:
Memory usage does increase heavily. I dumped the memory, it seems to be related to CA certificates. I was able to find thousands of instances of each CA certificate in php memory.

Also I get this error after 10-20 seconds:

PHP Fatal error:  Uncaught Http\Client\Exception\RequestException: error setting certificate file: /etc/ssl/certs/ca-certificates.crt in /home/kveri/goodwe/vendor/php-http/curl-client/src/Client.php:166
Stack trace:
#0 /home/kveri/goodwe/vendor/php-http/client-common/src/PluginClient.php(81): Http\Client\Curl\Client->sendRequest()
#1 /home/kveri/goodwe/vendor/php-http/client-common/src/Plugin/RedirectPlugin.php(175): Http\Client\Common\PluginClient->Http\Client\Common\{closure}()
#2 /home/kveri/goodwe/vendor/php-http/client-common/src/PluginChain.php(46): Http\Client\Common\Plugin\RedirectPlugin->handleRequest()
#3 /home/kveri/goodwe/vendor/php-http/client-common/src/Plugin/HeaderDefaultsPlugin.php(44): Http\Client\Common\PluginChain->Http\Client\Common\{closure}()
#4 /home/kveri/goodwe/vendor/php-http/client-common/src/PluginChain.php(46): Http\Client\Common\Plugin\HeaderDefaultsPlugin->handleRequest()
#5 /home/kveri/goodwe/vendor/php-http/client-common/src/PluginChain.php(61): Http\Client\Common\PluginChain->Http\Client\Common\{closure}()
#6 /home/kveri/goodwe/vendor/php-http/client-common/src/PluginClient.php(87): Http\Client\Common\PluginChain->__invoke()
#7 /home/kveri/goodwe/vendor/influxdata/influxdb-client-php/src/InfluxDB2/DefaultApi.php(151): Http\Client\Common\PluginClient->sendRequest()
#8 /home/kveri/goodwe/vendor/influxdata/influxdb-client-php/src/InfluxDB2/DefaultApi.php(214): InfluxDB2\DefaultApi->sendRequest()
#9 /home/kveri/goodwe/vendor/influxdata/influxdb-client-php/src/InfluxDB2/DefaultApi.php(79): InfluxDB2\DefaultApi->request()
#10 /home/kveri/goodwe/vendor/influxdata/influxdb-client-php/src/InfluxDB2/WriteApi.php(145): InfluxDB2\DefaultApi->post()
#11 [internal function]: InfluxDB2\WriteApi->InfluxDB2\{closure}()
#12 /home/kveri/goodwe/vendor/influxdata/influxdb-client-php/src/InfluxDB2/WriteRetry.php(70): call_user_func()
#13 /home/kveri/goodwe/vendor/influxdata/influxdb-client-php/src/InfluxDB2/WriteApi.php(146): InfluxDB2\WriteRetry->retry()
#14 /home/kveri/goodwe/vendor/influxdata/influxdb-client-php/src/InfluxDB2/WriteApi.php(86): InfluxDB2\WriteApi->writeRaw()
#15 /home/kveri/goodwe/xxx.php(24): InfluxDB2\WriteApi->write()
#16 {main}

Next InfluxDB2\ApiException: [0] error setting certificate file: /etc/ssl/certs/ca-certificates.crt in /home/kveri/goodwe/vendor/influxdata/influxdb-client-php/src/InfluxDB2/DefaultApi.php:179
Stack trace:
#0 /home/kveri/goodwe/vendor/influxdata/influxdb-client-php/src/InfluxDB2/DefaultApi.php(214): InfluxDB2\DefaultApi->sendRequest()
#1 /home/kveri/goodwe/vendor/influxdata/influxdb-client-php/src/InfluxDB2/DefaultApi.php(79): InfluxDB2\DefaultApi->request()
#2 /home/kveri/goodwe/vendor/influxdata/influxdb-client-php/src/InfluxDB2/WriteApi.php(145): InfluxDB2\DefaultApi->post()
#3 [internal function]: InfluxDB2\WriteApi->InfluxDB2\{closure}()
#4 /home/kveri/goodwe/vendor/influxdata/influxdb-client-php/src/InfluxDB2/WriteRetry.php(70): call_user_func()
#5 /home/kveri/goodwe/vendor/influxdata/influxdb-client-php/src/InfluxDB2/WriteApi.php(146): InfluxDB2\WriteRetry->retry()
#6 /home/kveri/goodwe/vendor/influxdata/influxdb-client-php/src/InfluxDB2/WriteApi.php(86): InfluxDB2\WriteApi->writeRaw()
#7 /home/kveri/goodwe/xxx.php(24): InfluxDB2\WriteApi->write()
#8 {main}
  thrown in /home/kveri/goodwe/vendor/influxdata/influxdb-client-php/src/InfluxDB2/DefaultApi.php on line 179

Specifications:

  • Client Version: 3.2.0
  • InfluxDB Version: 2.6.0
  • Platform: x86_64 ubuntu

InfluxDB Client for PHP - Not reporting HTTP 429 status

The issue

When using the InfluxDB Client for PHP it doesn't appear to report HTTP 429 status.

Here is an example script WriteAPITest.php

<?PHP

require __DIR__ . '/vendor/autoload.php';

use InfluxDB2\Client;
use InfluxDB2\Model\WritePrecision;;
use InfluxDB2\Point;

$client = new InfluxDB2\Client([
    "url" => 'https://██████.cloud2.influxdata.com',
    "token" => '████████████████████████████████████████████████',
    "bucket" => '██████████',
    "org" => '████████',
    "precision" => InfluxDB2\Model\WritePrecision::NS,
 ]);

$writeApi = $client->createWriteApi();

$point = Point::measurement('mem')
    ->addTag('host', 'host1')
    ->addField('used_percent', 23.43234543)
    ->time(microtime(true));

$writeApi->write($point, WritePrecision::S, $bucket, $org);

$client->close();

However, when the API is called using Curl it reports the HTTP 429 status:

- Trying ██████:443...
- Connected to ██████.cloud2.influxdata.com (██████) port 443 (#0)
- ALPN, offering h2
- ALPN, offering http/1.1
- successfully set certificate verify locations:
- CAfile: /etc/ssl/cert.pem
- CApath: none
- (304) (OUT), TLS handshake, Client hello (1):
- (304) (IN), TLS handshake, Server hello (2):
- (304) (IN), TLS handshake, Unknown (8):
- (304) (IN), TLS handshake, Certificate (11):
- (304) (IN), TLS handshake, CERT verify (15):
- (304) (IN), TLS handshake, Finished (20):
- (304) (OUT), TLS handshake, Finished (20):
- SSL connection using TLSv1.3 / AEAD-AES256-GCM-SHA384
- ALPN, server accepted to use h2
- Server certificate:
- subject: CN=██████.cloud2.influxdata.com
- start date: May 15 09:39:45 2022 GMT
- expire date: Aug 13 09:39:44 2022 GMT
- issuer: C=US; O=Let's Encrypt; CN=R3
- SSL certificate verify ok.
- Using HTTP2, server supports multiplexing
- Connection state changed (HTTP/2 confirmed)
- Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
- Using Stream ID: 1 (easy handle 0x14a012a00)
  > POST /api/v2/write?org=████████&bucket=██████████&precision=s HTTP/2
  > Host: ███████████████████
  > user-agent: curl/7.79.1
  > accept: _/_
  > authorization: Token ████████████████████████████████████████████████
  > content-length: 39
  > content-type: application/x-www-form-urlencoded
- We are completely uploaded and fine
- Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
  < HTTP/2 429
  HTTP/2 429
  < date: Thu, 30 Jun 2022 13:35:01 GMT
  date: Thu, 30 Jun 2022 13:35:01 GMT
  < content-type: application/json; charset=utf-8
  content-type: application/json; charset=utf-8
  < content-length: 97
  content-length: 97
  < trace-id: 23d958336e1a58a4
  trace-id: 23d958336e1a58a4
  < trace-sampled: false
  trace-sampled: false
  < x-platform-error-code: cardinality limitation
  x-platform-error-code: cardinality limitation
  < strict-transport-security: max-age=15724800; includeSubDomains
  strict-transport-security: max-age=15724800; includeSubDomains
  < x-influxdb-request-id: 1a6a995d434485cc61f28512422810d9
  x-influxdb-request-id: 1a6a995d434485cc61f28512422810d9
  < x-influxdb-build: Cloud
  x-influxdb-build: Cloud

<
- Connection #0 to host ██████.cloud2.influxdata.com left intact
  {"code":"too many requests","message":"org ███████████ has exceeded plan cardinality limit"}

Steps to reproduce:

  1. Amend above example script to use the necessary credentials and settings (redacted).
  2. Run the script, for example: php WriteAPITest.php

Expected behavior:
I would expect to see the following error the same as the Curl output:
{"code":"too many requests","message":"org ███████████ has exceeded plan cardinality limit"}

Actual behavior:
Nothing is returned.

Specifications:

  • Client Version: 2.8.0
  • InfluxDB Version: InfluxCloud 2.0
  • Platform: Linux

Error on retrieving data!

$query = "from(bucket: \"{$bucket}"\) |> range(start: -1h)";
$tables = $client->createQueryApi()->query($query, $org);

this lines return me error:
AH01071: Got error 'PHP message: PHP Parse error: syntax error, unexpected '"\\) |> range(start: -1h)"' (T_CONSTANT_ENCAPSED_STRING)

How i fix this?

Add support for Guzzle client version 7.x.

Proposal:
"guzzlehttp/guzzle": "^6.2|^7"

Current behavior:
"guzzlehttp/guzzle": "^6.2"

Desired behavior:
Supporting guzzle ^7.0 will allow this library to be used with Laravel 8.

Alternatives considered:
.

Use case:
Supporting guzzle ^7.0 will allow this library to be used with Laravel 8.

$write_api->write is really slow

Hi is there a way for batching writes ?

Currently i have like 5mins aggregated meteics with 180520 lines - json file.
Im parsing it with php file_file_get_contents and then triying to $write_api in foreach loop...

But without write and just echo values it takes ->
real 0m0.658s
user 0m0.413s
sys 0m0.245s

With write_api it takes forever :) like 15 to 30 minutes

Timezone

How do I queries using the local timezone? Is it possible to set in the client or do I do it in the query?

Switch Guzzle dependency to version 7 to prevent deprecation notices thrown in PHP 8.1

Steps to reproduce:
List the minimal actions needed to reproduce the behavior.

  1. Run any method which uses HTTP with PHP 8.1 and notices enabled

Expected behavior:
No notices being thrown, silent execution.

Actual behavior:
Notice thrown:

Deprecated: http_build_query(): Passing null to parameter #2 ($numeric_prefix) of type string is deprecated in /.../vendor/guzzlehttp/guzzle/src/Client.php on line 445

Notes

PHP 8.1 has tightened parameter types. Guzzle version 6 is affected and according to their issues will not fix this deprecation notice, because Guzzle version 7 is the only supported one.

influxdata/influxdb-client-php allows for both version in the composer.json requirements:

"guzzlehttp/guzzle": "^6.2|^7.0.1",

This should be fixed to version 7 only.

Specifications:

  • Client Version: 2.5.0
  • InfluxDB Version: 2.1.1
  • Platform: PHP 8.1 on MacOS 12.1

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.