每秒基于10k次以上的多个条件查找对象

问题描述

我有约10到15个类别为固定枚举的cat1cat2等,它们可能每两周发生一次更改,因此我们可以说它们是不变的。 例如,cat1枚举可以具有以下值:

cat1: [c1a,c1b,c1c,c1d,c1e]

我有这样的对象(其中约有10000个)

id: 1,cat1: [c1a,c1d],cat2: [ c2a,c2d,c2z],cat3: [c3d] ...
id: 2,cat1: [c1b,c2b],cat3: [c3a,c3b,c3c] ... 
id: 3,c1e],cat2: [ c2a],c3d] ... 

...
id: n,cat2: [ c2e],c3c,c3d] ...

现在我收到的请求看起来像这样,每个类别都有一个值:

cat1: c1b,cat2: c2a,cat3: c3d ...

我需要获取与该请求匹配的对象的所有ID,因此所有包含该请求中每个cat值的对象。请求和对象始终具有相同数量的类别。

为了更好地理解该问题,在sql解决该问题的天真的方法就像

SELECT id FROM objects WHERE 'c1b' IN cat1 AND 'c2a' IN cat2 AND 'c3d' IN cat3 ...

我们的示例请求和示例对象的结果为:id: [1,3]

我尝试为此使用集合,因此我为每个category-category_value进行了设置,例如cat1-c1acat1-c1bcat2-c2a等,其中对象的ID为该值设置然后根据请求,我会在请求中匹配值的集合之间进行交集,但是具有5位数的请求/秒,这确实无法很好地扩展。也许我可以用更多的空间来交换时间,或者几乎用所有的空间来交换时间,并预先计算一个具有获得O(1)的所有可能性的哈希表,但是所需的空间量确实会很高。我正在寻找解决此问题的其他可行解决方案。对象不会经常更改,也不会经常添加新对象,因此我们只读得很重。任何人有任何想法或建议或解决类似的问题?也许某些数据库/键值存储可以很好地处理此用例?有白皮书吗?

解决方法

我将您的ID存储在Python列表ids中。 ids[id_num]是类别列表。 ids[id_num][cat_num]是一组整数,而不是枚举中的字母,但重要的是它们是不同的。 从该id列表中,您可以生成一个反向映射,以便给定(cat_num,enum_num)对,您可以映射到所有cat_num'th类别中包含该enum_num的id_nums的集合!

#%% create reverse map from (cat,val) pairs to sets of possible id's

cat_entry_2_ids = dict()
for id_num,this_ids_cats in enumerate(ids):
    for cat_num,cat_vals in enumerate(this_ids_cats):
        for val in cat_vals:
            cat_num_val = (cat_num,val)
            cat_entry_2_ids.setdefault(cat_num_val,set()).add(id_num)

以上映射可以保存并重新加载,直到枚举/ id更改为止。

给出一个特定的请求,此处显示为该编号类别中包含的枚举的列表;然后使用该映射返回每个类别中具有所请求枚举的所有ID。

def get_id(request):
    idset = cat_entry_2_ids[(0,request[0])].copy()
    for cat_num_req in enumerate(request):
        idset.intersection_update(cat_entry_2_ids.get(cat_num_req,set()))
        if not idset:
            break
    return sorted(idset)

时间取决于10到15个字典查询并设置交集。在Python中,我的速度约为每秒2_500。也许在映射中更改语言和/或并行查找(您的10-15个类别中的每个线程都有一个线程),可能使您超过10_000查找/秒的障碍?