Индексатор папок ElasticSearch
Вы когда-нибудь хотели выполнить поиск внутри папки в Windows, но поиск этого файла занимает вечность? У вас много pdf-файлов или документов docx, и вы не помните, где прочитали предложение или ищете конкретную статью? Вы хотите создать небольшую поисковую систему для своего корпоративного/некорпоративного приложения? Если вы оказались в одной из вышеперечисленных ситуаций или вам просто интересно, как использовать ElasticSearch для индексации содержимого папки и ее подпапок, то эта статья для вас.
Настройка сцены
Разработка для этой статьи происходит в простой среде Windows. На момент написания этой статьи использовалась версия ElasticSearch 8.5.3 . В конце этой статьи мы создадим полностью работающее приложение с графическим интерфейсом, которое с учетом URL-адреса экземпляра ElasticSearch и местоположения папки индексирует содержимое этой папки в этот экземпляр ELK. Имя индекса можно ввести из пользовательского интерфейса, оно может быть существующим или новым.
Elastic Cluster можно настроить как обычно с несколькими экземплярами. Однако, поскольку мы запускаем экземпляр внутри одного компьютера, мы можем настроить экземпляр для запуска в режиме одного узла . Детали конфигурации, относящиеся к этому шагу, подробно описаны в следующих разделах.
Для создания приложения с графическим интерфейсом и индексатора я решил использовать Python из-за его богатых библиотек, быстрой и простой разработки. Менее чем в 250 строк кода написан полнофункциональный индексатор с возможностями индексирования docx, pdf и текста.
Настройка эластичного поиска
После загрузки zip elasticsearch я распаковал его в каталог. Первый шаг — отключение безопасности, так как экземпляр всегда будет запускаться локально. Однако для удаленной среды производственного уровня никогда не рекомендуется оставлять безопасность в стороне. Затем мы указываем, что узел должен работать на локальном хосте и обнаруживать только один узел на локальном хосте. Порты не меняются. (9200 для http, 9300 для TCP)
Конфигурацию yml (elasticsearch.yml) можно найти ниже:
xpack.security.enabled: false
xpack.security.enrollment.enabled: false
xpack.security.http.ssl:
enabled: false
xpack.security.transport.ssl:
enabled: false
http.host: 0.0.0.0
network.host: 0.0.0.0
discovery.type: single-node
-Xms4g
-Xmx4g
Поскольку цель этой статьи не в том, чтобы обсудить, как запустить экземпляр elasticsearch, а в том, как использовать его для индексации папки и ее подпапок с помощью языка программирования высокого уровня, такого как Python, давайте перейдем к «интересной части».
Проектирование пользовательского интерфейса
Индексатор папок elasticsearch сначала был приложением командной строки, но по мере разработки я решил создать для него простой графический интерфейс. Для настройки графического интерфейса я использовал tkinter , но подойдет и любая другая библиотека python gui.
import tkinter as tk
from PIL import ImageTk, Image
import tkinter.ttk as ttk
root= tk.Tk('Chipster')
root.title("ElasticSearch Folder Indexer ")
root.resizable(False,False)
canvas1 = tk.Canvas(root, width=400, height=600, relief='raised')
img = ImageTk.PhotoImage(Image.open("elk.png"))
img.width = 0.1
img.height = 0.1
imglabel = tk.Label(root,image=img)
canvas1.create_window(20,30,window = imglabel)
label1 = tk.Label(root, text='ElasticSearch Folder Indexer', background="lightblue")
label1.config(font=('Serif', 14, "bold"))
canvas1.create_window(230, 78, window=label1)
label2 = tk.Label(root, text='Enter the path of the folder to be indexed:')
label2.config(font=('helvetica', 10))
canvas1.create_window(200, 140, window=label2)
entry1 = tk.Entry(root , width=40)
canvas1.create_window(200, 160, window=entry1)
label3 = tk.Label(root, text='ElasticSearch Instance (ex: http://localhost:9200):')
label3.config(font=('helvetica', 10))
canvas1.create_window(200, 200, window=label3)
entry2 = tk.Entry(root,width=40)
canvas1.create_window(200, 220, window=entry2)
label4 = tk.Label(root, text='Index Name:')
label4.config(font=('helvetica', 10))
canvas1.create_window(200, 260, window=label4)
entry3 = tk.Entry(root,width=40)
canvas1.create_window(200, 280, window=entry3)
text = tk.Text(root, height=10,width=40)
progress_label = tk.Label(root,text="")
progress_label.config(font=("helvetica",12,"bold"))
client = ''
connected= False
def connect():
# Code that handles elasticsearch connection
return
def index():
# Code that handles elasticsearch indexing
return
def start_combine_in_bg():
# Threading to show real time logs in the Text Widget
style = ttk.Style()
style.theme_use('alt')
style.configure('TButton', background = 'red', foreground = 'white', width = 10, borderwidth=1, focusthickness=4, focuscolor='none' , font=('Sans serif', 12, "bold"))
style.map('TButton', background=[('active','indianred')])
button1 = ttk.Button(text='Index', command=start_combine_in_bg)
button12 = tk.Button(text='Connect' , command = connect , background="green" , foreground = 'white' ,width = 8, font=('Sans serif', 10, "bold"))
# button1.pack()
canvas1.create_window(250, 340, window=button1)
canvas1.create_window(150, 340, window=button12)
canvas1.create_window(200,370, window=progress_label)
canvas1.create_window(200,490,window=text)
canvas1.pack()
root.mainloop()
При нажатии кнопки Index начинается индексация ElasticSearch. Однако, если мы хотим показать что-то в консоли (текстовый объект в нашей реализации), мы должны иметь возможность отправлять текст в реальном времени для вставки в код. Если и графический интерфейс, и логика индексирования выполняются в одном потоке, то обработка графического интерфейса продолжится только после завершения всего процесса индексирования, и мы застрянем с зависанием пользовательского интерфейса, задаваясь вопросом, что не так в течение всего процесса. Чтобы избежать этого, мы запускаем индексацию в фоновом потоке. Это цель функции start_combine_in_bg , определенной ниже. Целью потока является вызываемая функция, которая должна быть выполнена, то есть индексная функция.
def start_combine_in_bg():
threading.Thread(target=index).start()
def connect():
global client
elk_url = entry2.get()
if elk_url is None or not elk_url:
client = Elasticsearch("http://localhost:9200")
else:
client = Elasticsearch(elk_url)
global connected
connected = True
text.insert(tk.END ,'Existing Indices')
indices = client.indices.get_alias()
for index in indices:
text.insert(tk.END ,'\n'+str(index))
print(indices)
return
Есть 2 основные функции, в которых выполняется логика индексации. Первая — индексная функция, которая также является своего рода оболочкой. Его логика проста и показана ниже. Эта функция будет вызываться при нажатии кнопки индекса.
def index():
if connected is False:
connect()
print("INDEX Clicked")
dir_to_index = entry1.get()
if os.path.isdir(dir_to_index) is False:
all_files = get_files_in_dir('.')
else:
all_files = get_files_in_dir(dir_to_index)
text.insert(tk.END,"TEST" ) # "\n " + "TOTAL FILES:", len( all_files )
try:
resp = helpers.bulk(
client,
yield_docs( all_files,text,entry3.get() , progress_label )
)
text.insert (tk.END,"\nhelpers.bulk() RESPONSE:"+ str(resp))
text.insert (tk.END,"RESPONSE TYPE:"+ str(type(resp)))
except Exception as err:
print("\nhelpers.bulk() ERROR:", str(err))
text.see(tk.END)
return
def yield_docs(all_files, textB: tk.Text , index, label: tk.Label):
if not index or index is None or len(index) == 0:
textB.insert(tk.END ,"\nNo Index Provided")
return
count = 0
for _id, _file in enumerate(all_files):
count+=1
label.configure(text="File:"+str(count) + "/" + str(len(all_files)))
textB.insert(tk.END ,"\nIndexing : " + _file)
textB.see(tk.END)
file_name = _file[ _file.rfind(slash)+1:]
try:
if file_name.lower().endswith(('.html' , '.txt' , '.php' ,'.htm')) is True :
data = get_data_from_text_file( _file )
data = "".join( data )
doc_source = {
"file_name": file_name,
"data": data ,
"file_path":_file
}
elif file_name.lower().endswith((".docx", ".doc")) is True :
pages = getText(_file)
for page in pages:
doc_source = {
"file_name": file_name,
"data": page,
"file_path":_file
}
yield {
"_index": index,
"_source": doc_source
}
elif file_name.lower().endswith((".pdf")) is True :
print("Ends with pdf")
pages = get_text(_file)
for page in pages:
doc_source = {
"file_name": file_name,
"data": page,
"file_path":_file
}
yield {
"_index": index,
"_source": doc_source
}
else:
doc_source = {
"file_name": file_name,
"data": _file,
"file_path":_file
}
yield {
"_index": index,
"_source": doc_source
}
except Exception as err:
print('\nError ',err)
doc_source = {
"file_name": file_name,
"data": _file,
"file_path":_file
}
yield {
"_index": index,
"_source": doc_source
}
Заключительные замечания
Индексатор папок ElasticSearch имеет множество вариантов использования не только для разработчиков, но и для других людей. Это может быть расширено, чтобы стать полнофункциональной платформой для индексации папок и поиска. На данный момент часть поиска можно выполнить с помощью внешнего приложения или через почтальона. Его можно использовать для поиска определенного файла в папке, определенных текстов внутри документов или определенных частей кода внутри файлов кодирования.
Дистрибутив этого приложения, который можно запустить на ПК с Windows без предустановленного Python, можно найти по следующей ссылке GitHub внутри папки dist. Чтобы индексатор папок заработал, достаточно запустить ui.exe внутри папки ui.
Гитхаб:https://github.com/joanjanku2000/elk-folder-indexer/tree/latest
Спасибо за ваш интерес, если вы добрались до сюда. Ваше здоровье.
© Джоан Янку 2022

![В любом случае, что такое связанный список? [Часть 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































