Skip to content
This repository was archived by the owner on Sep 28, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Python client for `Pilosa <https://www.pilosa.com>`_ high performance distribute
Requirements
------------

- Python 2.6 and higher or Python 3.3 and higher
- Python 2.7 and higher or Python 3.4 and higher.

Install
-------
Expand Down
4 changes: 4 additions & 0 deletions docs/data-model-queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ Index:
* `count(self, row)`
* `set_column_attrs(self, column_id, attrs)`
* `xor(self, *rows)`
* `not_(self, row)`
* `options(self, row_query, column_attrs=False, exclude_columns=False, exclude_row_attrs=False, shards=[])`

Field:

Expand All @@ -127,3 +129,5 @@ Field:
* `min(self, row=None)`
* `max(self, row=None)`
* `setvalue(self, column_id, value)`
* `store(self, row_query, row)`
* `clear_row(self, row)`
18 changes: 14 additions & 4 deletions docs/imports.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Importing Data

If you have large amounts of data, it is more efficient to import it to Pilosa instead of several `Set` queries.
If you have large amounts of data, it is more efficient to import it to Pilosa instead of several `Set` or `Clear` queries.

`pilosa.imports` module defines several format functions. Depending on the data, the following format is expected:
* `row_id_column_id`: `ROW_ID,COLUMN_ID`
Expand All @@ -17,10 +17,10 @@ ROW_ID,COLUMN_ID,TIMESTAMP
Note that, each line corresponds to a single bit and the lines end with a new line (`\n` or `\r\n`).
The target index and field must have been created before hand.

Here's some sample code that uses `row_id_column_id` formatter:
Here's some sample code that uses `csv_row_id_column_id` formatter:
```python
import pilosa
from pilosa.imports import csv_column_reader, row_id_column_id
from pilosa.imports import csv_column_reader, csv_row_id_column_id

try:
# python 2.7 and 3
Expand All @@ -35,11 +35,21 @@ text = u"""
3,41,683793385
10,10485760,683793385
"""
reader = csv_column_reader(StringIO(text), row_id_column_id)
reader = csv_column_reader(StringIO(text), csv_row_id_column_id)
client = pilosa.Client()
schema = client.schema()
index = schema.index("sample-index")
field = index.field("sample-field", time_quantum=pilosa.TimeQuantum.YEAR_MONTH_DAY_HOUR)
client.sync_schema(schema)
client.import_field(field, reader)
```

`client.import_field` function imports `Set` bits by default. If you want to import `Clear` bits instead, pass `clear=True`:
```python
client.import_field(field, reader, clear=True)
```

Pilosa supports a fast way of importing bits for row ID/Column ID data by transferring bits from the client to the server by packing bits into a roaring bitmap. You can enable that by passing `fast_import=True`:
```python
client.import_field(field, reader, fast_import=True)
```
2 changes: 1 addition & 1 deletion docs/server-interaction.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ changed = result.changed

## SSL/TLS

Make sure the Pilosa server runs on a TLS address. [How To Set Up a Secure Cluster](https://www.pilosa.com/docs/latest/tutorials/#how-to-set-up-a-secure-cluster) tutorial explains how to do that.
Make sure the Pilosa server runs on a TLS address. [How To Set Up a Secure Cluster](https://www.pilosa.com/docs/latest/tutorials/#setting-up-a-secure-cluster) tutorial explains how to do that.

In order to enable TLS support on the client side, the scheme of the address should be explicitly specified as `https`, e.g.: `https://01.pilosa.local:10501`

Expand Down
77 changes: 56 additions & 21 deletions pilosa/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,16 @@ class Client(object):
# Create a Client instance
client = pilosa.Client()

# Load the schema from the Pilosa server
schema = client.schema()

# Create an Index instance
index = pilosa.Index("repository")
index = schema.Index("repository")

# Create a Field instance
stargazer = index.field("stargazer")

# Execute a query
response = client.query(stargazer.row(5))

# Act on the result
Expand All @@ -88,6 +94,21 @@ class Client(object):
def __init__(self, cluster_or_uri=None, connect_timeout=30000, socket_timeout=300000,
pool_size_per_route=10, pool_size_total=100, retry_count=3,
tls_skip_verify=False, tls_ca_certificate_path=""):
"""Creates a Client.

:param object cluster_or_uri: A ``pilosa.Cluster`` or ``pilosa.URI` instance
:param int connect_timeout: The maximum amount of time in milliseconds to wait for a connection attempt to a server
to succeed
:param int socket_timeout: The maximum amount of time in milliseconds to wait between consecutive
read operations for a response from the server
:param int pool_size_per_route: Number of connections in the pool per server
:param int pool_size_total: Total number of connections in the pool
:param int retry_count: Number of connection trials
:param bool tls_skip_verify: Do not verify the TLS certificate of the server (Not recommended for production)
:param str tls_ca_certificate_path: Server's TLS certificate (Useful when using self-signed certificates)

* See `Pilosa Python Client/Server Interaction <https://github.com/pilosa/python-pilosa/blob/master/docs/server-interaction.md>`_.
"""
if cluster_or_uri is None:
self.cluster = Cluster(URI())
elif isinstance(cluster_or_uri, Cluster):
Expand All @@ -114,7 +135,7 @@ def __init__(self, cluster_or_uri=None, connect_timeout=30000, socket_timeout=30

def query(self, query, column_attrs=False, exclude_columns=False, exclude_attrs=False, shards=None):
"""Runs the given query against the server with the given options.

:param pilosa.PqlQuery query: a PqlQuery object with a non-null index
:param bool column_attrs: Enables returning column data from row queries
:param bool exclude_columns: Disables returning columns from row queries
Expand Down Expand Up @@ -149,7 +170,7 @@ def query(self, query, column_attrs=False, exclude_columns=False, exclude_attrs=

def create_index(self, index):
"""Creates an index on the server using the given Index object.

:param pilosa.Index index:
:raises pilosa.IndexExistsError: if there already is a index with the given name
"""
Expand All @@ -164,7 +185,7 @@ def create_index(self, index):

def delete_index(self, index):
"""Deletes the given index on the server.

:param pilosa.Index index:
:raises pilosa.PilosaError: if the index does not exist
"""
Expand All @@ -173,7 +194,7 @@ def delete_index(self, index):

def create_field(self, field):
"""Creates a field on the server using the given Field object.

:param pilosa.Field field:
:raises pilosa.FieldExistsError: if there already is a field with the given name
"""
Expand All @@ -189,7 +210,7 @@ def create_field(self, field):

def delete_field(self, field):
"""Deletes the given field on the server.

:param pilosa.Field field:
:raises pilosa.PilosaError: if the field does not exist
"""
Expand All @@ -198,7 +219,7 @@ def delete_field(self, field):

def ensure_index(self, index):
"""Creates an index on the server if it does not exist.

:param pilosa.Index index:
"""
try:
Expand All @@ -208,7 +229,7 @@ def ensure_index(self, index):

def ensure_field(self, field):
"""Creates a field on the server if it does not exist.

:param pilosa.Field field:
"""
try:
Expand All @@ -221,6 +242,11 @@ def _read_schema(self):
return json.loads(response.data.decode('utf-8')).get("indexes") or []

def schema(self):
"""Loads the schema from the server.

:return: a Schema instance.
:rtype: pilosa.Schema
"""
schema = Schema()
for index_info in self._read_schema():
index = schema.index(index_info["name"])
Expand All @@ -233,6 +259,12 @@ def schema(self):
return schema

def sync_schema(self, schema):
"""Syncs the given schema with the server.

Loads new indexes/fields from the server and creates indexes/fields not existing on the server. Does not delete remote indexes/fields/

:param pilosa.Schema schema: Local schema to be synced
"""
server_schema = self.schema()

# find out local - remote schema
Expand All @@ -259,9 +291,10 @@ def sync_schema(self, schema):
def import_field(self, field, bit_reader, batch_size=100000, fast_import=False, clear=False):
"""Imports a field using the given bit reader

:param field:
:param bit_reader:
:param batch_size:
:param pilosa.Field field: The field to import into
:param object bit_reader: An iterator that returns a bit on each call
:param int batch_size: Number of bits to read from the bit reader before posting them to the server
:param bool fast_import: Enables fast import for data with columnID/rowID bits
:param clear: clear bits instead of setting them
"""
for shard, columns in batch_columns(bit_reader, batch_size):
Expand All @@ -272,11 +305,11 @@ def http_request(self, method, path, data=None, headers=None):

NOTE: This function is experimental and may be removed in later revisions.

:param method: HTTP method
:param path: Request path
:param data: Request body
:param str method: HTTP method
:param str path: Request path
:param bytes data: Request body
:param headers: Request headers
:return HTTP response:
:return: HTTP response

"""
return self.__http_request(method, path, data=data, headers=headers)
Expand Down Expand Up @@ -447,6 +480,8 @@ class URI:
:param str scheme: is the scheme of the Pilosa Server, such as ``http`` or ``https``
:param str host: is the hostname or IP address of the Pilosa server. IPv6 addresses should be enclosed in brackets, e.g., ``[fe00::0]``.
:param int port: is the port of the Pilosa server

* See `Pilosa Python Client/Server Interaction <https://github.com/pilosa/python-pilosa/blob/master/docs/server-interaction.md>`_.
"""
__PATTERN = re.compile("^(([+a-z]+):\\/\\/)?([0-9a-z.-]+|\\[[:0-9a-fA-F]+\\])?(:([0-9]+))?$")

Expand All @@ -458,7 +493,7 @@ def __init__(self, scheme="http", host="localhost", port=10101):
@classmethod
def address(cls, address):
""" Creates a URI from an address.

:param str address: of the form ``${SCHEME}://${HOST}:{$PORT}``
:return: a Pilosa URI
:type: pilosa.URI
Expand Down Expand Up @@ -509,7 +544,7 @@ def __eq__(self, other):

class Cluster:
"""Contains hosts in a Pilosa cluster.

:param hosts: URIs of hosts. Leaving out hosts creates the default cluster
"""

Expand All @@ -521,7 +556,7 @@ def __init__(self, *hosts):

def add_host(self, uri):
"""Makes a host available.

:param pilosa.URI uri:
"""
with self.__lock:
Expand All @@ -535,7 +570,7 @@ def add_host(self, uri):

def remove_host(self, uri):
"""Makes a host unavailable.

:param pilosa.URI uri:
"""
with self.__lock:
Expand All @@ -545,9 +580,9 @@ def remove_host(self, uri):

def get_host(self):
"""Returns the next host in the cluster.

:return: next host
:rtype: pilosa.URI
:rtype: pilosa.URI
"""
for host, ok in self.hosts:
if not ok:
Expand Down
Loading