67 try: |
67 try: |
68 self.session.repo.clear_caches( |
68 self.session.repo.clear_caches( |
69 self.session.transaction_data['neweids']) |
69 self.session.transaction_data['neweids']) |
70 except KeyError: |
70 except KeyError: |
71 pass |
71 pass |
72 |
|
73 |
|
74 class FTIndexEntityOp(hook.LateOperation): |
|
75 """operation to delay entity full text indexation to commit |
|
76 |
|
77 since fti indexing may trigger discovery of other entities, it should be |
|
78 triggered on precommit, not commit, and this should be done after other |
|
79 precommit operation which may add relations to the entity |
|
80 """ |
|
81 |
|
82 def precommit_event(self): |
|
83 session = self.session |
|
84 entity = self.entity |
|
85 if entity.eid in session.transaction_data.get('pendingeids', ()): |
|
86 return # entity added and deleted in the same transaction |
|
87 session.repo.system_source.fti_unindex_entity(session, entity.eid) |
|
88 for container in entity.fti_containers(): |
|
89 session.repo.index_entity(session, container) |
|
90 |
|
91 def commit_event(self): |
|
92 pass |
|
93 |
72 |
94 |
73 |
95 def del_existing_rel_if_needed(session, eidfrom, rtype, eidto): |
74 def del_existing_rel_if_needed(session, eidfrom, rtype, eidto): |
96 """delete existing relation when adding a new one if card is 1 or ? |
75 """delete existing relation when adding a new one if card is 1 or ? |
97 |
76 |
131 'NOT X eid %%(x)s' % rtype, |
110 'NOT X eid %%(x)s' % rtype, |
132 {'x': eidfrom, 'y': eidto}, 'y') |
111 {'x': eidfrom, 'y': eidto}, 'y') |
133 if rset: |
112 if rset: |
134 safe_delete_relation(session, rschema, *rset[0]) |
113 safe_delete_relation(session, rschema, *rset[0]) |
135 |
114 |
|
115 |
136 def safe_delete_relation(session, rschema, subject, object): |
116 def safe_delete_relation(session, rschema, subject, object): |
137 if not rschema.has_perm(session, 'delete', fromeid=subject, toeid=object): |
117 if not rschema.has_perm(session, 'delete', fromeid=subject, toeid=object): |
138 raise Unauthorized() |
118 raise Unauthorized() |
139 session.repo.glob_delete_relation(session, subject, rschema.type, object) |
119 session.repo.glob_delete_relation(session, subject, rschema.type, object) |
140 |
120 |
162 # initial schema, should be build or replaced latter |
142 # initial schema, should be build or replaced latter |
163 self.schema = schema.CubicWebSchema(config.appid) |
143 self.schema = schema.CubicWebSchema(config.appid) |
164 self.vreg.schema = self.schema # until actual schema is loaded... |
144 self.vreg.schema = self.schema # until actual schema is loaded... |
165 # querier helper, need to be created after sources initialization |
145 # querier helper, need to be created after sources initialization |
166 self.querier = querier.QuerierHelper(self, self.schema) |
146 self.querier = querier.QuerierHelper(self, self.schema) |
167 # should we reindex in changes? |
|
168 self.do_fti = not config['delay-full-text-indexation'] |
|
169 # sources |
147 # sources |
170 self.sources = [] |
148 self.sources = [] |
171 self.sources_by_uri = {} |
149 self.sources_by_uri = {} |
172 # FIXME: store additional sources info in the system database ? |
150 # FIXME: store additional sources info in the system database ? |
173 # FIXME: sources should be ordered (add_entity priority) |
151 # FIXME: sources should be ordered (add_entity priority) |
776 return session |
754 return session |
777 |
755 |
778 # data sources handling ################################################### |
756 # data sources handling ################################################### |
779 # * correspondance between eid and (type, source) |
757 # * correspondance between eid and (type, source) |
780 # * correspondance between eid and local id (i.e. specific to a given source) |
758 # * correspondance between eid and local id (i.e. specific to a given source) |
781 # * searchable text indexes |
|
782 |
759 |
783 def type_and_source_from_eid(self, eid, session=None): |
760 def type_and_source_from_eid(self, eid, session=None): |
784 """return a tuple (type, source, extid) for the entity with id <eid>""" |
761 """return a tuple (type, source, extid) for the entity with id <eid>""" |
785 try: |
762 try: |
786 eid = typed_eid(eid) |
763 eid = typed_eid(eid) |
903 def add_info(self, session, entity, source, extid=None, complete=True): |
880 def add_info(self, session, entity, source, extid=None, complete=True): |
904 """add type and source info for an eid into the system table, |
881 """add type and source info for an eid into the system table, |
905 and index the entity with the full text index |
882 and index the entity with the full text index |
906 """ |
883 """ |
907 # begin by inserting eid/type/source/extid into the entities table |
884 # begin by inserting eid/type/source/extid into the entities table |
908 self.system_source.add_info(session, entity, source, extid) |
|
909 if complete: |
|
910 entity.complete(entity.e_schema.indexable_attributes()) |
|
911 new = session.transaction_data.setdefault('neweids', set()) |
885 new = session.transaction_data.setdefault('neweids', set()) |
912 new.add(entity.eid) |
886 new.add(entity.eid) |
913 # now we can update the full text index |
887 self.system_source.add_info(session, entity, source, extid, complete) |
914 if self.do_fti: |
|
915 FTIndexEntityOp(session, entity=entity) |
|
916 CleanupEidTypeCacheOp(session) |
888 CleanupEidTypeCacheOp(session) |
917 |
889 |
918 def delete_info(self, session, eid): |
890 def delete_info(self, session, eid): |
919 self._prepare_delete_info(session, eid) |
891 self._prepare_delete_info(session, eid) |
920 self._delete_info(session, eid) |
892 self._delete_info(session, eid) |
959 selection = '%s %s X' % (var, rtype) |
931 selection = '%s %s X' % (var, rtype) |
960 rql = 'DELETE %s WHERE X eid %%(x)s' % selection |
932 rql = 'DELETE %s WHERE X eid %%(x)s' % selection |
961 # unsafe_execute since we suppose that if user can delete the entity, |
933 # unsafe_execute since we suppose that if user can delete the entity, |
962 # he can delete all its relations without security checking |
934 # he can delete all its relations without security checking |
963 session.unsafe_execute(rql, {'x': eid}, 'x', build_descr=False) |
935 session.unsafe_execute(rql, {'x': eid}, 'x', build_descr=False) |
964 |
|
965 def index_entity(self, session, entity): |
|
966 """full text index a modified entity""" |
|
967 alreadydone = session.transaction_data.setdefault('indexedeids', set()) |
|
968 if entity.eid in alreadydone: |
|
969 self.debug('skipping reindexation of %s, already done', entity.eid) |
|
970 return |
|
971 alreadydone.add(entity.eid) |
|
972 self.system_source.fti_index_entity(session, entity) |
|
973 |
936 |
974 def locate_relation_source(self, session, subject, rtype, object): |
937 def locate_relation_source(self, session, subject, rtype, object): |
975 subjsource = self.source_from_eid(subject, session) |
938 subjsource = self.source_from_eid(subject, session) |
976 objsource = self.source_from_eid(object, session) |
939 objsource = self.source_from_eid(object, session) |
977 if not subjsource is objsource: |
940 if not subjsource is objsource: |
1104 self.hm.call_hooks('before_add_relation', session, |
1067 self.hm.call_hooks('before_add_relation', session, |
1105 eidfrom=entity.eid, rtype=attr, eidto=value) |
1068 eidfrom=entity.eid, rtype=attr, eidto=value) |
1106 if not only_inline_rels: |
1069 if not only_inline_rels: |
1107 self.hm.call_hooks('before_update_entity', session, entity=entity) |
1070 self.hm.call_hooks('before_update_entity', session, entity=entity) |
1108 source.update_entity(session, entity) |
1071 source.update_entity(session, entity) |
1109 if not only_inline_rels: |
1072 self.system_source.update_info(session, entity, need_fti_update) |
1110 if need_fti_update and self.do_fti: |
1073 if source.should_call_hooks: |
1111 # reindex the entity only if this query is updating at least |
1074 if not only_inline_rels: |
1112 # one indexable attribute |
|
1113 FTIndexEntityOp(session, entity=entity) |
|
1114 if source.should_call_hooks: |
|
1115 self.hm.call_hooks('after_update_entity', session, entity=entity) |
1075 self.hm.call_hooks('after_update_entity', session, entity=entity) |
1116 if source.should_call_hooks: |
|
1117 for attr, value, prevvalue in relations: |
1076 for attr, value, prevvalue in relations: |
1118 # if the relation is already cached, update existant cache |
1077 # if the relation is already cached, update existant cache |
1119 relcache = entity.relation_cached(attr, 'subject') |
1078 relcache = entity.relation_cached(attr, 'subject') |
1120 if prevvalue is not None: |
1079 if prevvalue is not None: |
1121 self.hm.call_hooks('after_delete_relation', session, |
1080 self.hm.call_hooks('after_delete_relation', session, |