From a2ea6b1e516f5f605a5c8774b41d8ed1b40ed732 Mon Sep 17 00:00:00 2001 From: Kees Bakker Date: Thu, 11 Dec 2008 10:06:16 +0000 Subject: [PATCH] Speed improvement for the "has common ancestor" filter rule. For a database with 2300 persons the time to filter goes from 500 seconds to 80. I believe that the difference must be even bigger for larger database. The new algorithm is more or less linear. Each person is only visited once to compute the ancestors, and it does depth first. * src/Filters/Rules/Person/_HasCommonAncestorWith.py svn: r11451 --- .../Rules/Person/_HasCommonAncestorWith.py | 56 ++++++++++--------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/src/Filters/Rules/Person/_HasCommonAncestorWith.py b/src/Filters/Rules/Person/_HasCommonAncestorWith.py index 658f9f7b1..e47960ddd 100644 --- a/src/Filters/Rules/Person/_HasCommonAncestorWith.py +++ b/src/Filters/Rules/Person/_HasCommonAncestorWith.py @@ -49,35 +49,41 @@ class HasCommonAncestorWith(Rule): description = _("Matches people that have a common ancestor " "with a specified person") - def prepare(self,db): + def prepare(self, db): self.db = db - # Keys in `ancestor_cache' are ancestors of list[0]. - # We delay the computation of ancestor_cache until the - # first use, because it's not uncommon to instantiate - # this class and not use it. + # For each(!) person we keep track of who their ancestors + # are, in a set(). So we only have to compute a person's + # ancestor list once. + # Start with filling the cache for root person (gramps_id in self.list[0]) self.ancestor_cache = {} + self.root_person = db.get_person_from_gramps_id(self.list[0]) + self.add_ancs(db, self.root_person) + + def add_ancs(self, db, person): + if person.handle not in self.ancestor_cache: + self.ancestor_cache[person.handle] = set() + + for fam_handle in person.get_parent_family_handle_list(): + fam = db.get_family_from_handle(fam_handle) + for par_handle in (fam.get_father_handle(), fam.get_mother_handle()): + if par_handle: + par = db.get_person_from_handle(par_handle) + if par and par.handle not in self.ancestor_cache: + self.add_ancs(db, par) + if par: + self.ancestor_cache[person.handle].add(par) + self.ancestor_cache[person.handle] |= self.ancestor_cache[par.handle] def reset(self): self.ancestor_cache = {} - def init_ancestor_cache(self,db): - # list[0] is an Id, but we need to pass a Person to for_each_ancestor. - try: - handle = db.get_person_from_gramps_id(self.list[0]).get_handle() - if handle: - def init(self, handle): self.ancestor_cache[handle] = 1 - for_each_ancestor(db,[handle],init,self) - except: - pass + def has_common_ancestor(self, other): + if self.ancestor_cache[self.root_person.handle] & self.ancestor_cache[other.handle]: + return True + return False - def apply(self,db,person): - # On the first call, we build the ancestor cache for the - # reference person. Then, for each person to test, - # we browse his ancestors until we found one in the cache. - if len(self.ancestor_cache) == 0: - self.init_ancestor_cache(db) - handle = person.handle - return for_each_ancestor( - db,[handle], - lambda self, handle: handle in self.ancestor_cache, - self); + def apply(self, db, person): + if person.handle not in self.ancestor_cache: + self.add_ancs(db, person) + + return self.has_common_ancestor(person)