Package: azure-kusto-ingestion-tools
Kusto Ingestion Tools (Kit)azure-kusto-ingestion-toolsa simple toolkit to help with ingestions, avialble herePurposeMake ingestion simpler (at least for common cases).
After creating an ADX (Kusto) cluster via Azure portal, we want to explore / visualize some data.
When evaluating data stores / tools we usually want to  just POC capabilities and move fast.That is what this project was created for.
It contains features to support:Data sourceschemainference (csv / kusto/ ...)Commoningestionscenarios (from file /entire folder / ...)Other helpful utilities (kql generator, ...)TOCConceptInstallUsageExamplesConceptGiven a data source, usually the workflow would consist of:Describing the data source.Preparing the target data store (in our case, Kusto)Mapping Source to TargetLoading the dataOptional: Automation / Moving to ProductionInstallMinimum RequirementsPython 3.7+See setup.py for dependenciesPipTo install via the Python Package Index (PyPI), type:pip install azure-kusto-ingestion-toolsThis will expose a new cli commands :kit --helpUsageBasickit ingest -d /path/to/data/imdb -h mycluster.westusThe following command will try to ingest all files in/path/to/data/imdb(non-recursive) using type inference.<!>NOTICE<!>: without providing any other arguments, this command is extremelyopinionated, and will assume the following:OptionsAuthEvery command that needs to authenticate against kusto, will require authentication arguemnts.By default, will try to grab token fromazure cliOther options are:App:kit [command] -app {app_id}:{app_secret}User:kit [command] -user {user_id}:{password}Namingdatabasewill be set to is the dir in which data sits, so/path/to/data/imdbwill look for, and create if missing, a database namedimdb.If more control is need, try--databasetablesare actual file names, so/path/to/data/imdb/aka_name.csvwill look for, and create if missing, a table namedaka_name.This can be tweaked by making sure data is split into folder, where any folder would be a table.This recursive mode assumes that the table structure is the same for all files.FilesDatabase Schema fileThis is a simple way to describe a database.This can be used to describe a db schema using plain JSON format, and thus easily copy entire database schemas.{"name":"imdb","tables":[{"name":"aka_name","columns":[{"dtype":"int","name":"id","index":0},{"dtype":"int","name":"person_id","index":1},{"dtype":"string","name":"name","index":2},{"dtype":"string","name":"imdb_index","index":3},{"dtype":"string","name":"name_pcode_cf","index":4},{"dtype":"string","name":"name_pcode_nf","index":5},{"dtype":"string","name":"surname_pcode","index":6},{"dtype":"string","name":"md5sum","index":7}]},...]}From an existing clusterkit schema create -h 'https://mycluster.kusto.windows.net' -db imdb > imdb_schema.jsonFrom an sql filekit schema create -sql imdb.sql > schema.jsonFrom a folder with raw datakit schema create -d path/to/dir > schema.jsonMore to come...Manifest fileA file to describe the details of an ingestion which can be run later{"databases":["same as schema.json"],"mappings":[{"name":"aka_name_from_csv","columns":[{"source":{"index":0,"data_type":"str"},"target":{"index":0,"data_type":"str"}}]}],"operations":[{"database":"imdb","sources":[{"files":["1.csv","...","99.csv"],"mapping":"aka_name_from_csv"}],"target":["aka_name"]}]}ExamplesExample 1 : Ingest IMDB Dataset , CSV files (used in Join Order Benchmark)One useful scenario would be to load an entire existing dataset into Kusto.Let's take for example theJoin Order Benchmarkused in the paperHow good are query optimizers really?.1. Copy files to local dir:Downloadfrom Azure Storagewget https://imdb2013dataset.blob.core.windows.net/data/imdb.tgz --no-check-certificateorcurl https://imdb2013dataset.blob.core.windows.net/data/imdb.tgz --output imdb.tgzOriginal Filesare available, but are malformed (don't conform tohttps://tools.ietf.org/html/rfc4180).One can fix them using tools likexsv,but this is we shall leave error handling for another section.2. Extract files:tar -xvzf imdb.tgz3. Download SQL Create commands:wget https://raw.githubusercontent.com/gregrahn/join-order-benchmark/master/schema.sql -O imdb.sql --no-check-certificateorcurl https://raw.githubusercontent.com/gregrahn/join-order-benchmark/master/schema.sql --output imdb.sql4. Create schema from sql statementkit schema create -sql schema.sql > imdb_schema.json5. Apply schema on clusterAssuming we already have a cluster, and we are signed in using az cli, we can just apply the schema on a database we choose:kit schema apply -f imdb_schema.json -h mycluster.westus -db imdb6. Ingest data from local fileskit ingest -d . --pattern "*.csv" -h mycluster.westus -db imdb7. QueryUsing the Azure portal, you can now easily login and query your data.You can always make sure that data was loaded by comparing the source line count with target column count:xsv count aka_name.csv- should show 901343 rowsorwc -l aka_name.csv- should show 901343 rowsQuery from kusto should show the same:kit count --table aka_name -h mycluster.westus -db imdb- should show 901343And take a peek at the data:kit peek --table aka_name -n 10 -h mycluster.westus -db imdbExample 2 : Ingest Kaggle ML Datasets, CSV and JSONKaggale has tons of interesting dataset for ML/AI purposes.Let's try and ingest some:https://www.kaggle.com/mlg-ulb/creditcardfraud/https://www.kaggle.com/START-UMD/gtd/Uploaded to our azure storage for convenience:wget https://imdb2013dataset.blob.core.windows.net/data/creditcard.csv.gz --no-check-certificate  
wget https://imdb2013dataset.blob.core.windows.net/data/globalterrorism.csv.gz --no-check-certificate
wget https://imdb2013dataset.blob.core.windows.net/data/arxivData.csv.gz --no-check-certificateorcurl https://imdb2013dataset.blob.core.windows.net/data/creditcard.csv.gz --output creditcard.csv.gz
curl https://imdb2013dataset.blob.core.windows.net/data/globalterrorism.csv.gz --output globalterrorism.csv.gz   
curl https://imdb2013dataset.blob.core.windows.net/data/arxivData.json.gz --output arxivData.json.gzOnce downloaded and unzipped, same idea, only this time files contain headers, so schema is infered:kit ingest -d . -h mycluster.westus -db ml --headersExample 3 : Complex nested JSON mappingsLet's look at a more advance use case:wget https://imdb2013dataset.blob.core.windows.net/data/demo.json --no-check-certificateorcurl https://imdb2013dataset.blob.core.windows.net/data/demo.json --output demo.jsonSay our data is a json lines files, where each item looks like:{"header":{"time":"24-Aug-18 09:42:15", "id":"0944f542-a637-411b-94dd-8874992d6ebc", "api_version":"v2"}, "payload":{"data":"NEEUGQSPIPKDPQPIVFE", "user":"owild@fabrikam.com"}}It seems that we have a nested object.
Because we are not sure what will happen, let's dry run.
Let's try and--dryrun an ingestion with--object-depth 2.kit ingest -f demo.json --object-depth 2 -h mycluster.westus -db ml --dry > manifest.jsonThis produces the followingmanifest.jsonwhich contains the operations to be executed.{"databases":[{"name":"ml","tables":[]}],"mappings":[{"name":"demo_from_json","columns":[{"source":{"dtype":"string","name":"header.time","index":null},"target":{"dtype":"string","name":"header.time","index":null}},{"source":{"dtype":"string","name":"header.id","index":null},"target":{"dtype":"string","name":"header.id","index":null}},{"source":{"dtype":"string","name":"header.api_version","index":null},"target":{"dtype":"string","name":"header.api_version","index":null}},{"source":{"dtype":"string","name":"payload.data","index":null},"target":{"dtype":"string","name":"payload.data","index":null}},{"source":{"dtype":"string","name":"payload.user","index":null},"target":{"dtype":"string","name":"payload.user","index":null}}]}],"operations":[{"database":"ml","sources":[{"files":["C:\\Users\\dadubovs\\temp\\ml_datasets\\demo.json"],"mapping":"demo_from_json","options":{},"data_format":"json"}],"target":"demo"}]}Now, let's say that we don't need theidfield, we can edit the mapping and save it.If we are still unsure, and want to get a better understanding are the commands that will be created, we can inspect the kqlkit kql -m manifest.jsonWhich should output something like:// Table Creation Commands:
.create table demo (['header.time']:string,['header.api_version']:string,['payload.data']:string,['payload.user']:string)

// Ingestion Mapping Creation Commands:
.create table demo ingestion json mapping "demo_from_json" '[{"column":"header.time","path":"$.header.time","datatype":"string"},{"column":"header.api_version","path":"$.header.api_version","datatype":"string"},{"column":"payload.data","path":"$.payload.data","datatype":"string"},{"column":"payload.user","path":"$.payload.user","datatype":"string"}]'Once we are ready, we can resume our ingestion based on the manifestkit ingest -m manifest.json -h mycluster.westus