Процент соответствует двум столбцам JSONB, - PullRequest
1 голос
/ 19 июня 2020

Я пытаюсь сравнить два столбца JSONB в таблице, на данный момент это делается в приложении, однако это не позволяет выполнять правильный поиск, фильтрацию и упорядочивание без загрузки всего набора данных. Было бы лучше, если бы мы могли провести это сравнение в базе данных.

Ниже приведен пример данных и расчета.

employer = {
  "autism": "1",
  "social": "1",
  "dementia": "0",
  "domestic": "1",
}

employers_keys = ["autism","social","domestic"]

candidate = {
  "autism": "0",
  "social": "1",
  "dementia": "0",
  "domestic": "1",
}

candidate_keys = ["social","domestic"]

remainder_keys = employer_key - candidate_key = ["autism"]

1-(remainder_keys.length/employer_keys.length) = 1-(1/3) = 2/3 = 66%


Этот процесс довольно тривиален в Ruby , jsonb-> array -> select -> вычисление

Однако я хотел бы выполнить это в SQL или в функции на уровне БД, например,

function compare_ json (работодатель, кандидат), возвращающий десятичное число.

Точнее

 Select candidates.id,
       st_distance_sphere(st_makepoint(employer.long, employer.lat), st_makepoint(candidates.long, candidates.lat)) /
       1000 / 8 * 5 as distance
from (select * from users where id = 8117) employer,
     (select * from users where role_id = 5) candidates
where st_distance_sphere(st_makepoint(employer.long, employer.lat), st_makepoint(candidates.long, candidates.lat)) /
      1000 / 8 * 5 < 25
order by distance

Приведенное выше SQL вычисляет расстояние между одним работодателем и несколькими кандидатами, встроенные запросы работодателя.skills (1 строка), кандидат. навыки (n строк).

Таким образом, результат должен быть ..

Идентификатор кандидата, расстояние, соответствие навыков (работодатель. навыки, кандидаты. навыки)

Как и перед редактированием, любые указания будут приветствоваться.

Ответы [ 2 ]

0 голосов
/ 21 июня 2020

Хорошо, вот что я получил.

CREATE OR REPLACE FUNCTION JSON_COMPARE(employer_json jsonb, candidate_json jsonb, OUT _result numeric)
AS
$$
BEGIN
    select 1 - avg(((d.candidate ->> e.k)::int is distinct from 1)::int)
    into _result
    from (values (employer_json, candidate_json)) d(employer, candidate)
             cross join lateral jsonb_each_text(d.employer) e(k, v)
    where e.v::int = 1;
    RETURN;
END;
$$
    LANGUAGE PLPGSQL;

Это небольшая вариация сверхбыстрого ответа GMB. С помощью нескольких индексов и правильного ограничения размера списка кандидатов мы получаем разумную производительность.

Я новичок в Stack, поэтому мой голос за GMB не отображается, но еще раз спасибо.

0 голосов
/ 19 июня 2020

Вот чистый SQL подход: он работает, превращая объект работодателя в набор записей, а затем выполняя условную агрегацию:

select 1 - avg( ((d.candidate ->> e.k)::int is distinct from 1)::int ) res
from (values(
    '{ "autism": "1", "social": "1", "dementia": "0", "domestic": "1" }'::jsonb,
    '{ "autism": "0", "social": "1", "dementia": "0", "domestic": "1" }'::jsonb
)) d(employer, candidate)
cross join lateral jsonb_each_text(d.employer) e(k, v)
where e.v::int = 1

Вы можете легко перейти к функции, заменив литерал объекты в конструкторе строк values() с параметрами.

Демонстрация на DB Fiddle :

|                    res |
| ---------------------: |
| 0.66666666666666666667 |
...