Skip to content

KrozhDev/pcs-final-diplom

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

22 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Дипломная работа. Поисковая система

Класс поискового движка, который способен быстро находить указанное слово среди pdf-файлов, причём ранжировать результаты по количеству вхождений.

Сервер обслуживает входящие запросы с помощью этого движка.

Заготовка проекта

В папке проекта есть папка pdfs - в ней находятся .pdf-файлы, по которым будет искать поисковый движок.

image

Структура движка:

Класс Описание
Main Запуск сервера, обслуживающего поисковые запросы
PageEntry Класс, описывающий один элемент результата одного поиска. Он состоит из имени пдф-файла, номера страницы и количества раз, которое встретилось это слово на ней
SearchEngine Интерфейс, описывающий поисковый движок. Всё что должен уметь делать поисковый движок, это на запрос из слова отвечать списком элементов результата ответа
BooleanSearchEngine Реализация поискового движка, которую вам предстоит написать. Слово Boolean пришло из теории информационного поиска, тк наш движок будет искать в тексте ровно то слово, которое было указано, без использования синонимов и прочих приёмов нечёткого поиска

PDF

Для работы с пдф использовается библиотека com.itextpdf:itext7-core:7.1.15, которая подключена в pom.xml:

        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext7-core</artifactId>
            <version>7.1.15</version>
            <type>pom</type>
        </dependency>

Основные инструменты из этой библиотеки:

  • Чтобы создать объект пдф-документа, нужно указать объект File этого документа следующим классам: var doc = new PdfDocument(new PdfReader(pdf));.
  • Чтобы получить объект одной страницы документа, нужно вызвать doc.getPage(номерСтраницы). Полистайте методы doc чтобы найти способ узнать количество * страниц в документе.
  • Чтобы получить текст со страницы, используйте var text = PdfTextExtractor.getTextFromPage(page);.
  • Чтобы разбить текст на слова (а они в этих документах разделены могут быть не только пробелами), используйте var words = text.split("\\P{IsAlphabetic}+");.

Индексация и поиск

Класс BooleanSearchEngine: поиск (метод search) должен работать быстро, поэтому предпочтём сканирование всех пдф-ок в конструкторе класса с сохранением информации для каждого слова из pdf-файлов. Тогда метод search сможет отрабатывать быстро, по сути возвращать уже посчитанный в конструкторе для слова готовый список-ответ. Т.е. в конструкторе для каждого слова нужно сохранить готовый на возможный будущий запрос ответ в виде List<PageEntry> Для этого можно использовать мапу, где ключом будет слово, а значением - искомый список. Такое предварительное сканирование файлов по которым мы будем искать называется индексацией.

В итоге, нужно реализовать логику индексации в конструкторе. Сканируя каждый пдф-файл класс перебираетего страницы, для каждой страницы извлекаются из неё слова и подсчитывается их количество. После подсчёта, для каждого уникального слова пдф-файла создаёте объект PageEntry и сохраняется в мапу в поле. Поиск регистронезависимый.

Для подсчёта частоты слов используется следующий приём:

Map<String, Integer> freqs = new HashMap<>(); // мапа, где ключом будет слово, а значением - частота
for (var word : words) { // перебираем слова
    if (word.isEmpty()) {
        continue;
    }
    word = word.toLowerCase();
    freqs.put(word, freqs.getOrDefault(word, 0) + 1);
}

Списки ответов для каждого слова отсортированы в порядке уменьшения поля count. Для этого класс PageEntry реализует интерфейс Comparable.

Вот пример работы на слове "бизнес":

[PageEntry{pdf=Этапы оценки проекта_ понятия, методы и полезные инструменты.pdf, page=12, count=6}, PageEntry{pdf=Этапы оценки проекта_ понятия, методы и полезные инструменты.pdf, page=4, count=3}, PageEntry{pdf=Этапы оценки проекта_ понятия, методы и полезные инструменты.pdf, page=5, count=3}, PageEntry{pdf=1. DevOps_MLops.pdf, page=5, count=2}, PageEntry{pdf=Что такое блокчейн.pdf, page=1, count=2}, PageEntry{pdf=Что такое блокчейн.pdf, page=3, count=2}, PageEntry{pdf=Этапы оценки проекта_ понятия, методы и полезные инструменты.pdf, page=2, count=1}, PageEntry{pdf=Этапы оценки проекта_ понятия, методы и полезные инструменты.pdf, page=11, count=1}, PageEntry{pdf=1. DevOps_MLops.pdf, page=3, count=1}, PageEntry{pdf=1. DevOps_MLops.pdf, page=4, count=1}, PageEntry{pdf=Что такое блокчейн.pdf, page=2, count=1}, PageEntry{pdf=Что такое блокчейн.pdf, page=4, count=1}, PageEntry{pdf=Что такое блокчейн.pdf, page=5, count=1}, PageEntry{pdf=Что такое блокчейн.pdf, page=7, count=1}, PageEntry{pdf=Что такое блокчейн.pdf, page=9, count=1}, PageEntry{pdf=Продвижение игр.pdf, page=7, count=1}, PageEntry{pdf=Как управлять рисками IT-проекта.pdf, page=2, count=1}]

Сервер

В main запускается сервер, слушающий порт 8989, к которому происходят подключения и на входной поток подается одно слово (обозначим как word), отвечать результатом вызова метода search(word), но в виде JSON-текста.

Простой сервер
      try (ServerSocket serverSocket = new ServerSocket(8989);) { // стартуем сервер один(!) раз
          while (true) { // в цикле(!) принимаем подключения
              try (
                      Socket socket = serverSocket.accept();
                      BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                      PrintWriter out = new PrintWriter(socket.getOutputStream());
                  ) {
                  // обработка одного подключения
              }
          }
      } catch (IOException e) {
          System.out.println("Не могу стартовать сервер");
          e.printStackTrace();
      }

Пример ответа на запрос:

[
  {
    "pdfName": "Этапы оценки проекта_ понятия, методы и полезные инструменты.pdf",
    "page": 12,
    "count": 6
  },
  {
    "pdfName": "Этапы оценки проекта_ понятия, методы и полезные инструменты.pdf",
    "page": 4,
    "count": 3
  },
  {
    "pdfName": "Этапы оценки проекта_ понятия, методы и полезные инструменты.pdf",
    "page": 5,
    "count": 3
  },
  {
    "pdfName": "1. DevOps_MLops.pdf",
    "page": 5,
    "count": 2
  },
  {
    "pdfName": "Что такое блокчейн.pdf",
    "page": 1,
    "count": 2
  },
  {
    "pdfName": "Что такое блокчейн.pdf",
    "page": 3,
    "count": 2
  },
  {
    "pdfName": "Этапы оценки проекта_ понятия, методы и полезные инструменты.pdf",
    "page": 2,
    "count": 1
  },
  {
    "pdfName": "Этапы оценки проекта_ понятия, методы и полезные инструменты.pdf",
    "page": 11,
    "count": 1
  },
  {
    "pdfName": "1. DevOps_MLops.pdf",
    "page": 3,
    "count": 1
  },
  {
    "pdfName": "1. DevOps_MLops.pdf",
    "page": 4,
    "count": 1
  },
  {
    "pdfName": "Что такое блокчейн.pdf",
    "page": 2,
    "count": 1
  },
  {
    "pdfName": "Что такое блокчейн.pdf",
    "page": 4,
    "count": 1
  },
  {
    "pdfName": "Что такое блокчейн.pdf",
    "page": 5,
    "count": 1
  },
  {
    "pdfName": "Что такое блокчейн.pdf",
    "page": 7,
    "count": 1
  },
  {
    "pdfName": "Что такое блокчейн.pdf",
    "page": 9,
    "count": 1
  },
  {
    "pdfName": "Продвижение игр.pdf",
    "page": 7,
    "count": 1
  },
  {
    "pdfName": "Как управлять рисками IT-проекта.pdf",
    "page": 2,
    "count": 1
  }
]

Дополнительно

Метод запроса теперь принимает не слово, а полноценный запрос из нескольких слов. Всё также возвращается в качестве результата поиска список из PageEntry, только count в нём должен теперь содержать суммарное количество раз, которое встретилось любое из слов запроса. При этом слова из списка стоп-слов должны никак не влиять на запрос (т.е. игнорироваться), тк их встречаемость в запросе никакой информационной нагрузки не несёт (в нашем булевом поиске).

About

Pdf searching engine

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Java 100.0%