Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

query.py 6.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. # -*- coding: utf-8 -*-
  2. import json
  3. import math
  4. import re
  5. import logging
  6. import copy
  7. from elasticsearch_dsl import Q
  8. from rag.nlp import huqie, term_weight, synonym
  9. class EsQueryer:
  10. def __init__(self, es):
  11. self.tw = term_weight.Dealer()
  12. self.es = es
  13. self.syn = synonym.Dealer(None)
  14. self.flds = ["ask_tks^10", "ask_small_tks"]
  15. @staticmethod
  16. def subSpecialChar(line):
  17. return re.sub(r"([:\{\}/\[\]\-\*\"\(\)\|~\^])", r"\\\1", line).strip()
  18. @staticmethod
  19. def isChinese(line):
  20. arr = re.split(r"[ \t]+", line)
  21. if len(arr) <= 3:
  22. return True
  23. e = 0
  24. for t in arr:
  25. if not re.match(r"[a-zA-Z]+$", t):
  26. e += 1
  27. return e * 1. / len(arr) >= 0.7
  28. @staticmethod
  29. def rmWWW(txt):
  30. patts = [
  31. (r"是*(什么样的|哪家|一下|那家|啥样|咋样了|什么时候|何时|何地|何人|是否|是不是|多少|哪里|怎么|哪儿|怎么样|如何|哪些|是啥|啥是|啊|吗|呢|吧|咋|什么|有没有|呀)是*", ""),
  32. (r"(^| )(what|who|how|which|where|why)('re|'s)? ", " "),
  33. (r"(^| )('s|'re|is|are|were|was|do|does|did|don't|doesn't|didn't|has|have|be|there|you|me|your|my|mine|just|please|may|i|should|would|wouldn't|will|won't|done|go|for|with|so|the|a|an|by|i'm|it's|he's|she's|they|they're|you're|as|by|on|in|at|up|out|down)", " ")
  34. ]
  35. for r, p in patts:
  36. txt = re.sub(r, p, txt, flags=re.IGNORECASE)
  37. return txt
  38. def question(self, txt, tbl="qa", min_match="60%"):
  39. txt = re.sub(
  40. r"[ \r\n\t,,。??/`!!&]+",
  41. " ",
  42. huqie.tradi2simp(
  43. huqie.strQ2B(
  44. txt.lower()))).strip()
  45. txt = EsQueryer.rmWWW(txt)
  46. if not self.isChinese(txt):
  47. tks = huqie.qie(txt).split(" ")
  48. q = copy.deepcopy(tks)
  49. for i in range(1, len(tks)):
  50. q.append("\"%s %s\"^2" % (tks[i - 1], tks[i]))
  51. if not q:
  52. q.append(txt)
  53. return Q("bool",
  54. must=Q("query_string", fields=self.flds,
  55. type="best_fields", query=" ".join(q),
  56. boost=1)#, minimum_should_match=min_match)
  57. ), tks
  58. def needQieqie(tk):
  59. if len(tk) < 4:
  60. return False
  61. if re.match(r"[0-9a-z\.\+#_\*-]+$", tk):
  62. return False
  63. return True
  64. qs, keywords = [], []
  65. for tt in self.tw.split(txt)[:256]: # .split(" "):
  66. if not tt:
  67. continue
  68. twts = self.tw.weights([tt])
  69. syns = self.syn.lookup(tt)
  70. logging.info(json.dumps(twts, ensure_ascii=False))
  71. tms = []
  72. for tk, w in sorted(twts, key=lambda x: x[1] * -1):
  73. sm = huqie.qieqie(tk).split(" ") if needQieqie(tk) else []
  74. sm = [
  75. re.sub(
  76. r"[ ,\./;'\[\]\\`~!@#$%\^&\*\(\)=\+_<>\?:\"\{\}\|,。;‘’【】、!¥……()——《》?:“”-]+",
  77. "",
  78. m) for m in sm]
  79. sm = [EsQueryer.subSpecialChar(m) for m in sm if len(m) > 1]
  80. sm = [m for m in sm if len(m) > 1]
  81. if len(sm) < 2:
  82. sm = []
  83. keywords.append(re.sub(r"[ \\\"']+", "", tk))
  84. tk_syns = self.syn.lookup(tk)
  85. tk = EsQueryer.subSpecialChar(tk)
  86. if tk.find(" ") > 0:
  87. tk = "\"%s\"" % tk
  88. if tk_syns:
  89. tk = f"({tk} %s)" % " ".join(tk_syns)
  90. if sm:
  91. tk = f"{tk} OR \"%s\" OR (\"%s\"~2)^0.5" % (
  92. " ".join(sm), " ".join(sm))
  93. tms.append((tk, w))
  94. tms = " ".join([f"({t})^{w}" for t, w in tms])
  95. if len(twts) > 1:
  96. tms += f" (\"%s\"~4)^1.5" % (" ".join([t for t, _ in twts]))
  97. if re.match(r"[0-9a-z ]+$", tt):
  98. tms = f"(\"{tt}\" OR \"%s\")" % huqie.qie(tt)
  99. syns = " OR ".join(
  100. ["\"%s\"^0.7" % EsQueryer.subSpecialChar(huqie.qie(s)) for s in syns])
  101. if syns:
  102. tms = f"({tms})^5 OR ({syns})^0.7"
  103. qs.append(tms)
  104. flds = copy.deepcopy(self.flds)
  105. mst = []
  106. if qs:
  107. mst.append(
  108. Q("query_string", fields=flds, type="best_fields",
  109. query=" OR ".join([f"({t})" for t in qs if t]), boost=1, minimum_should_match=min_match)
  110. )
  111. return Q("bool",
  112. must=mst,
  113. ), keywords
  114. def hybrid_similarity(self, avec, bvecs, atks, btkss, tkweight=0.3,
  115. vtweight=0.7):
  116. from sklearn.metrics.pairwise import cosine_similarity as CosineSimilarity
  117. import numpy as np
  118. sims = CosineSimilarity([avec], bvecs)
  119. def toDict(tks):
  120. d = {}
  121. if isinstance(tks, str):
  122. tks = tks.split(" ")
  123. for t, c in self.tw.weights(tks):
  124. if t not in d:
  125. d[t] = 0
  126. d[t] += c
  127. return d
  128. atks = toDict(atks)
  129. btkss = [toDict(tks) for tks in btkss]
  130. tksim = [self.similarity(atks, btks) for btks in btkss]
  131. return np.array(sims[0]) * vtweight + \
  132. np.array(tksim) * tkweight, tksim, sims[0]
  133. def similarity(self, qtwt, dtwt):
  134. if isinstance(dtwt, type("")):
  135. dtwt = {t: w for t, w in self.tw.weights(self.tw.split(dtwt))}
  136. if isinstance(qtwt, type("")):
  137. qtwt = {t: w for t, w in self.tw.weights(self.tw.split(qtwt))}
  138. s = 1e-9
  139. for k, v in qtwt.items():
  140. if k in dtwt:
  141. s += v # * dtwt[k]
  142. q = 1e-9
  143. for k, v in qtwt.items():
  144. q += v # * v
  145. #d = 1e-9
  146. # for k, v in dtwt.items():
  147. # d += v * v
  148. return s / q / max(1, math.sqrt(math.log10(max(len(qtwt.keys()), len(dtwt.keys())))))# math.sqrt(q) / math.sqrt(d)