relation view has been optimised

This commit is contained in:
pycook 2019-12-03 19:10:54 +08:00
parent 4987908368
commit 9b9ae61505
9 changed files with 88 additions and 32 deletions

View File

@ -67,7 +67,7 @@ def init_cache():
ci_relations = CIRelation.get_by(to_dict=False) ci_relations = CIRelation.get_by(to_dict=False)
relations = dict() relations = dict()
for cr in ci_relations: for cr in ci_relations:
relations.setdefault(cr.first_ci_id, []).append(cr.second_ci_id) relations.setdefault(cr.first_ci_id, {}).update({cr.second_ci_id: cr.second_ci.type_id})
for i in relations: for i in relations:
relations[i] = json.dumps(relations[i]) relations[i] = json.dumps(relations[i])
if relations: if relations:

View File

@ -566,4 +566,4 @@ class CIRelationManager(object):
ci_relation_delete.apply_async(args=(first_ci_id, second_ci_id), queue=CMDB_QUEUE) ci_relation_delete.apply_async(args=(first_ci_id, second_ci_id), queue=CMDB_QUEUE)
return cls.delete(cr.cr_id) return cls.delete(cr.id)

View File

@ -13,6 +13,7 @@ from api.lib.cmdb.cache import AttributeCache
from api.lib.cmdb.cache import CITypeAttributeCache from api.lib.cmdb.cache import CITypeAttributeCache
from api.lib.cmdb.cache import CITypeCache from api.lib.cmdb.cache import CITypeCache
from api.models.cmdb import CITypeAttribute from api.models.cmdb import CITypeAttribute
from api.models.cmdb import CITypeRelation
from api.models.cmdb import PreferenceRelationView from api.models.cmdb import PreferenceRelationView
from api.models.cmdb import PreferenceShowAttributes from api.models.cmdb import PreferenceShowAttributes
from api.models.cmdb import PreferenceTreeView from api.models.cmdb import PreferenceTreeView
@ -124,17 +125,28 @@ class PreferenceManager(object):
for i in result[view_name]: for i in result[view_name]:
id2type[i['parent_id']] = None id2type[i['parent_id']] = None
id2type[i['child_id']] = None id2type[i['child_id']] = None
topo = {i['child_id']: {i['parent_id']} for i in result[view_name]}
leaf = list(set(toposort.toposort_flatten(topo)) - set([j for i in topo.values() for j in i]))
result[view_name] = toposort.toposort_flatten( leaf2show_types = {i: [t['child_id'] for t in CITypeRelation.get_by(parent_id=i)] for i in leaf}
{i['child_id']: {i['parent_id']} for i in result[view_name]})
result[view_name] = dict(topo=list(map(list, toposort.toposort(topo))),
topo_flatten=list(toposort.toposort_flatten(topo)),
leaf=leaf,
leaf2show_types=leaf2show_types,
show_types=[CITypeCache.get(j).to_dict()
for i in leaf2show_types.values() for j in i])
for type_id in id2type: for type_id in id2type:
id2type[type_id] = CITypeCache.get(type_id).to_dict() id2type[type_id] = CITypeCache.get(type_id).to_dict()
print(result)
return result, id2type, sorted(name2id, key=lambda x: x[1]) return result, id2type, sorted(name2id, key=lambda x: x[1])
@classmethod @classmethod
def create_or_update_relation_view(cls, name, cr_ids): def create_or_update_relation_view(cls, name, cr_ids):
if not cr_ids:
return abort(400, "Node must be selected")
existed = PreferenceRelationView.get_by(name=name, to_dict=False, first=True) existed = PreferenceRelationView.get_by(name=name, to_dict=False, first=True)
if existed is None: if existed is None:
PreferenceRelationView.create(name=name, cr_ids=json.dumps(cr_ids)) PreferenceRelationView.create(name=name, cr_ids=json.dumps(cr_ids))
@ -145,4 +157,5 @@ class PreferenceManager(object):
def delete_relation_view(name): def delete_relation_view(name):
for existed in PreferenceRelationView.get_by(name=name, to_dict=False): for existed in PreferenceRelationView.get_by(name=name, to_dict=False):
existed.soft_delete() existed.soft_delete()
return name return name

View File

@ -73,6 +73,10 @@ class Search(object):
def _in_query_handle(self, attr, v): def _in_query_handle(self, attr, v):
terms = v[1:-1].split(";") terms = v[1:-1].split(";")
operator = "|" operator = "|"
if attr in ('_type', 'ci_type', 'type_id') and terms and terms[0].isdigit():
attr = "type_id"
terms = map(int, terms)
current_app.logger.warning(terms)
for term in terms: for term in terms:
self._operator2query(operator).append({ self._operator2query(operator).append({
"term": { "term": {
@ -164,6 +168,7 @@ class Search(object):
def _query_build_raw(self): def _query_build_raw(self):
queries = handle_arg_list(self.orig_query) queries = handle_arg_list(self.orig_query)
current_app.logger.debug(queries) current_app.logger.debug(queries)
self.__query_build_by_field(queries) self.__query_build_by_field(queries)

View File

@ -15,7 +15,14 @@ from api.models.cmdb import CI
class Search(object): class Search(object):
def __init__(self, root_id, level=1, query=None, fl=None, facet_field=None, page=1, count=None, sort=None): def __init__(self, root_id,
level=1,
query=None,
fl=None,
facet_field=None,
page=1,
count=None,
sort=None):
self.orig_query = query self.orig_query = query
self.fl = fl self.fl = fl
self.facet_field = facet_field self.facet_field = facet_field
@ -24,22 +31,36 @@ class Search(object):
self.sort = sort or ("ci_id" if current_app.config.get("USE_ES") else None) self.sort = sort or ("ci_id" if current_app.config.get("USE_ES") else None)
self.root_id = root_id self.root_id = root_id
self.level = int(level) self.level = level
def search(self): def search(self):
ci = CI.get_by_id(self.root_id) or abort(404, "CI <{0}> does not exist".format(self.root_id))
ids = [self.root_id] if not isinstance(self.root_id, list) else self.root_id ids = [self.root_id] if not isinstance(self.root_id, list) else self.root_id
for _ in range(0, self.level): cis = [CI.get_by_id(_id) or abort(404, "CI <{0}> does not exist".format(_id)) for _id in ids]
print(rd.get(ids, REDIS_PREFIX_CI_RELATION))
_tmp = list(map(json.loads, filter(lambda x: x is not None, rd.get(ids, REDIS_PREFIX_CI_RELATION) or []))) merge_ids = []
for level in self.level:
ids = [self.root_id] if not isinstance(self.root_id, list) else self.root_id
for _ in range(0, level):
_tmp = list(map(lambda x: list(json.loads(x).keys()),
filter(lambda x: x is not None, rd.get(ids, REDIS_PREFIX_CI_RELATION) or [])))
ids = [j for i in _tmp for j in i] ids = [j for i in _tmp for j in i]
merge_ids.extend(ids)
if not self.orig_query or ("_type:" not in self.orig_query if not self.orig_query or ("_type:" not in self.orig_query
and "type_id:" not in self.orig_query and "type_id:" not in self.orig_query
and "ci_type:" not in self.orig_query): and "ci_type:" not in self.orig_query):
type_ids = CITypeRelationManager.get_child_type_ids(ci.type_id, self.level) type_ids = []
for level in self.level:
for ci in cis:
type_ids.extend(CITypeRelationManager.get_child_type_ids(ci.type_id, level))
type_ids = list(set(type_ids))
if self.orig_query:
self.orig_query = "_type:({0}),{1}".format(";".join(list(map(str, type_ids))), self.orig_query) self.orig_query = "_type:({0}),{1}".format(";".join(list(map(str, type_ids))), self.orig_query)
else:
self.orig_query = "_type:({0})".format(";".join(list(map(str, type_ids))))
if not ids: if not merge_ids:
# cis, counter, total, self.page, numfound, facet_ # cis, counter, total, self.page, numfound, facet_
return [], {}, 0, self.page, 0, {} return [], {}, 0, self.page, 0, {}
@ -50,7 +71,7 @@ class Search(object):
page=self.page, page=self.page,
count=self.count, count=self.count,
sort=self.sort, sort=self.sort,
ci_ids=ids).search() ci_ids=merge_ids).search()
else: else:
return SearchFromDB(self.orig_query, return SearchFromDB(self.orig_query,
fl=self.fl, fl=self.fl,
@ -58,17 +79,27 @@ class Search(object):
page=self.page, page=self.page,
count=self.count, count=self.count,
sort=self.sort, sort=self.sort,
ci_ids=ids).search() ci_ids=merge_ids).search()
def statistics(self): def statistics(self, type_ids):
ids = [self.root_id] if not isinstance(self.root_id, list) else self.root_id ids = [self.root_id] if not isinstance(self.root_id, list) else self.root_id
for l in range(0, self.level): for l in range(0, int(self.level)):
if l == 0: if l == 0:
_tmp = list(map(json.loads, [i or '[]' for i in rd.get(ids, REDIS_PREFIX_CI_RELATION) or []])) _tmp = list(map(lambda x: list(json.loads(x).keys()),
[i or '{}' for i in rd.get(ids, REDIS_PREFIX_CI_RELATION) or []]))
else: else:
for idx, i in enumerate(_tmp): for idx, i in enumerate(_tmp):
if i: if i:
__tmp = list(map(json.loads, filter(lambda x: x is not None, if type_ids and l == self.level - 1:
__tmp = list(
map(lambda x: list({_id: 1 for _id, type_id in json.loads(x).items()
if type_id in type_ids}.keys()),
filter(lambda x: x is not None,
rd.get(i, REDIS_PREFIX_CI_RELATION) or [])))
else:
__tmp = list(map(lambda x: list(json.loads(x).keys()),
filter(lambda x: x is not None,
rd.get(i, REDIS_PREFIX_CI_RELATION) or []))) rd.get(i, REDIS_PREFIX_CI_RELATION) or [])))
_tmp[idx] = [j for i in __tmp for j in i] _tmp[idx] = [j for i in __tmp for j in i]
else: else:

View File

@ -90,7 +90,7 @@ class RoleRelationCRUD(object):
@staticmethod @staticmethod
def delete2(parent_id, child_id): def delete2(parent_id, child_id):
existed = RoleRelation.get_by(parent_id=parent_id, child_id=child_id) existed = RoleRelation.get_by(parent_id=parent_id, child_id=child_id, first=True, to_dict=False)
existed or abort(400, "RoleRelation < {0} -> {1} > does not exist".format(parent_id, child_id)) existed or abort(400, "RoleRelation < {0} -> {1} > does not exist".format(parent_id, child_id))
existed.soft_delete() existed.soft_delete()

View File

@ -14,6 +14,7 @@ from api.extensions import rd
from api.lib.cmdb.const import CMDB_QUEUE from api.lib.cmdb.const import CMDB_QUEUE
from api.lib.cmdb.const import REDIS_PREFIX_CI from api.lib.cmdb.const import REDIS_PREFIX_CI
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION
from api.models.cmdb import CIRelation
@celery.task(name="cmdb.ci_cache", queue=CMDB_QUEUE) @celery.task(name="cmdb.ci_cache", queue=CMDB_QUEUE)
@ -46,10 +47,13 @@ def ci_delete(ci_id):
@celery.task(name="cmdb.ci_relation_cache", queue=CMDB_QUEUE) @celery.task(name="cmdb.ci_relation_cache", queue=CMDB_QUEUE)
def ci_relation_cache(parent_id, child_id): def ci_relation_cache(parent_id, child_id):
children = rd.get([parent_id], REDIS_PREFIX_CI_RELATION)[0] children = rd.get([parent_id], REDIS_PREFIX_CI_RELATION)[0]
children = json.loads(children) if children is not None else [] children = json.loads(children) if children is not None else {}
children.append(child_id)
rd.create_or_update({parent_id: json.dumps(list(set(children)))}, REDIS_PREFIX_CI_RELATION) cr = CIRelation.get_by(first_ci_id=parent_id, second_ci_id=child_id, first=True, to_dict=False)
if child_id not in children:
children[child_id] = cr.second_ci.type_id
rd.create_or_update({parent_id: json.dumps(children)}, REDIS_PREFIX_CI_RELATION)
current_app.logger.info("ADD ci relation cache: {0} -> {1}".format(parent_id, child_id)) current_app.logger.info("ADD ci relation cache: {0} -> {1}".format(parent_id, child_id))
@ -57,10 +61,11 @@ def ci_relation_cache(parent_id, child_id):
@celery.task(name="cmdb.ci_relation_delete", queue=CMDB_QUEUE) @celery.task(name="cmdb.ci_relation_delete", queue=CMDB_QUEUE)
def ci_relation_delete(parent_id, child_id): def ci_relation_delete(parent_id, child_id):
children = rd.get([parent_id], REDIS_PREFIX_CI_RELATION)[0] children = rd.get([parent_id], REDIS_PREFIX_CI_RELATION)[0]
children = json.loads(children) if children is not None else [] children = json.loads(children) if children is not None else {}
if child_id in children:
children.remove(child_id)
rd.create_or_update({parent_id: json.dumps(list(set(children)))}, REDIS_PREFIX_CI_RELATION) if child_id in children:
children.pop(child_id)
rd.create_or_update({parent_id: json.dumps(children)}, REDIS_PREFIX_CI_RELATION)
current_app.logger.info("DELETE ci relation cache: {0} -> {1}".format(parent_id, child_id)) current_app.logger.info("DELETE ci relation cache: {0} -> {1}".format(parent_id, child_id))

View File

@ -34,7 +34,7 @@ class CIRelationSearchView(APIView):
count = get_page_size(request.values.get("count") or request.values.get("page_size")) count = get_page_size(request.values.get("count") or request.values.get("page_size"))
root_id = request.values.get('root_id') root_id = request.values.get('root_id')
level = request.values.get('level', 1) level = list(map(int, handle_arg_list(request.values.get('level', '1'))))
query = request.values.get('q', "") query = request.values.get('q', "")
fl = handle_arg_list(request.values.get('fl', "")) fl = handle_arg_list(request.values.get('fl', ""))
@ -64,11 +64,12 @@ class CIRelationStatisticsView(APIView):
def get(self): def get(self):
root_ids = list(map(int, handle_arg_list(request.values.get('root_ids')))) root_ids = list(map(int, handle_arg_list(request.values.get('root_ids'))))
level = request.values.get('level', 1) level = request.values.get('level', 1)
type_ids = set(map(int, handle_arg_list(request.values.get('type_ids', []))))
start = time.time() start = time.time()
s = Search(root_ids, level) s = Search(root_ids, level)
try: try:
result = s.statistics() result = s.statistics(type_ids)
except SearchError as e: except SearchError as e:
return abort(400, str(e)) return abort(400, str(e))
current_app.logger.debug("search time is :{0}".format(time.time() - start)) current_app.logger.debug("search time is :{0}".format(time.time() - start))

View File

@ -77,6 +77,7 @@ class PreferenceRelationApiView(APIView):
@role_required(RoleEnum.CONFIG) @role_required(RoleEnum.CONFIG)
@args_required("name") @args_required("name")
@args_required("cr_ids")
def post(self): def post(self):
name = request.values.get("name") name = request.values.get("name")
cr_ids = request.values.get("cr_ids") cr_ids = request.values.get("cr_ids")