Tutorial¶
This tutorial will introduce you to the concepts and features of the HumbleDB
micro-ODM and covers basic and advanced usage. It will teach you how to install
HumbleDB, how to create Document
and
Mongo
subclasses that fit your needs, and the HumbleDB
way of manipulating documents. The API Documentation covers more details, but has less
explanation.
Installation¶
HumbleDB requires Pymongo (>=2.0.1
), Pytool (>=3.0.1
) and
Pyconfig. These are installed for you automatically when you install HumbleDB
via pip or easy_install.
$ pip install -U humbledb # preferred
$ easy_install -U humbledb # without pip
To get the latest and greatest development version of HumbleDB, clone the code via github and install:
$ git clone http://github.com/shakefu/humbledb.git
$ cd humbledb
$ python setup.py install
Quickstart: The humblest document¶
This tutorial assumes you already have HumbleDB installed
and working. Let’s start with a very basic Document
:
from humbledb import Document
class HumbleDoc(Document):
config_database = 'humble'
config_collection = 'example'
description = 'd'
value = 'v'
The config_database
and config_collection
attributes are required to tell the HumbleDoc class which database and
collection that it lives in.
HumbleDB’s basic attribute access works by looking for class attributes whose
values are str
or unicode
objects, and mapping those
values to the attribute names given.
We see above that the description
attribute is mapped to the 'd'
key,
and the value
attribute is mapped to the 'v'
key. These keys can bet
set by assigning their attributes:
doc = HumbleDoc()
doc.description = "The humblest document"
doc.value = 3.14
In addition to any keys you specify, every Document is given a _id
attribute which maps to the '_id'
key.
Accessing Keys and Values
When you access a mapped key on your document class, it returns the key for you, so you can reference your short key names more readably:
>>> HumbleDoc.description
'd'
>>> HumbleDoc.value
'v'
When querying or setting keys you should use these attributes, rather than the short key names, for more understandable code:
HumbleDoc.find({HumbleDoc._id: 'example'})
HumbleDoc.update({HumbleDoc._id: 'example'},
{'$set': {HumbleDoc.value: 4}})
HumbleDoc.find_one({HumbleDoc.value: 4})
When these same attributes are accessed on a document instance, they return the current value of that key:
>>> with Mongo:
... doc = HumbleDoc.find_one()
...
>>> doc.description
u'A humble example'
>>> doc.value
3.14159265358979
Connecting to MongoDB
The Mongo
class is a context manager which takes care
of establishing a pymongo.connection.Connection
instance for you.
By default, the Mongo class will connect to 'localhost'
, port 27017
(see Configuring Connections if you need different settings).
When doing any operation that hits the database, you always need to use the
with
statement with Mongo
(or a
subclass):
with Mongo:
HumbleDoc.insert(doc)
docs = HumbleDoc.find({HumbleDoc.value: {'$gt': 3}})
The Mongo context ensures any operations you do are within a single request (for consistency) and that the socket is released back to the connection pool as soon as possible (for concurrency).
Working with a Collection
For your convenience, all of the pymongo.collection.Collection
methods are mapped onto your document class (but not onto class instances).
Because these methods imply using the MongoDB connection, they’re only
available within a Mongo
context.
Within a Mongo
context, all the
Collection
methods are available on your
document class:
with Mongo:
doc = HumbleDoc.find_one()
Without a context, a RuntimeError
is raised:
>>> HumbleDoc.insert
Traceback (most recent call last):
...
RuntimeError: 'collection' not available without context
Document
instances do not have collection methods
and will raise a AttributeError
:
>>> doc.insert
Traceback (most recent call last):
...
AttributeError: 'HumbleDoc' object has no attribute 'insert'
Working with Documents¶
Document subclasses provide a clean attribute oriented interface to your
collection’s documents, but at their heart, they’re just dictionaries. The only
required attributes on a document are
config_database
, and
config_collection
.
Example: Documents are dictionaries
from humbledb import Document
class Basic(Document):
# These are required
config_database = 'humble'
config_collection = 'basic'
# Documents are dictionaries
doc = Basic()
doc['my-key'] = 'Hello'
# Documents can be initialized with dictionaries
doc = Basic({'key': 'value'})
doc['key'] == 'value'
# You also can query using arbitrary keys
with Mongo:
docs = Basic.find({'my-key': {'$exists': True}})
Attribute Mapping¶
Attributes are created by assigning string key in your class definition to
attribute names. These attributes are mapped to the dictionary keys internally.
In addition to any attributes you specify, a _id
attribute is always
available.
Attributes names with a leading underscore (_
) are not mapped to keys.
When an mapped attribute is accessed from the class, the short key is returned, and when accessed from an instance, that instance’s value for that key is returned.
If a document doesn’t have a value set for a mapped attribute, {}
is
returned (rather than raising an AttributeError
), so you can easily
check whether an attribute exists. This also allows you to create embedded
documents whose keys are not mapped.
When a document is inserted, its _id
attribute is set to the created
ObjectId
, if it wasn’t already set.
Changed in version 3.0: Unset attributes on a Document return {}
rather than None
Example: Attribute mapping
class MyDoc(Document):
config_database = 'humble'
config_collection = 'mydoc'
# Keys are mapped to attributes
my_attribute = 'my_key'
# Private names are ignored
_my_str = 'private'
# Non string values are ignored
an_int = 1
doc = MyDoc()
# Unset attributes return {}, which evaluates to False
if not doc.my_attribute:
# Attribute assignment works like normal
doc.my_attribute = 'Hello'
# Attribute deletion works like normal too
del doc.my_attribute
# You can explicitly check if you expect to assign values which also
# evaluate to False
if doc.my_attribute == {}:
doc.my_attribute = 'Hello World'
# Class attributes return the key
MyDoc.my_attribute # 'my_key'
# Instance attributes return the value
doc.my_attribute # 'Hello World'
# Private names aren't mapped
doc._my_str # 'private'
# Neither are non-string values
doc.an_int # 1
doc._id # {}
if not doc._id:
with Mongo:
MyDoc.insert(doc)
doc._id # ObjectId(...)
Giving Documents Default Values¶
Sometimes it’s useful to allow a document to provide a default value for a missing key. HumbleDB provides a convenient syntax for specifying both persisted and unpersisted default values.
New in version 5.2.0.
Both persisted and unpersisted values are declared by assigning a 2-tuple to an attribute where the first item is a string document key, and the second item is a default value.
If the second item is a callable, then that indicates that the value should be persisted. It will be called once on first access or first save and persisted with the document.
By convention, HumbleDB uses the no-parentheses form of tuple declaration when declaring default values.
Lastly, when serializing to JSON via the
for_json()
method, the default values will be
inserted into the resulting JSON.
attr = 'key', value
- Ifvalue
is not callable, provide an unpersisted default value, which is available through attribute access only, and not part of the document.Examples:
class MyDoc(Document): config_database = 'humble' config_collection = 'docs' attr = 'a' # Regular mapping # Any non-callable as the second item is used as a default value truth = 't', False number = 'u', 1 name = 'n', "humble" # Any expression may be used to declare an unpersisted default some_value = 'v', func() # Where func() returns a non-callable one_day = 'd', 60*60*24
attr = 'key', default
- Wheredefault
is a callable, provide a persisted default value, which will become part of the document the first time it’s accessed via attribute or inserted or saved. Updates do not trigger default values.Examples:
class MyDoc(Document): config_database = 'humble' config_collection = 'docs' attr = 'a' # Regular mapping # A callable makes this a persisted default - as soon as it's # accessed, it is assigned to the doc, or when saved or inserted # Here, my_func() will be called without arguments to provide a # default value which will be saved with the document my_value = 'v', my_func # Here, uuid.uuid4() will be called - note the lack of parens, we're # not calling it (which would return a value) - we're providing the # function itself uid = 'u', uuid.uuid4 # Here, the datetime.now() function will be called on save which is # a convenient way to provide a creation timestamp created = 'c', datetime.now
Example: Declaring default values for keys
class BlogPost(Document):
config_database = 'humble'
config_collection = 'posts'
title = 't' # Normal attribute
author = 'a' # Still normal
public = 'p', False # Default value that is not saved automatically
created = 'c', datetime.now # This will be saved to the document
# Create a post
post = BlogPost()
post.title = "A post"
post.author = "HumbleDB"
# The default value is provided on the document object when accessed via an
# attribute
post.public # False
# But it isn't part of the document itself, so dict key access will raise a
# KeyError
post[BlogPost.public] # KeyError
post['p'] # KeyError
# After the first access, the persisted default is called and the returned
# value is stored in the document and will be consistent from then on
post.created # datetime(2014, 2, 14, 6, 59, 0)
# Saving the post would also call the persisted default and store the value
# Save the post and reload it
with Mongo:
_id = BlogPost.save(post)
post = BlogPost.find_one(_id)
# The unpersisted default value is not stored with the document
post # BlogPost({'_id': ObjectId(), 't': "A post", 'a': "HumbleDB",
# 'c': datetime(2014, 2, 14, 6, 59, 0)})
# But it's still available on the document object
post.public # False
# Once modified, the value will be saved and retrieved like normal
post.public = True
with Mongo:
BlogPost.save(post)
post # BlogPost({'_id': ObjectId(), 't': "A post", 'a': "HumbleDB",
# 'p': True, 'c': datetime(2014, 2, 14, 6, 59, 0)})
Introspecting Documents¶
Sometimes it’s useful to be able to introspect a document schema to find out
what attributes or keys are mapped. To do this, HumbleDB provides two methods,
mapped_keys()
and
mapped_attributes()
. These methods will
return all the mapped dictionary keys and document attributes, respectively,
excluding the _id
key/attribute.
Example: Introspecting documents
class MyDoc(Document):
config_database = 'humble'
config_collection = 'mydoc'
my_attr = 'k'
other_attr = 'o'
MyDoc.mapped_keys() # ['k', 'o']
MyDoc.mapped_attributes() # ['my_attr', 'other_attr']
# Mapping an arbitrary dict, while restricting keys
some_dict = {'spam': 'ham', 'k': True, 'o': "Hello"}
# Create an empty doc
doc = MyDoc()
# Iterate over the mapped keys, assigning common keys
for key in MyDoc.mapped_keys():
if key in some_dict:
doc[key] = some_dict[key]
Embedding Documents¶
Attribute mapping to embedded documents is done via the Embed
class.
Because a document is also a dictionary, using Embed is totally optional, but
helps keep your code more readable.
An embedded document can be assigned mapped attributes, just like a document.
Mapped embedded document attributes that aren’t assigned return an empty dictionary when accessed.
When accessed via the class, an embedded document attribute returns the full dot-notation key name. If you want just the key name of the attribute, it is available as the attribute key.
Example: Embedded documents and nested documents
from humbledb import Document, Embed
class Example(Document):
config_database = 'humble'
config_collection = 'embed'
# Any mapped attribute can be used as an embedded document
my_attribute = 'my_attr'
# This maps embedded_doc to embed_key
embedded_doc = Embed('embed_key')
# This maps embedded_doc.my_attribute to embed_key.my_key
embedded_doc.my_attribute = 'my_key'
# This is a nested embedded document
embedded_doc.nested_doc = Embed('nested_key')
embedded_doc.nested_doc.value = 'val'
empty_doc = Example()
# Empty or missing embedded documents are returned as an empty dictionary
empty_doc.embedded_doc # {}
# Missing attributes are returned as {} so you can have unmapped subdocs
empty_doc.embedded_doc.my_attribute # {}
doc = Example()
# You can use attributes as unmapped embedded documents
doc.my_attribute['embedded_key'] = 'Hello'
doc.my_attribute # {'embedded_key': 'Hello'}
doc # {'my_attr': {'embedded_key': 'Hello'}}
# Attribute assignment works like normal
if not doc.embedded_doc:
doc.embedded_doc.my_attribute = "A Fish"
doc.embedded_doc.nested_doc.value = 42
# Class attributes return the dot-notation key
Example.embedded_doc # 'embed_key'
Example.embedded_doc.my_attribute # 'embed_key.my_key'
Example.embedded_doc.nested_doc # 'embed_key.nested_key'
Example.embedded_doc.nested_doc.value # 'embed_key.nested_key.val'
# The subdocument key is available via .key
Example.embedded_doc.my_attribute.key # 'my_key'
Example.embedded_doc.nested_doc.key # 'nested_key'
Example.embedded_Doc.nested_doc.value.key # 'val'
# Instances return the value
doc.embedded_doc.my_attribute # "A Fish"
doc.embedded_doc.nested_doc.value # 42
doc.embedded_doc.nested_doc # {'nested_key': {'val': 42}}
doc.embedded_doc # {'embed_key': {'my_key': "A Fish",
# 'nested_key': {'val: 42}}}
Example: A BlogPost class with embedded document
from humbledb import Document, Embed
class BlogPost(Document):
config_database = 'humble'
config_collection = 'posts'
meta = Embed('m')
meta.timestamp = 'ts'
meta.author = 'a'
meta.tags = 't'
title = 't'
preview = 'p'
content = 'c'
As you can see using embedded documents here lets you keep your keys short, and your code clear and understandable.
Embedded Document Lists¶
Sometimes your documents will have list of embedded documents in them, and for
your convenience, HumbleDB allows you to use attribute mapping on those
documents as well. Because attribute mapping is not just useful for retrieval,
but also for creation, HumbleDB provides a special new()
method for
creating new embedded documents within lists.
HumbleDB doesn’t treat embedded lists specially unless they actually have a list value. This is because HumbleDB’s philosophy is to not validate data types based on their keys, just like MongoDB’s.
You can also embed lists within documents within lists, etc., to your heart’s delight and mapped attributes will work as you would expect.
Creating embedded documents within a list
The easiest way to create embedded documents within a list is to use the
new()
helper. Of course, you can always do it “manually” by building and
appending dictionaries, but who wants to do that?
# An example student roster
class Roster(Document):
config_database = 'humble'
config_collection = 'lists'
# Embedded lists are declared the same way as embedded documents
students = Embed('s')
students.name = 'n'
students.grade = 'g'
# Create a new roster instance
roster = Roster()
# You must assign a list to it first
roster.students = []
# You can use the new() convenience method which creates and appends an
# empty embedded document to your list
student = roster.students.new()
student.name = "Lisa Simpson"
student.grade = "A"
# Note: We don't have to add it to our list - it is already appended
# roster.students.append(student) # DON'T DO THIS: it will create duplicates
# Everything else works the same
with Mongo:
Roster.insert(roster)
Retrieving embedded list data
Upon retrieval, HumbleDB knows if an Embed
attribute has a list assigned to it, and lets you use your mapped attributes
normally.
# Contining our example from above
with Mongo:
roster = Roster.find_one()
# You can iterate over it like any list
for student in roster.students:
# You get attributes mapped to your embedded document values
print student.name, student.grade
# You can modify attributes of the embedded list items
student.grade = "A" # Everybody gets As!
# Once modified, you can save your changes
with Mongo:
Roster.save(roster)
Querying within lists
Because HumbleDB gives you dot-notation keys for embedded attribute mappings, querying for list values is straight-forward.
# Find a roster containing a given student
with Mongo:
roster = Roster.find_one({Roster.students.name: "Bart Simpson"})
# Find all rosters where at least one student has an F
with Mongo:
rosters = Roster.find({Roster.students.grade: "F"})
Querying, Updating and Deleting¶
All the standard pymongo find/update/remove, etc., are mapped onto Document
subclasses, however these are only available within a Mongo
context.
If you attempt to access the collection
attribute of a document outside a Mongo
context, a RuntimeError
will be raised.
Document methods
For your convenience, all the methods of the Document.collection
are
mapped onto the document class.
Here’s a listing of all those methods as of pymongo
2.4:
aggregate() |
find_and_modify() |
options() |
count() |
find_one() |
reindex() |
create_index() |
get_lasterror_options() |
remove() |
distinct() |
group() |
rename() |
drop() |
index_information() |
save() |
drop_index() |
inline_map_reduce() |
set_lasterror_options() |
drop_indexes() |
insert() |
unset_lasterror_options() |
ensure_index() |
map_reduce() |
update() |
find() |
Example: A blog post document
This class is used for all the examples in this section.
# A basic blog post class for illustration
class BlogPost(Document):
config_database = 'humble'
config_collection = 'posts'
config_indexes = [Index('meta.url', unique=True)]
meta = Embed('m')
meta.tags = 't'
meta.published = 'p'
meta.url = 's'
author = 'a'
title = 't'
body = 'b'
Best practices
Reference keys via their attributes when building query, update, or removal
dictionaries. For example, use BlogPost.meta.tags
rather than 'm.t'
.
This helps keep your code clean, readable, and avoids typos in string keys.
# GOOD
# Clear, typo-proof and highly readlable
with Mongo:
BlogPost.find({BlogPost.author: 'Humble'}).sort(BlogPost.meta.published)
# BAD
# Hard to read, prone to typos, and obfuscated
with Mongo:
BlogPost.find({'a': 'Humble'}).sort('m.p')
Creating, inserting, and updating documents
If you’re familiar with how Pymongo does inserting and updating, using HumbleDB will be much the same. The main difference is that HumbleDB lets you use attributes to reference the document keys, rather than using string keys.
# Creating a new post
post = BlogPost()
post.meta.tags = ['python', 'humbledb']
post.meta.url = 'using-humbledb'
post.author = "Humble"
post.title = "How to Use HumbleDB"
post.body = "Lorem ipsum, etc."
# Inserting a new post
with Mongo:
post_id = BlogPost.insert(post)
# Updating a post atomically
with Mongo:
BlogPost.update({BlogPost._id: post_id},
{'$set': {BlogPost.meta.published: datetime.now()}})
# Updating a post by retrieval
with Mongo:
post = BlogPost.find_one({BlogPost.meta.url: 'using-humbledb'})
post.meta.published = False
BlogPost.save(post)
Querying for documents
Querying, like inserting and updating, works just like raw Pymongo, but with the convenience and readability of using attributes instead of string keys.
# Get all posts by an author
with Mongo:
posts = BlogPost.find({BlogPost.author: "Humble"})
# Get 10 most recent posts by an author
with Mongo:
posts = BlogPost.find({BlogPost.author: "Humble"})
posts = posts.sort(BlogPost.meta.published, humbledb.DESC)
posts = posts.limit(10)
# Get all unpublished posts
with Mongo:
posts = BlogPost.find({BlogPost.meta.published: {'$exists': False}})
# Get an individual post according to its URL
with Mongo:
post = BlogPost.find_one({BlogPost.meta.url: 'using-humbledb'})
# Unpublish a post and retrieve it
with Mongo:
post = BlogPost.find_and_modify({BlogPost.meta.url: 'using-humbledb'},
{'$unset': {BlogPost.meta.published: 1}}, new=True)
# Find all posts with a Python tag
with Mongo:
posts = BlogPost.find({BlogPost.meta.tags: 'python'})
Removing documents
Removing works just like removing in Pymongo, but with the convenience of
using attributes rather than string keys. It’s strongly recommended that you
only use the _id
key when removing items to prevent accidental removal.
# Remove an individual post
with Mongo:
BlogPost.remove({BlogPost._id: post_id})
Specifying Indexes¶
Indexes are specified using the
config_indexes
attribute. This attribute
should be a list of attribute names to index. These names will be automatically
mapped to their key names when the index call is made. More complicated indexes
can be made using the Index
class, which takes the
same areguments as ensure_index()
.
HumbleDB uses an ensure_index()
call with
a default cache_for=
of 24 hours, and background=True
. This will be
called before any find()
,
find_one()
, or
find_and_modify()
operation.
New in version 2.2: Index
class for index creation customization.
New in version 3.0: Support for compound indexes.
Example: Indexes on a BlogPost class
class BlogPost(Document):
config_database = 'humble'
config_collection = 'posts'
config_indexes = [
# Basic indexes
'author',
'timestamp',
# Indexes with additional creation arguments
Index('tags', sparse=True),
# Directional indexes with additional creation arguments
Index([('slug', humbledb.DESC)], unique=True),
# Embedded indexes
Index('meta.url')
# Compound indexes
Index([('author', humbledb.ASC), ('timestamp', humbledb.DESC)]),
]
timestamp = 'ts'
author = 'a'
tags = 'g'
title = 't'
slug = 's'
content = 'c'
meta = Embed('m')
meta.url = 'u'
Configuring Connections¶
The Mongo
class provides a default connection for you,
but what do you do if you need to connect to a different host, port, or a
replica set? You can subclass Mongo to change your settings to whatever you
need.
Mongo subclasses are used as context managers, just like Mongo. Different
Mongo subclasses can be nested within one another, should your code require it,
however you cannot nest a connection within itself (this will raise a
RuntimeError
).
Connection Settings
- config_host (
str
) - Hostname to connect to. - config_port (
int
) - Port to connect to. - config_replica (
str
, optional) - Name of the replica set. - config_ssl (
bool
, optional) - If True, use SSL for this connection.
If config_replica
is present on the class, then HumbleDB will automatically
use a ReplicaSetConnection
for you. (Requires
pymongo >= 2.1
.)
Global Connection Settings
These settings are available globally through Pyconfig configuration keys. Use
either Pyconfig.set()
(i.e. pyconfig.set('humbledb.connection_pool',
20)
or create a Pyconfig plugin to change these.
- humbledb.connection_pool (
int
, default:300
) - Size of the connection pool to use. - humbledb.allow_explicit_request (
bool
, default:True
) - Whether or notstart()
can be used to define a request, without using Mongo as a context manager. - humbledb.auto_start_request (
bool
, default:True
) - Whether to useauto_start_request
with theConnection
instance. - humbledb.use_greenlets (
bool
, default:False
) - Whether to useuse_greenlets
with theConnection
instance. (This is only needed if you intend on using threading and greenlets at the same time.) - humbledb.ssl (
bool
, default:False
) - Whether to usessl
with theConnection
instance. The mongod or mongos you are connecting to must have SSL enabled.
More configuration settings are going to be added in the near future, so you
can customize your Connection
to completely suit
your needs.
Example: Using different connection settings
from humbledb import Mongo
# A basic class which connects to a different host and port
class MyDB(Mongo):
config_host = 'mydb.example.com'
config_port = 3001
# A replica set class which will use a ReplicaSetConnection
class MyReplica(Mongo):
config_host = 'replica.example.com'
config_port = 3002
config_replica = 'RS1'
# Use your custom subclasses as context managers
with MyDB:
docs = MyDoc.find({MyDoc.value: {'$gt': 3}})
# You can nest different connections when you need to
# (But you cannot nest the same connection)
with MyReplica:
values = MyGroup.find({MyGroup.tags: 'example'})
value = sum(doc['value'] for doc in values)
# HumbleDB allows you to nest different connections when you need
# consistency
with MyDoc:
doc = MyDoc()
doc.value = value
MyDoc.insert(doc)
MyGroup.update({MyGroup.tags: 'example'},
{'$push': {MyGroup.related: MyDoc._id},
multi=True)
Pre-aggregated Reports¶
HumbleDB provides a framework for creating pre-aggregated reports based on the ideas laid out here.
These reports are ideal for gathering metrics on a relatively low number of unique events that happen with a regular frequency. For example, hits to a certain webpage, or offer signups.
In cases where the event data is sparse, diverse, or has many parameters, other aggregation approaches may work better.
note: | The documentation on reports is incomplete, and a work in progress. Please see the API Documentation for more information. If you have questions or issues, please contact me via github issues. |
---|
Changed in version 5.0: Reports got a full rewrite in version 5 of HumbleDB. They are not backwards compatible, but much more useful and efficient. It’s highly recommended that you migrate old reports to the new classes.
Example:
from humbledb.report import Report, MONTH, HOUR
class DailyPageHits(Report):
"""
This is an example of a class used to record hits to pages.
This class creates one document per page per month
This class records the total hits per page per month, as well as the
hits per page per hour for each hour in the month.
"""
config_database = 'reports'
config_collection = 'page_hits'
config_period = MONTH # This is the document timeframe
config_intervals = [MONTH, HOUR] # Timeframes that data is recorded
# Record a hit to the /about page
page = '/about'
with Mongo:
DailyPageHits.record(page)
# Get the last 24 hours worth of hits for /about as a list
with Mongo:
hits = DailyPageHits.hourly(page)[-24:]
Paginated Arrays¶
One of the limitations of HumbleDB is the performance of very large arrays within documents, particulary arrays which have indexing on keys within embedded documents for that array.
There are two issues which arise; first, that each append to the array increases the array size, which leads to document moves, decreasing the efficiency of the database, and second, that with each move of a very large array, the indexing on the whole array must be updated, which can be very slow for arrays over 10,000 elements.
The humbledb.array.Array
class addresses both of these situations by
first allowing for transparent preallocation of the array document to ensure
that only a minimal number of document moves happen, and second, by
transparently paginating the arrays into multiple documents, to limit the
maximum array size for any single document.
note: | Due to time constrants, the documentation on arrays is incomplete. I hope to fix this as soon as possible. |
---|
Example:
from humbledb import Mongo
from humbledb.array import Array
class Comments(Array):
config_database = 'humble'
config_collection = 'comments'
config_padding = 10000 # Number of bytes to pad with
config_max_size = 100 # Number of entries per page
with Mongo:
post = BlogPost.find_one(...) # This is the post associated with
# comments for this example
comments = Comments(post._id) # The argument is used to construct the array
# id, which uniquely identifies pages in this
# array. This can be any unique string value
with Mongo:
# Appending adds to the Array, creating a new page if necessary, and
# paginating when an array is too large.
# The current number of pages is returned
page_count = comments.append({
'user_name': "example_user",
'comment': "I really like arrays.",
'timestamp': datetime.now(),
})
comments.pages() # Return the current number of pages
comments.length() # Return the current number of entries
entries = comments.all() # Return all the entries as a list
first_page = comments[0] # Return a given page of entries as a list
pages = comments[0:2] # Slices work, but negative indexes *do not*
spec = {'user_name': "example_user"}
comments.remove(spec) # Remove all entries matching `spec`
comments.clear() # Remove all entries