Подсчет вхождений строк с помощью ArangoDB AQL - PullRequest
1 голос
/ 14 января 2020

Чтобы подсчитать количество объектов, содержащих указанное значение атрибута c, я могу сделать что-то вроде:

FOR t IN thing
  COLLECT other = t.name = "Other" WITH COUNT INTO otherCount
  FILTER other != false
  RETURN otherCount

Но как мне посчитать три других вхождения в одном запросе, не приводя к выполнению подзапросов? через один и тот же набор данных несколько раз?

Я пробовал что-то вроде:

FOR t IN thing
  COLLECT 
    other = t.name = "Other",
    some = t.name = "Some",
    thing = t.name = "Thing"
  WITH COUNT INTO count
  RETURN {
   other, some, thing,
   count
  }

Но я не могу понять результаты: я должен подходить к этому неправильно?

1 Ответ

2 голосов
/ 16 января 2020

Разделить и считать

Вы можете разделить строку по фразе и вычесть 1 из числа. Это работает для любой подстроки, что, с другой стороны, означает, что она не учитывает границы слов.

LET things = [
    {name: "Here are SomeSome and Some Other Things, brOther!"},
    {name: "There are no such substrings in here."},
    {name: "some-Other-here-though!"}
]

FOR t IN things
  LET Some = LENGTH(SPLIT(t.name, "Some"))-1
  LET Other = LENGTH(SPLIT(t.name, "Other"))-1
  LET Thing = LENGTH(SPLIT(t.name, "Thing"))-1
  RETURN {
   Some, Other, Thing
}

Результат:

[
  {
    "Some": 3,
    "Other": 2,
    "Thing": 1
  },
  {
    "Some": 0,
    "Other": 0,
    "Thing": 0
  },
  {
    "Some": 0,
    "Other": 1,
    "Thing": 0
  }
]

Вы можете использовать SPLIT(LOWER(t.name), LOWER("...")), чтобы сделать регистр нечувствительный.

СБОР слова

Функцию TOKENS() можно использовать для разделения ввода на массивы слов, которые затем можно группировать и подсчитывать. Обратите внимание, что я немного изменил ввод. Ввод "SomeSome" не будет засчитан, потому что "somesome" != "some" (этот вариант основан на слове, а не на подстроке).

LET things = [
    {name: "Here are SOME some and Some Other Things. More Other!"},
    {name: "There are no such substrings in here."},
    {name: "some-Other-here-though!"}
]
LET whitelist = TOKENS("Some Other Things", "text_en")

FOR t IN things
  LET whitelisted = (FOR w IN TOKENS(t.name, "text_en") FILTER w IN whitelist RETURN w)
  LET counts = MERGE(FOR w IN whitelisted
    COLLECT word = w WITH COUNT INTO count
    RETURN { [word]: count }
  )
  RETURN {
    name: t.name,
    some: counts.some || 0,
    other: counts.other || 0,
    things: counts.things ||0
  }

Результат:

[
  {
    "name": "Here are SOME some and Some Other Things. More Other!",
    "some": 3,
    "other": 2,
    "things": 0
  },
  {
    "name": "There are no such substrings in here.",
    "some": 0,
    "other": 0,
    "things": 0
  },
  {
    "name": "some-Other-here-though!",
    "some": 1,
    "other": 1,
    "things": 0
  }
]

Это использует подзапрос для COLLECT, в противном случае он будет считать общее количество вхождений для всего ввода.

Шаг белого списка не является строго необходимым, вы также можете позволить ему подсчитать все слова. Для больших входных строк это может сэкономить некоторую память, чтобы не делать этого для слов, которые вас не интересуют в любом случае.

Возможно, вы захотите создать отдельный Анализатор с отключенным основанием для языка, если вы хочу точно подобрать слова. Вы также можете отключить нормализация ("accent": true, "case": "none"). В качестве альтернативы можно использовать REGEX_SPLIT() для типичных пробельных символов и знаков препинания для более простого токенизации, но это зависит от вашего варианта использования.

Другие решения

Не знаю Не думаю, что можно подсчитать каждый входной объект независимо с помощью COLLECT без подзапроса, если только вам не нужен общий счет.

Разделение - это немного хак, но вы можете заменить SPLIT () на REGEX_SPLIT () и Оберните поисковые фразы в \b, чтобы они совпадали только в том случае, если границы слов находятся с обеих сторон. Тогда он должен соответствовать только словам (более или менее):

LET things = [
    {name: "Here are SomeSome and Some Other Things, brOther!"},
    {name: "There are no such substrings in here."},
    {name: "some-Other-here-though!"}
]

FOR t IN things
  LET Some = LENGTH(REGEX_SPLIT(t.name, "\\bSome\\b"))-1
  LET Other = LENGTH(REGEX_SPLIT(t.name, "\\bOther\\b"))-1
  LET Thing = LENGTH(REGEX_SPLIT(t.name, "\\bThings\\b"))-1
  RETURN {
   Some, Other, Thing
}

Результат:

[
  {
    "Some": 1,
    "Other": 1,
    "Thing": 1
  },
  {
    "Some": 0,
    "Other": 0,
    "Thing": 0
  },
  {
    "Some": 0,
    "Other": 1,
    "Thing": 0
  }
]

Более элегантным решением будет использование ArangoSearch для подсчета слов, но это не так есть функция, позволяющая вам узнать, как часто слово встречается. Он может отслеживать это уже внутри (функция анализатора «частота» ), но на данный момент он определенно не отображается.

...