Skip to content

Commit

Permalink
Projected keys in '$sub' or 'Ssub.' projections are now convered to f…
Browse files Browse the repository at this point in the history
…ull paths within the projection.
  • Loading branch information
anthonyjb committed Feb 6, 2019
1 parent 80954ae commit ac1fd20
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 4 deletions.
95 changes: 94 additions & 1 deletion .pytest_cache/v/cache/lastfailed
Original file line number Diff line number Diff line change
@@ -1 +1,94 @@
{}
{
"tests/factory/makers/test_dates.py::test_date_between": true,
"tests/factory/makers/test_images.py::test_image_url": true,
"tests/factory/makers/test_makers.py::test_dict_of": true,
"tests/factory/makers/test_makers.py::test_faker": true,
"tests/factory/makers/test_makers.py::test_lambda": true,
"tests/factory/makers/test_makers.py::test_list_of": true,
"tests/factory/makers/test_makers.py::test_maker": true,
"tests/factory/makers/test_makers.py::test_static": true,
"tests/factory/makers/test_makers.py::test_sub_factory": true,
"tests/factory/makers/test_makers.py::test_unique": true,
"tests/factory/makers/test_numbers.py::test_counter": true,
"tests/factory/makers/test_numbers.py::test_float": true,
"tests/factory/makers/test_numbers.py::test_int": true,
"tests/factory/makers/test_selections.py::test_cycle": true,
"tests/factory/makers/test_selections.py::test_one_of": true,
"tests/factory/makers/test_selections.py::test_random_reference": true,
"tests/factory/makers/test_selections.py::test_some_of": true,
"tests/factory/makers/test_text.py::test_code": true,
"tests/factory/makers/test_text.py::test_join": true,
"tests/factory/makers/test_text.py::test_lorem": true,
"tests/factory/makers/test_text.py::test_markov": true,
"tests/factory/makers/test_text.py::test_sequence": true,
"tests/factory/test_blueprints.py::test_blueprint_assemble": true,
"tests/factory/test_blueprints.py::test_blueprint_defaults": true,
"tests/factory/test_blueprints.py::test_blueprint_finish": true,
"tests/factory/test_blueprints.py::test_blueprint_getters": true,
"tests/factory/test_blueprints.py::test_blueprint_reassemble": true,
"tests/factory/test_blueprints.py::test_blueprint_reset": true,
"tests/factory/test_factory.py::test_factory_assemble": true,
"tests/factory/test_factory.py::test_factory_finish": true,
"tests/factory/test_factory.py::test_factory_populate": true,
"tests/factory/test_factory.py::test_factory_reassemble": true,
"tests/factory/test_quotas.py::test_gauss": true,
"tests/factory/test_quotas.py::test_quota": true,
"tests/factory/test_quotas.py::test_random": true,
"tests/test_frames.py::test_by_id": true,
"tests/test_frames.py::test_cascade": true,
"tests/test_frames.py::test_count": true,
"tests/test_frames.py::test_delete": true,
"tests/test_frames.py::test_delete_many": true,
"tests/test_frames.py::test_dot_notation": true,
"tests/test_frames.py::test_equal": true,
"tests/test_frames.py::test_frame": true,
"tests/test_frames.py::test_get_collection": true,
"tests/test_frames.py::test_get_db": true,
"tests/test_frames.py::test_get_fields": true,
"tests/test_frames.py::test_get_private_fields": true,
"tests/test_frames.py::test_ids": true,
"tests/test_frames.py::test_insert": true,
"tests/test_frames.py::test_insert_many": true,
"tests/test_frames.py::test_listen": true,
"tests/test_frames.py::test_many": true,
"tests/test_frames.py::test_nullify": true,
"tests/test_frames.py::test_one": true,
"tests/test_frames.py::test_projection": true,
"tests/test_frames.py::test_pull": true,
"tests/test_frames.py::test_python_sort": true,
"tests/test_frames.py::test_reload": true,
"tests/test_frames.py::test_stop_listening": true,
"tests/test_frames.py::test_timestamp_insert": true,
"tests/test_frames.py::test_timestamp_update": true,
"tests/test_frames.py::test_to_json_type": true,
"tests/test_frames.py::test_update": true,
"tests/test_frames.py::test_update_many": true,
"tests/test_frames.py::test_upsert": true,
"tests/test_pagination.py::test_page": true,
"tests/test_pagination.py::test_paginator": true,
"tests/test_pagination.py::test_paginator_with_filter": true,
"tests/test_pagination.py::test_paginator_with_orphans": true,
"tests/test_pagination.py::test_paginator_with_per_page": true,
"tests/test_pagination.py::test_paginator_with_projection": true,
"tests/test_pagination.py::test_paginator_with_sort": true,
"tests/test_queries.py::test_all": true,
"tests/test_queries.py::test_and": true,
"tests/test_queries.py::test_elem_match": true,
"tests/test_queries.py::test_equal": true,
"tests/test_queries.py::test_exists": true,
"tests/test_queries.py::test_greater_than": true,
"tests/test_queries.py::test_greater_than_or_equal": true,
"tests/test_queries.py::test_in": true,
"tests/test_queries.py::test_less_than": true,
"tests/test_queries.py::test_less_than_or_equal": true,
"tests/test_queries.py::test_nor": true,
"tests/test_queries.py::test_not": true,
"tests/test_queries.py::test_not_equal": true,
"tests/test_queries.py::test_not_in": true,
"tests/test_queries.py::test_or": true,
"tests/test_queries.py::test_q": true,
"tests/test_queries.py::test_size": true,
"tests/test_queries.py::test_sort_by": true,
"tests/test_queries.py::test_to_refs": true,
"tests/test_queries.py::test_type": true
}
1 change: 1 addition & 0 deletions .pytest_cache/v/cache/nodeids
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,7 @@
"tests/test_frames.py::test_get_db",
"tests/test_frames.py::test_get_fields",
"tests/test_frames.py::test_get_private_fields",
"tests/test_frames.py::test_flattern_projection",
"tests/test_pagination.py::test_paginator",
"tests/test_pagination.py::test_paginator_with_filter",
"tests/test_pagination.py::test_paginator_with_sort",
Expand Down
49 changes: 48 additions & 1 deletion mongoframes/frames.py
Original file line number Diff line number Diff line change
Expand Up @@ -657,7 +657,19 @@ def _flatten_projection(cls, projection):
elif '$sub' in value or '$sub.' in value:
subs[key] = value

flat_projection[key] = project_value
if '$sub' in value:
sub_frame = value['$sub']

if '$sub,' in value:
sub_frame = value['$sub.']

project_value = sub_frame._projection_to_paths(key, value)

if isinstance(project_value, dict):
flat_projection.update(project_value)

else:
flat_projection[key] = project_value

elif key == '$ref':
# Strip any $ref key
Expand Down Expand Up @@ -800,3 +812,38 @@ def _apply_projection(cls, documents, projection):
# Add sub-frames to the documents (if required)
if subs:
Frame._apply_sub_frames(documents, subs)

@classmethod
def _projection_to_paths(cls, root_key, projection):
"""
Expand a $sub/$sub. projection to a single projection of True (if
inclusive) or a map of full paths (e.g `employee.company.tel`).
"""

# Referenced projections are handled separately so just flag the
# reference field to true.
if '$ref' in projection:
return True

sub_projection = {}
for key, value in projection.items():
if key in ['$sub', '$sub.']:
continue

sub_key = root_key + '.' + key

if isinstance(value, dict):
sub_value = cls._projection_to_paths(sub_key, value)
if isinstance(sub_value, dict):
sub_projection.update(sub_value)
else:
sub_projection[sub_key] = True

else:
sub_projection[sub_key] = True

if len(sub_projection) == 0:
# No specific keys so this is inclusive
return True

return sub_projection
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
# Versions should comply with PEP440. For a discussion on single-sourcing
# the version across setup.py and the project code, see
# https://packaging.python.org/en/latest/single_source_version.html
version='1.2.14',
version='1.3.0',
description='A fast unobtrusive MongoDB ODM for Python',
long_description=long_description,

Expand Down
23 changes: 22 additions & 1 deletion tests/test_frames.py
Original file line number Diff line number Diff line change
Expand Up @@ -728,4 +728,25 @@ def test_get_fields(mongo_client):

def test_get_private_fields(mongo_client):
"""Return the private fields for the class"""
assert Dragon.get_private_fields() == {'breed'}
assert Dragon.get_private_fields() == {'breed'}

def test_flattern_projection():
"""Flattern projection"""

projection, refs, subs = Lair._flatten_projection({
'name': True,
'inventory': {
'$sub': Inventory,
'gold': True,
'secret_draw': {
'$sub': Inventory,
'gold': True
}
}
})

assert projection == {
'name': True,
'inventory.gold': True,
'inventory.secret_draw.gold': True
}

0 comments on commit ac1fd20

Please sign in to comment.