# Cloud BigTable
Vamos a usar un dataset público de los buses de Nueva York, donde hay más de 300 buses y 5,800 vehículos siguiendo las rutas.

Este archivo está en formato SequenceFile que es un archivo plano, binario con pares key/value. Es muy usado por MapReduce en Apache Hadoop.

El dataset contiene Nombre Destino, id vehículo, latitud, longitud, tiempo de llegada esperado, tiempo de llegada planificado. Este dataset se construyó con snapshots tomados cada 10 minutos en Junio 2017.

El esquema tendrá como row key: [compañía bus/línea bus/Timestamp redondeado a la hora/ID vehículo]
Tendremos una familia de columnas, por simplicidad.
Mire este link para saber más sobre el diseño de los esquemas [Link](https://cloud.google.com/bigtable/docs/schema-design)
**Crear instancia, tabla y familia**
En un nuevo proyecto, activa el cloud shell y setea las siguientes variables de entorno, reemplazando ID_PROYECTO con el tuyo:
```
INSTANCE_ID="bus-instance"
CLUSTER_ID="bus-cluster"
TABLE_ID="bus-data"
CLUSTER_NUM_NODES=3
CLUSTER_ZONE="us-central1-c"
GOOGLE_CLOUD_PROJECT="ID_PROYECTO"
````
Primero, vamos a habilitar las APIs que usaremos con el siguiente comando (Esto también lo puedes hacer por consola, buscando las dos APIs y poniendo Enable):
```
gcloud services enable bigtable.googleapis.com bigtableadmin.googleapis.com
```
Para crear una instancia usaremos el comando gcloud de la siguiente manera:
```
gcloud bigtable instances create $INSTANCE_ID \
--cluster-config=id=$CLUSTER_ID,zone=$CLUSTER_ZONE,nodes=$CLUSTER_NUM_NODES \
--display-name=$INSTANCE_ID
```
Por consola Web sería, ir en menú de navegación a BigTable y crear instancia, luego:




Cloud BigTable tiene un archivo de configuración, le pondremos la configuración del proyecto e instancia y luego crearemos una tabla y una familia de columnas, llamada cf.
```
echo project = $GOOGLE_CLOUD_PROJECT > ~/.cbtrc
echo instance = $INSTANCE_ID >> ~/.cbtrc
cbt createtable $TABLE_ID
cbt createfamily $TABLE_ID cf
```
Por consola esto sería ir a Tablas -> +CREAR TABLA:

Y luego llenar los datos:

**Importar datos**
El dataset utilizado está en Cloud Storage en gs://cloud-bigtable-public-datasets/bus-data
Para importarlo usaremos un trabajo de Dataflow, por lo que habilitamos la API de la siguiente manera:
```
gcloud services enable dataflow.googleapis.com
````
Además, agregaremos permisos en el servicio IAM para que nuestro proyecto puedo leer datos de Cloud Storage, donde está el dataset. Para esto en el menu de navegador selecciona IAM > IAM.

Edita los permisos de Compute Engine default service account:

Y agrega los roles de Trabajador deDataflow, Usuario de Bigtable, Visualizador de objetos de Storage, Creador de objetos de Storage. Luego presiona Guardar.

El comando que utilizaremos para lanzar el trabajo en Dataflow es:
```
NUM_WORKERS=$(expr 3 \* $CLUSTER_NUM_NODES)
gcloud beta dataflow jobs run import-bus-data-$(date +%s) \
--gcs-location gs://dataflow-templates/latest/GCS_SequenceFile_to_Cloud_Bigtable \
--num-workers=$NUM_WORKERS --max-workers=$NUM_WORKERS --region 'us-central1' \
--parameters bigtableProject=$GOOGLE_CLOUD_PROJECT,bigtableInstanceId=$INSTANCE_ID,bigtableTableId=$TABLE_ID,sourcePattern=gs://cloud-bigtable-public-datasets/bus-data/*
````
Puedes monitorear el avance del trabajo en la consola de Dataflow, seleccionándolo del menu de navegación. Veremos algo como esto:

Este trabajo también lo podemos lanzar por interfaz gráfica, accediendo a Dataflow, + CREAR TRABAJO A PARTIR DE UNA PLANTILLA, llenar los datos y luego EJECUTAR TRABAJO. La plantilla es SequenceFile Files on Cloud Storage to Cloud BigTable.


Si haz hecho los pasos por consola y por interfaz gráfica a la vez, antes de pasar a la siguiente sección borra una de las instancias que hemos creado.
**Consultar los datos**
Vamos a crear una pequeña aplicación en Python para conectarnos a Cloud BigTable y consultar los datos, mediante un lookup de una key de fila y un scan en un rango de key de filas.
Primero crearemos una carpeta y un archivo llamado requirements.txt:
```
mkdir querycbt
cd querycbt
nano requirements.txt
````
El archivo requirements.txt tendrá lo siguiente, que son las librerías que ocupará el programa de python.
```
protobuf==3.20.1
google-cloud==0.34.0
google-cloud-bigtable==2.10.0
google-cloud-core==2.3.0
```
Luego crearemos un Virtual Environment de Python
[Link creación ambientes virtuales](https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/)
```
python3 -m pip install --user virtualenv
python3 -m venv env
source env/bin/activate
python3 -m pip install -r requirements.txt
```
[Link ejemplos leer de BigTable en Python](https://github.com/googleapis/python-bigtable/blob/HEAD/samples/snippets/reads/read_snippets.py)
Luego crearemos el archivo main.py. El archivo tendrá lo siguiente:
```python=
import argparse
import datetime
from google.cloud import bigtable
from google.cloud.bigtable import column_family
from google.cloud.bigtable import row_filters
from google.cloud.bigtable.row_set import RowSet
# Funcion que imprime los resultados de una fila,
# recuerde que una fila en cloud BigTable es tridimensional.
# Recuerde ademas que los datos no estan en texto plano
def print_row(row):
print("Reading data for {}:".format(row.row_key.decode("utf-8")))
for cf, cols in sorted(row.cells.items()):
print("Column Family {}".format(cf))
for col, cells in sorted(cols.items()):
for cell in cells:
labels = (
" [{}]".format(",".join(cell.labels)) if len(cell.labels) else ""
)
print(
"\t{}: {} @{}{}".format(
col.decode("utf-8"),
cell.value.decode("utf-8"),
cell.timestamp,
labels,
)
)
print("")
# En esta función esta la conexion y las consultas
def main(project_id, instance_id, table_id):
# Conexion a la base de datos
client = bigtable.Client(project=project_id, admin=True)
# Obtencion de instancia y tabla
instance = client.instance(instance_id)
table = instance.table(table_id)
# Lookup: el lookup busca la fila asociada a una clave de fila
row_key = "MTA/M86-SBS/1496275200000/NYCT_5824"
# Este comando lee la fila completa que coincida con row_key
row1= table.read_row(row_key.encode("utf-8"))
#Este comando aplica filtros para mejorar rendimiento
# No lee la fila completa sino solo lasa columnas indicadas
row2 = table.read_row(row_key.encode("utf-8"), filter_ = row_filters.ColumnQualifierRegexFilter("VehicleLocation.*".encode("utf-8")))
print("Lookup 1")
print_row(row1)
print("\nLookup 2\n")
print_row(row2)
# Scan: entrega todas lasa filas desde la fila de inicio
# hasta la del final
print("\nScan\n")
key_start = "MTA/M86-SBS/1496275200000".encode("utf-8")
key_end = "MTA/M86-SBS/1496300000000".encode("utf-8")
# El scan no devuelve una fila, sino un RowSet
row_set = RowSet()
row_set.add_row_range_from_keys(start_key=key_start, end_key=key_end)
rows =table.read_rows(row_set=row_set)
# Imprimo todas las filas
for row in rows:
print_row(row)
# MAIN Recibe y parsea Argumentos
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description=__doc__, formatter_class=argparse.ArgumentDefaultsHelpFormatter
)
parser.add_argument("project_id", help="Your Cloud Platform project ID.")
parser.add_argument(
"instance_id", help="ID of the Cloud Bigtable instance to connect to."
)
parser.add_argument(
"table_id", help="Table to create and destroy."
)
args = parser.parse_args()
main(args.project_id, args.instance_id, args.table_id)
```
Vamos a ir haciendo las consultas una a una, comentando las otras para tener orden en los resultados. Ejecuto el programa en python con:
```
python3 main.py GOOGLE_PROJECT_ID bus-instance bus-data
```
Los resultados serán de la siguiente forma:

En el siguiente link pueden encontrar la documentación completa de Cloud BigTable para seguir experimentando [Link](https://cloud.google.com/bigtable/docs/samples/).
**Recuerda borrar la instancia de Bigtable y apagar el proyecto.**
```
gcloud bigtable instances delete $INSTANCE_ID
```