Indexeur de dossier ElasticSearch
Avez-vous déjà voulu effectuer une recherche dans un dossier sous Windows, mais cette recherche de ce fichier prend une éternité ? Vous avez beaucoup de fichiers pdf ou de documents docx et vous ne savez plus où vous avez lu une phrase ou cherchez un article en particulier ? Vous souhaitez construire un petit moteur de recherche pour votre application entreprise / non entreprise ? Si vous vous trouvez dans l'une des situations ci-dessus, ou si vous êtes simplement curieux de savoir comment utiliser ElasticSearch pour indexer le contenu d'un dossier et de ses sous-dossiers, cet article est pour vous.
Mise en scène
Le développement de cet article se déroule sur un environnement Windows simple. Au moment de la rédaction de cet article, la version d'ElasticSearch utilisée est la 8.5.3 . À la fin de cet article, nous aurons créé une application graphique entièrement fonctionnelle qui, à partir de l'URL de l'instance ElasticSearch et de l'emplacement du dossier, indexe le contenu de ce dossier dans cette instance ELK. Le nom de l'index peut être saisi à partir de l'interface utilisateur et peut être un nom existant ou un nouveau.
Elastic Cluster peut être configuré comme d'habitude avec plusieurs instances. Cependant, puisque nous exécutons l'instance sur un seul ordinateur, nous pouvons configurer l'instance pour qu'elle soit exécutée en mode nœud unique. Les détails de configuration concernant cette étape sont détaillés dans les sections suivantes.
Pour construire l'application graphique et l'indexeur, j'ai décidé d'utiliser Python , en raison de ses bibliothèques riches, de son développement court et facile. En moins de 250 lignes de code, un indexeur entièrement fonctionnel avec des capacités d'indexation docx, pdf et texte est écrit.
Configuration d'ElasticSearch
Après avoir téléchargé le zip elasticsearch , je l'ai extrait dans un répertoire. La première étape consiste à désactiver la sécurité , car l'instance sera toujours exécutée localement. Cependant, pour un environnement distant au niveau de la production, il n'est jamais recommandé de laisser de côté la sécurité. Ensuite, nous spécifions que le nœud doit s'exécuter dans localhost et ne découvrir qu'un seul nœud dans localhost. Les ports ne sont pas modifiés. (9200 pour http , 9300 pour tcp)
La configuration yml (elasticsearch.yml) se trouve ci-dessous :
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
Étant donné que le but de cet article n'est pas de discuter de la façon d'exécuter une instance elasticsearch, mais plutôt de la façon de l'utiliser pour indexer un dossier et ses sous-dossiers à l'aide d'un langage de programmation de haut niveau comme Python, passons à la "partie amusante".
Conception de l'interface utilisateur
L'indexeur de dossiers elasticsearch était d'abord une application en ligne de commande, mais au fur et à mesure du développement, j'ai décidé de créer une interface graphique simple pour cela. Pour configurer l'interface graphique, j'ai utilisé tkinter, mais toute autre bibliothèque d'interface graphique python fera l'affaire.
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()
Lorsque vous cliquez sur le bouton Indexer, l'indexation ElasticSearch commence. Cependant, si nous voulons pouvoir afficher quelque chose dans la console (objet texte dans notre implémentation), nous devons pouvoir envoyer du texte en temps réel à insérer dans le code. Si l'interface graphique et la logique d'indexation s'exécutent dans le même thread, le traitement de l'interface graphique ne se poursuivra qu'après la fin du processus d'indexation et nous serons bloqués avec une interface utilisateur figée se demandant ce qui ne va pas pendant tout le processus. Pour éviter cela, nous exécutons l'indexation dans un thread d'arrière-plan. C'est le but de la fonction start_combine_in_bg définie ci-dessous. La cible du Thread est la fonction appelée à exécuter, qui est la fonction index.
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
Il existe 2 fonctions principales dans lesquelles la logique d'indexation est exécutée. La première est la fonction d'index, qui est aussi une sorte de wrapper. Sa logique est simple et illustrée ci-dessous. Cette fonction sera appelée lorsque le bouton d'index sera cliqué.
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
}
Mot de la fin
L'indexeur de dossiers ElasticSearch a de nombreux cas d'utilisation applicables non seulement pour les développeurs mais aussi pour d'autres personnes. Cela peut être étendu pour devenir une plate-forme entièrement fonctionnelle pour l'indexation et la recherche de dossiers. Pour le moment la partie recherche peut se faire avec une application externe ou via le facteur. Il peut être utilisé pour rechercher un fichier spécifique dans le dossier , des textes spécifiques dans des documents ou des parties de code spécifiques dans des fichiers de codage.
Une distribution de cette application qui peut être exécutée sur des PC Windows sans python préinstallé peut être trouvée dans le lien GitHub suivant, dans le dossier dist. Pour faire fonctionner l'indexeur de dossiers, il suffit d'exécuter ui.exe dans le dossier ui.
Github :https://github.com/joanjanku2000/elk-folder-indexer/tree/latest
Merci de votre intérêt si vous avez réussi jusqu'ici. À votre santé.
© Joan Janku 2022
![Qu'est-ce qu'une liste liée, de toute façon? [Partie 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































