Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unique/Name Key #112

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
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
35 changes: 0 additions & 35 deletions docs/make.bat

This file was deleted.

66 changes: 66 additions & 0 deletions docs/source/keys.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
================
Keys in Snovault
================

Broadly speaking in the 4DN space there are four different types of 'keys'. 'Unique', 'name' and 'identifying' keys are used in both Fourfront/CGAP and Snovault while 'lookup' keys are used only in FF/CGAP. This document will only touch on the first three. Below is a small table illustrating where each type of key is defined.


.. csv-table::
:header: "Schema", "Collection", "Type"
:widths: 10, 20, 10

uniqueKey, identification_key, name_key


Unique Key and Identification Key
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

These two keys are bundled together because they are closely related. A unique key is denoted in the schema of an item. This is a constraint on the items in the database that says that no two items can share the same value in this field. Note that if there are multiple fields that are marked as uniqueKey's then either one can be used to uniquely identify items. In either case a identification key must be specified in the collection decorator, which must be one of the unique keys. If a identification key is not specified you will not be able to lookup items via the resource path with either of their unique keys. See the below examples from the tests.

.. code-block:: python

# All of the following collections are referencing a schema which denotes
# fields 'obj_id' and 'name' as uniqueKey's. In the below case though if you
# wanted to look up an item by this type from the point of view of snovault
# you would only be able to do so via uuid
@collection('testing-keys')
class TestingKeys(Item):
""" Intended to test the behavior of uniqueKey value in schema """
item_type = 'testing_keys'
schema = load_schema('snovault:test_schemas/TestingKeys.json')


# In this case we specify the identification_key to be the obj_id. This allows us to
# use the resource path to get the item ie: Get /testing-keys-def/<obj_id>
# Note that the resource path is still the uuid
@collection('testing-keys-def', identification_key='testing_keys_def:obj_id')
class TestingKeysDef(Item):
"""
Intended to test the behavior of setting a identification key equal to one of the
uniqueKey's specified in the schema. This should allow us to get the object
via obj_id whereas before we could not.
"""
item_type = 'testing_keys_def'
schema = load_schema('snovault:test_schemas/TestingKeys.json')

Name Key
^^^^^^^^

The name key is a special field specified on the item type definition. It augments the resource path so that the '@id' field of the item contains a path using the name_key instead of the uuid. To be explicit, the name key must match the identification key and is only really useful for changing the resource path. See final example below.

.. code-block:: python

# In this case we specify matching identification_key and name_key. This means that
# the resource path is augmented to show the name_key instead of the uuid AND
# you can get the item via resource path ie: Get /testing-keys-name/<name>
# and that is what always shows up when you access that item
@collection('testing-keys-name', identification_key='testing_keys_name:name')
class TestingKeysName(Item):
"""
We set name as a identification key so that it can be used as a name_key in the
resource path. We should now see the name key in the @id field instead of
the uuid
"""
item_type = 'testing_keys_name'
schema = load_schema('snovault:test_schemas/TestingKeys.json')
name_key = 'name'
2 changes: 1 addition & 1 deletion src/snovault/_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Version information."""

# The following line *must* be the last in the module, exactly as formatted:
__version__ = "1.3.4"
__version__ = "1.3.6"
5 changes: 0 additions & 5 deletions src/snovault/elasticsearch/create_mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -474,11 +474,6 @@ def es_mapping(mapping, agg_items_mapping):
'type': 'object',
'include_in_all': False
},
'paths': {
'type': 'keyword',
'ignore_above': KW_IGNORE_ABOVE,
'include_in_all': False
},
'indexing_stats': {
'type': 'object',
'include_in_all': False
Expand Down
2 changes: 1 addition & 1 deletion src/snovault/elasticsearch/indexer_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def get_namespaced_index(config, index):
settings = config.registry.settings
except: # accept either config or registry as first arg
settings = config.settings
namespace = settings.get('indexer.namespace', '')
namespace = settings.get('indexer.namespace') or ''
return namespace + index


Expand Down
22 changes: 2 additions & 20 deletions src/snovault/indexing_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,28 +106,11 @@ def item_index_data(context, request):
links = new_links

principals_allowed = calc_principals(context)
path = resource_path(context)
paths = {path}
path = resource_path(context) + '/'
collection = context.collection

with indexing_timer(indexing_stats, 'unique_keys'):
unique_keys = context.unique_keys(properties)
if collection.unique_key in unique_keys:
paths.update(
resource_path(collection, key)
for key in unique_keys[collection.unique_key])

with indexing_timer(indexing_stats, 'paths'):
for base in (collection, request.root):
for key_name in ('accession', 'alias'):
if key_name not in unique_keys:
continue
paths.add(resource_path(base, uuid))
paths.update(
resource_path(base, key)
for key in unique_keys[key_name])

path = path + '/'

# setting _indexing_view enables the embed_cache and cause population of
# request._linked_uuids and request._rev_linked_uuids_by_item
request._indexing_view = True
Expand Down Expand Up @@ -182,7 +165,6 @@ def item_index_data(context, request):
'links': links,
'max_sid': context.max_sid,
'object': object_view,
'paths': sorted(paths),
'principals_allowed': principals_allowed,
'properties': properties,
'propsheets': {
Expand Down
20 changes: 10 additions & 10 deletions src/snovault/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def jsonld_context(self, request):
def actions(self, request):
actions = calculate_properties(self, request, category='action')
if actions:
return list(actions.values())
return sorted(list(actions.values()), key=lambda d: d.get('name'))


class Root(Resource):
Expand Down Expand Up @@ -142,21 +142,21 @@ class AbstractCollection(Resource, Mapping):
And some other info as well.

Collections allow retrieval of specific items with them by using the `get`
method with uuid or the unique_key
method with uuid or the identification_key (which must be a unique key)
"""
properties = {}
unique_key = None
identification_key = None

def __init__(self, registry, name, type_info, properties=None, acl=None, unique_key=None):
def __init__(self, registry, name, type_info, properties=None, acl=None, identification_key=None):
self.registry = registry
self.__name__ = name
self.type_info = type_info
if properties is not None:
self.properties = properties
if acl is not None:
self.__acl__ = acl
if unique_key is not None:
self.unique_key = unique_key
if identification_key is not None:
self.identification_key = identification_key

@reify
def connection(self):
Expand Down Expand Up @@ -200,8 +200,8 @@ def get(self, name, default=None):
if not self._allow_contained(resource):
return default
return resource
if self.unique_key is not None:
resource = self.connection.get_by_unique_key(self.unique_key, name)
if self.identification_key is not None:
resource = self.connection.get_by_unique_key(self.identification_key, name)
if resource is not None:
if not self._allow_contained(resource):
return default
Expand Down Expand Up @@ -248,8 +248,8 @@ class Collection(AbstractCollection):

class Item(Resource):
item_type = None
base_types = ['Item']
name_key = None
base_types = ['Item']
rev = {}
aggregated_items = {}
embedded_list = []
Expand Down Expand Up @@ -280,7 +280,6 @@ def __parent__(self):

@property
def __name__(self):

if self.name_key is None:
return str(self.uuid)
return self.properties.get(self.name_key, None) or str(self.uuid)
Expand Down Expand Up @@ -359,6 +358,7 @@ def get_filtered_rev_links(self, request, name):
return filtered_uuids

def unique_keys(self, properties):
""" Gets all schema fields defined to be uniqueKey's """
return {
name: [v for prop in props for v in ensurelist(properties.get(prop, ()))]
for name, props in self.type_info.schema_keys.items()
Expand Down
2 changes: 1 addition & 1 deletion src/snovault/test_schemas/EmbeddingTest.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@
"title": "Date added"
}
}
}
}
27 changes: 27 additions & 0 deletions src/snovault/test_schemas/TestingKeys.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"type": "object",
"properties": {
"name": {
"title": "Common Name",
"description": "Unique name for this object",
"type": "string",
"uniqueKey": true
},
"grouping": {
"title": "Grouping",
"description": "String name of a group that this item belongs to (not unique)",
"type": "string"
},
"obj_id": {
"title": "Object ID",
"description": "Unique ID of this object",
"type": "string",
"uniqueKey": true
},
"system_id": {
"title": "System ID",
"description": "Unique System ID for this object that is not marked as unique in schema",
"type": "string"
}
}
}
2 changes: 1 addition & 1 deletion src/snovault/test_schemas/TestingLinkTargetSno.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
}
},
"additionalProperties": false
}
}
Loading