Users can connect directly to a MongoDB (suitable for personal use or testing) or connect to an HTTP server with a RESTful API. Amostra provides a Python client for each of these modes that present identical interfaces to the user.

Direct Connection to MongoDB

For direct connection to MongoDB without an HTTP server in the middle, use amostra.mongo_client.Client.

import amostra.mongo_client
client = amostra.mongo_client.Client('mongodb://localhost:27017/amostra_demo')

HTTP Server and Client

On a machine with access to the MongoDB server, start amostra’s HTTP server. There is only one required option: the URI for a MongoDB database. Note that a database name must be included in the URI, as in test_amostra below. Any database name may be used, and it will be created if it does not yet exist.

$ python -m amostra.server --mongo_uri='mongodb://localhost:27017/test_amostra'
[I 190802 15:24:41 server:62] Listening on, path None

See python -m amostra.server --help for more options, including SSL support and a custom base URL.

On any machine that can see that server, use amostra.http_client.Client.

import amostra.mongo_client
client = amostra.http_client.Client('http://localhost:5000')

Using the Client

Create a new Sample.

In [1]: s ='peanut butter')

In [2]: s
Out[2]: Sample(composition='', description='', name='peanut butter', projects=[], tags=[])

In [3]:
Out[3]: 'peanut butter'

Update a trait of the sample.

In [4]: = 'jelly'

In [5]: s
Out[5]: Sample(composition='', description='', name='jelly', projects=[], tags=[])

And again.

In [6]: = 'grape jelly'

In [7]: s
Out[7]: Sample(composition='', description='', name='grape jelly', projects=[], tags=[])

All revisions are automatically persisted in the database and can be accessed. The most recent revisions are given first.

In [8]: list(s.revisions())
[Sample(composition='', description='', name='jelly', projects=[], tags=[]),
 Sample(composition='', description='', name='peanut butter', projects=[], tags=[])]


The Sample has two read-only traits that are used internally:

In [9]: s.uuid
Out[9]: '8ae15640-3fe1-4f3e-834c-3f81e7357acb'

In [10]: s.revision
Out[10]: 2

A Sample is automatically given a uuid when it is created. All revisions of that Sample share that same uuid, but the combination of uuid and revision number is globally unique. The revision is incremented on each update.

Create several more samples, and then search to find one.

In [11]:'coffee')
Out[11]: Sample(composition='', description='', name='coffee', projects=[], tags=[])

In [12]:'tea')
Out[12]: Sample(composition='', description='', name='tea', projects=[], tags=[])

In [13]:'water')
Out[13]: Sample(composition='', description='', name='water', projects=[], tags=[])

In [14]: s = client.samples.find_one({'name': 'tea'})

In [15]: s
Out[15]: Sample(composition='', description='', name='tea', projects=[], tags=[])

Searches that are expected to return potentially more than one result, such as a search for all samples, look like this.

In [16]: results = client.samples.find({})

In [17]: results
Out[17]: <generator object CollectionAccessor.find at 0x7f5f1f5a9048>

The search results are given as a generator, a lazy object that pulls data on demand, to avoid clobbering the system if the set of results is large. To pull just one result we can use next.

In [18]: s = next(results)

In [19]: s
Out[19]: Sample(composition='', description='', name='grape jelly', projects=[], tags=[])

To pull them all we can use list.

In [20]: results = client.samples.find({})

In [21]: list(results)
[Sample(composition='', description='', name='grape jelly', projects=[], tags=[]),
 Sample(composition='', description='', name='coffee', projects=[], tags=[]),
 Sample(composition='', description='', name='tea', projects=[], tags=[]),
 Sample(composition='', description='', name='water', projects=[], tags=[])]

Note that the revisions result above worked in the same way.