Обработка массива текстов
С помощью парсера можно обрабатывать большие массивы текстовых файлов, источников RSS (Really simple syndication) или каналов в Telegram.
Запуск приложения
Приложения text_parser и news_parser запускаются в Docker-контейнере на сервере робота и обрабатывают заданные массивы текстов. Участники проекта Ф-2 могут получить доступ к серверу разборов, но не могут сами запускать контейнеры на своих компьютерах.
Проверка добавления в базу
Чтобы убедиться, что процесс разбора идёт, нужно задать вопрос к базе. Следующий запрос показывает максимальный идентификатор в таблицах. Если происходит запись в таблицы, то значения запроса при последовательных запусках будут расти.
SELECT 'sentences' base, MAX(id) maxid FROM sentences
UNION ALL
SELECT 'syntax' base, MAX(id) maxid FROM syntax
UNION ALL
SELECT 'facts' base, MAX(id) maxid FROM facts
Возможный результат:
base | maxid |
---|---|
sentences | 2343552 |
syntax | 267514 |
facts | 4860259 |
Последняя запись в базе
Конечно, во время разбора интересно, что именно было добавлено в каждую таблицу. Следующий запрос показывает последнюю добавленную запись в каждой таблице:
-- что происходит?
-- по каждой таблице выводит последний процесс, id последнего элемента и хеш
-- при последовательных запусках позволяет следить за изменениями в базе, проверять, какие таблицы пополняются при разборе
(SELECT 'sentences' base, substring(meta -> 'metadata' -> 'splitter_processor' ->> 'process_id' FOR 8) process, id, substring(hash::text for 5) hash
, 'matcher: "' || (meta -> 'metadata' -> 'morphology_matcher' ->> 'version') ||
'", guesser: "' || (meta -> 'metadata' -> 'morphology_guesser' ->> 'version') ||
'", splitter: "' || (meta -> 'metadata' -> 'splitter_processor' ->> 'version') ||
'", processor: "' || (meta -> 'metadata' -> 'morphology_processor' ->> 'version') || '"' ver
, meta -> 'metadata' -> 'splitter' ->> 'text' sent
, jsonb_pretty(sentences.sentence) cont
FROM sentences
ORDER BY id DESC LIMIT 1)
UNION ALL
(SELECT 'syntax' base, substring(meta -> 'metadata' -> 'splitter_processor' ->> 'process_id' FOR 8) process, id, substring(hash::text for 5) hash
, 'syntax: "' || (meta -> 'metadata' -> 'syntax_processor' ->> 'version') ||
'", weighting: "' || (meta -> 'metadata' -> 'weighting_processor' ->> 'version') || '"' ver
, meta -> 'metadata' -> 'splitter' ->> 'text' sent
, jsonb_pretty(syntax.tree) cont
FROM syntax
ORDER BY id DESC LIMIT 1)
UNION ALL
(SELECT 'facts' base, substring(meta -> 'metadata' -> 'splitter_processor' ->> 'process_id' FOR 8) process, id, substring(hash::text for 5) hash
, 'syntax: "' || (meta -> 'metadata' -> 'syntax_processor' ->> 'version') ||
'", weighting: "' || (meta -> 'metadata' -> 'weighting_processor' ->> 'version') ||
'", semantics: "' || (meta -> 'metadata' -> 'semantics_processor' ->> 'version') || '"' ver
, F.meta -> 'metadata' -> 'splitter' ->> 'text' sent
, '' cont
FROM facts F
ORDER BY id DESC LIMIT 1)
UNION ALL
(SELECT 'fact_valencies' base, substring(meta -> 'metadata' -> 'splitter_processor' ->> 'process_id' FOR 8) process, FV.id, substring(hash::text for 5) hash
, '' ver
, F.meta -> 'metadata' -> 'splitter' ->> 'text' sent
, '' cont
FROM fact_valencies FV
LEFT JOIN facts F ON FV.fact = F.id
ORDER BY FV.id DESC LIMIT 1)
UNION ALL
(SELECT 'referents' base, substring(meta -> 'metadata' -> 'splitter_processor' ->> 'process_id' FOR 8) process, R.id, substring(hash::text for 5) hash
, '' ver
, F.meta -> 'metadata' -> 'splitter' ->> 'text' sent
, '' cont
FROM referents R
LEFT JOIN fact_valencies FV ON R.id = FV.referent
LEFT JOIN facts F ON FV.fact = F.id
ORDER BY R.id DESC LIMIT 1)
UNION ALL
(SELECT 'fact_markers' base, substring(meta -> 'metadata' -> 'splitter_processor' ->> 'process_id' FOR 8) process, FM.id, substring(hash::text for 5) hash
, '' ver
, F.meta -> 'metadata' -> 'splitter' ->> 'text' sent
, '' cont
FROM fact_markers FM
LEFT JOIN fact_valencies FV ON FM.valency = FV.id
LEFT JOIN facts F ON FV.fact = F.id
ORDER BY FM.id DESC LIMIT 1)
Возможный результат:
base | process | id | hash | ver | sent | cont |
---|---|---|---|---|---|---|
sentences | 51f949d3 | 2343548 | 2EE67 | matcher: “1.3-ak”, guesser: “1.0:WEB”, splitter: “1.2”, processor: “1.0:1.03-lz” | осуществление проекта будет разбито на два этапа. | { “tokens”: [ { “order”: 1,… |
syntax | 4e742066 | 267514 | FD4FF | syntax: “1.6-D30:1.8 ak”, weighting: “2.3-R0,0100T512:0.5:ak” | средняя скорость движения трамваев на маршруте составит более 25 километров в час. | { “nodes”: [ { “order”: 1,… |
facts | 51f949d3 | 4860251 | 12D84 | syntax: “1.6-D30:1.8 ak”, weighting: “2.3-R0,0100T512:0.5:ak”, semantics: “1.0:1.02-AK” | осуществление проекта будет разбито на два этапа. | |
fact_valencies | 51f949d3 | 8332498 | 12D84 | осуществление проекта будет разбито на два этапа. | ||
referents | 51f949d3 | 8332498 | 12D84 | осуществление проекта будет разбито на два этапа. | ||
fact_markers | 51f949d3 | 20685475 | 12D84 | осуществление проекта будет разбито на два этапа. |
Здесь для каждой таблицы (sentences, synatx и т. д.) видно, какой процесс (например, 51f949d3) добавил запись с каким идентификатором (например, 2343548), на каких версиях компонентов был произведён разбор (matcher: “1.3-ak”, guesser: “1.0:WEB”…), какое предложение разбиралось (осуществление проекта будет…), как выглядит запись предложения, разделённого на словоформы, и как выглядит синтаксическое дерево в формате treeJSON ({ “nodes”: [ { “nodes”: [ { …)
Предложения из последнего разбора
Чтобы просмотреть предложения, которые были проанализированы в рамках последнего запуска, можно использовать следующий запрос:
SELECT N1.*, B.unknown_token FROM
(
SELECT
substring(A.procid FOR 8) -- преобразуем json в строку и обрезаем
, Se.id sentID
, Se.meta ->'metadata'->'splitter'->>'text' sent
, COUNT(Sy.id) treeCNT
, COUNT(F.id) fCNT
-- проверяем, что для предложения сохранено дерево или факт, значит, предложение разобрано
, CASE (COUNT(Sy.id) > 0) OR (COUNT(F.id) > 0) WHEN True THEN 1 ELSE NULL END oksent
FROM
( SELECT meta -> 'metadata' -> 'splitter_processor' ->> 'process_id' procid, MAX(id) maxid
FROM sentences
GROUP BY procid
ORDER BY maxid DESC
LIMIT 1 -- взять последний разбор
) A --берем последний procid
JOIN sentences Se ON A.procid = Se.meta ->'metadata'->'splitter_processor'->>'process_id'
LEFT JOIN facts F ON F.meta ->'metadata'->'morphology'->>'id' = Se.meta ->'metadata'->'morphology'->>'id'
LEFT JOIN syntax Sy ON Sy.meta ->'metadata'->'morphology'->>'id' = Se.meta ->'metadata'->'morphology'->>'id'
GROUP BY
A.procid
, Se.id
ORDER BY --oksent,
sentid DESC
) N1
LEFT JOIN
(
select X.token->'token'->>'token_text' unknown_token, X.sentID
from (
select jsonb_array_elements(sentence -> 'tokens') token, id sentID from sentences
) X
where X.token->'token'->>'token_type' = 'Unknown'
) B ON B.sentID = N1.sentID
ORDER BY N1.sentID DESC
Возможный результат:
substring | sentid | sent | treecnt | fcnt | oksent | unknown_token |
---|---|---|---|---|---|---|
51f949d3 | 2343552 | средняя скорость движения трамваев на маршруте составит более 25 километров в час. | 0 | 4 | 1 | [NULL] |
51f949d3 | 2343551 | предполагается, что первая часть маршрута будет закончена к 2023 году. | 0 | 4 | 1 | [NULL] |
51f949d3 | 2343550 | на втором этапе будут выстроены 14 километров путей от петергофского шоссе до города петергофа. | 0 | 0 | [NULL] | [NULL] |
51f949d3 | 2343549 | сначала будет построена ветка протяженностью шесть километров от существующих трамвайных путей— по улицам маршала казакова, маршала захарова, балтийскому бульвару, проспекту героев до петергофского шоссе. | 0 | 0 | [NULL] | [NULL] |
Таблица показывает, что в рамках процесса 51f949d3 были проанализированы несколько предложений. Для некоторых из них были успешно построены факты (fcnt), это значит, что предложение было разобрано. Если в процессе разбора были найдены незнакомые слова, они появятся в столбце unknown_token. Но при использовании компонента «гессер» неизвестных слов не остаётся, для любого слова предлагается та или иная гипотеза разбора.
Теперь можно переходить к анализу результатов разбора в базе данных