Commit 4d594631 authored by Sylvain's avatar Sylvain
Browse files

support for entities whose fulltext content should be indexed on a related

entity, using new fulltext_container attribute on RelationSchema instance
and new fulltext_relations and fulltext_containers methods on EntitySchema
parent 3c4b362d8c5f
...@@ -4,6 +4,9 @@ ChangeLog for yams ...@@ -4,6 +4,9 @@ ChangeLog for yams
-- --
* fix a bug in entity validation : should convert value to the correct * fix a bug in entity validation : should convert value to the correct
python type before checking constraints python type before checking constraints
* support for entities whose fulltext content should be indexed on a related
entity, using new fulltext_container attribute on RelationSchema instance
and new fulltext_relations and fulltext_containers methods on EntitySchema
2008-02-15 -- 0.16.0 2008-02-15 -- 0.16.0
* nicer schema image view * nicer schema image view
......
...@@ -24,7 +24,7 @@ __all__ = ('ObjectRelation', 'SubjectRelation', 'BothWayRelation', ...@@ -24,7 +24,7 @@ __all__ = ('ObjectRelation', 'SubjectRelation', 'BothWayRelation',
ETYPE_PROPERTIES = ('meta', 'description', 'permissions') ETYPE_PROPERTIES = ('meta', 'description', 'permissions')
# don't put description inside, handled "manually" # don't put description inside, handled "manually"
RTYPE_PROPERTIES = ('meta', 'symetric', 'inlined', 'permissions') RTYPE_PROPERTIES = ('meta', 'symetric', 'inlined', 'fulltext_container', 'permissions')
RDEF_PROPERTIES = ('cardinality', 'constraints', 'composite', RDEF_PROPERTIES = ('cardinality', 'constraints', 'composite',
'order', 'default', 'uid', 'indexed', 'uid', 'order', 'default', 'uid', 'indexed', 'uid',
'fulltextindexed', 'internationalizable') 'fulltextindexed', 'internationalizable')
...@@ -229,6 +229,7 @@ class EntityType(Definition): ...@@ -229,6 +229,7 @@ class EntityType(Definition):
class RelationType(Definition): class RelationType(Definition):
symetric = MARKER symetric = MARKER
inlined = MARKER inlined = MARKER
fulltext_container = MARKER
def __init__(self, name=None, **kwargs): def __init__(self, name=None, **kwargs):
super(RelationType, self).__init__(name) super(RelationType, self).__init__(name)
......
...@@ -356,24 +356,42 @@ class EntitySchema(ERSchema): ...@@ -356,24 +356,42 @@ class EntitySchema(ERSchema):
for rschema in self.object_relations(): for rschema in self.object_relations():
yield rschema, rschema.subjects(self), 'object' yield rschema, rschema.subjects(self), 'object'
def main_attribute(self): def main_attribute(self):
"""convenience method that returns the *main* (i.e. the first non meta) """convenience method that returns the *main* (i.e. the first non meta)
attribute defined in the entity schema attribute defined in the entity schema
""" """
for rschema, _ in self.attribute_definitions(): for rschema, _ in self.attribute_definitions():
if not rschema.meta: if not rschema.meta:
# XXX return rschema.type for bw compat ?
return rschema return rschema
def indexable_attributes(self): def indexable_attributes(self):
"""return the name of relations to index""" """return the (name, role) of relations to index"""
assert not self.is_final() assert not self.is_final()
for rschema in self.subject_relations(): for rschema in self.subject_relations():
if rschema.is_final(): if rschema.is_final():
if self.rproperty(rschema, 'fulltextindexed'): if self.rproperty(rschema, 'fulltextindexed'):
# XXX return rschema.type for bw compat ?
yield rschema yield rschema
def fulltext_relations(self):
"""return the (name, role) of relations to index"""
assert not self.is_final()
for rschema in self.subject_relations():
if not rschema.is_final() and rschema.fulltext_container == 'subject':
yield rschema, 'subject'
for rschema in self.object_relations():
if rschema.fulltext_container == 'object':
yield rschema, 'object'
def fulltext_containers(self):
"""return relations whose extremity points to an entity that should
contains the full text index content of entities of this type
"""
for rschema in self.subject_relations():
if rschema.fulltext_container == 'object':
yield rschema, 'object'
for rschema in self.object_relations():
if rschema.fulltext_container == 'subject':
yield rschema, 'subject'
def defaults(self): def defaults(self):
"""return an iterator on (attribute name, default value)""" """return an iterator on (attribute name, default value)"""
...@@ -382,7 +400,6 @@ class EntitySchema(ERSchema): ...@@ -382,7 +400,6 @@ class EntitySchema(ERSchema):
if rschema.is_final(): if rschema.is_final():
value = self.default(rschema) value = self.default(rschema)
if value is not None: if value is not None:
# XXX return rschema.type for bw compat ?
yield rschema, value yield rschema, value
def default(self, rtype): def default(self, rtype):
...@@ -531,6 +548,9 @@ class RelationSchema(ERSchema): ...@@ -531,6 +548,9 @@ class RelationSchema(ERSchema):
# if this relation is symetric/inlined # if this relation is symetric/inlined
self.symetric = rdef.symetric or False self.symetric = rdef.symetric or False
self.inlined = rdef.inlined or False self.inlined = rdef.inlined or False
# if full text content of subject/object entity should be added
# to other side entity (the container)
self.fulltext_container = rdef.fulltext_container or None
# if this relation is an attribute relation # if this relation is an attribute relation
self.final = False self.final = False
# mapping to subject/object with schema as key # mapping to subject/object with schema as key
...@@ -685,7 +705,7 @@ class RelationSchema(ERSchema): ...@@ -685,7 +705,7 @@ class RelationSchema(ERSchema):
raise KeyError('%s %s %s' % (subject, self, object)) raise KeyError('%s %s %s' % (subject, self, object))
def rproperty(self, subject, object, property): def rproperty(self, subject, object, property):
"""return the properties dictionary of a relation""" """return the property for a relation definition"""
return self.rproperties(subject, object).get(property) return self.rproperties(subject, object).get(property)
def set_rproperty(self, subject, object, pname, value): def set_rproperty(self, subject, object, pname, value):
...@@ -696,7 +716,7 @@ class RelationSchema(ERSchema): ...@@ -696,7 +716,7 @@ class RelationSchema(ERSchema):
def init_rproperties(self, subject, object, rdef): def init_rproperties(self, subject, object, rdef):
key = subject, object key = subject, object
if key in self._rproperties: if key in self._rproperties:
msg = '%s already defined for %s' % (key, self) msg = '(%s, %s) already defined for %s' % (subject, object, self)
raise BadSchemaDefinition(msg) raise BadSchemaDefinition(msg)
self._rproperties[key] = {} self._rproperties[key] = {}
for prop, default in self.rproperty_defs(key[1]).iteritems(): for prop, default in self.rproperty_defs(key[1]).iteritems():
......
...@@ -11,6 +11,7 @@ class require_permission(RelationType): ...@@ -11,6 +11,7 @@ class require_permission(RelationType):
"""link a permission to the entity. This permission should be used in the """link a permission to the entity. This permission should be used in the
security definition of the entity's type to be useful. security definition of the entity's type to be useful.
""" """
fulltext_container = 'subject'
permissions = { permissions = {
'read': ('managers', 'users', 'guests'), 'read': ('managers', 'users', 'guests'),
'add': ('managers',), 'add': ('managers',),
...@@ -26,5 +27,6 @@ class missing_require_permission(RelationDefinition): ...@@ -26,5 +27,6 @@ class missing_require_permission(RelationDefinition):
class EPermission(MetaEntityType): class EPermission(MetaEntityType):
"""entity type that may be used to construct some advanced security configuration """entity type that may be used to construct some advanced security configuration
""" """
name = String(required=True, indexed=True, internationalizable=True, maxsize=100, name = String(required=True, indexed=True, internationalizable=True,
fulltextindexed=True, maxsize=100,
description=_('name or identifier of the permission')) description=_('name or identifier of the permission'))
...@@ -125,6 +125,23 @@ class SchemaLoaderTC(TestCase): ...@@ -125,6 +125,23 @@ class SchemaLoaderTC(TestCase):
self.assert_(not eschema.rproperty('sexe', 'fulltextindexed')) self.assert_(not eschema.rproperty('sexe', 'fulltextindexed'))
indexable = sorted(eschema.indexable_attributes()) indexable = sorted(eschema.indexable_attributes())
self.assertEquals(['nom', 'prenom', 'titre'], indexable) self.assertEquals(['nom', 'prenom', 'titre'], indexable)
self.assertEquals(schema.rschema('works_for').fulltext_container, None)
self.assertEquals(schema.rschema('require_permission').fulltext_container,
'subject')
eschema = schema.eschema('Company')
indexable = sorted(eschema.indexable_attributes())
self.assertEquals([], indexable)
indexable = sorted(eschema.fulltext_relations())
self.assertEquals([('require_permission', 'subject')], indexable)
containers = sorted(eschema.fulltext_containers())
self.assertEquals([], containers)
eschema = schema.eschema('EPermission')
indexable = sorted(eschema.indexable_attributes())
self.assertEquals(['name'], indexable)
indexable = sorted(eschema.fulltext_relations())
self.assertEquals([], indexable)
containers = sorted(eschema.fulltext_containers())
self.assertEquals([('require_permission', 'subject')], containers)
def test_internationalizable(self): def test_internationalizable(self):
eschema = schema.eschema('Eetype') eschema = schema.eschema('Eetype')
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment