ElasticSearch-Ordner-Indexer

Dec 18 2022
Wollten Sie schon immer eine Suche in einem Ordner in Windows durchführen, aber diese Suche nach dieser Datei dauert ewig? Sie haben viele pdf-Dateien oder docx-Dokumente und wissen nicht mehr, wo Sie einen Satz gelesen haben oder suchen nach einem bestimmten Artikel? Sie möchten eine kleine Suchmaschine für Ihre Enterprise-/Non-Enterprise-Anwendung bauen? Wenn Sie sich in einer der oben genannten Situationen befinden oder einfach nur neugierig sind, wie Sie mit ElasticSearch den Inhalt eines Ordners und seiner Unterordner indizieren können, dann ist dieser Artikel genau das Richtige für Sie. Bühnenbild Die Entwicklung für diesen Artikel findet in einer einfachen Windows-Umgebung statt.

Wollten Sie schon immer eine Suche in einem Ordner in Windows durchführen, aber diese Suche nach dieser Datei dauert ewig? Sie haben viele pdf-Dateien oder docx-Dokumente und wissen nicht mehr, wo Sie einen Satz gelesen haben oder suchen nach einem bestimmten Artikel? Sie möchten eine kleine Suchmaschine für Ihre Enterprise-/Non-Enterprise-Anwendung bauen? Wenn Sie sich in einer der oben genannten Situationen befinden oder einfach nur neugierig sind, wie Sie mit ElasticSearch den Inhalt eines Ordners und seiner Unterordner indizieren können, dann ist dieser Artikel genau das Richtige für Sie.

In Szene setzen

Die Entwicklung für diesen Artikel findet auf einer einfachen Windows-Umgebung statt. Zum Zeitpunkt der Erstellung dieses Artikels ist die verwendete ElasticSearch-Version 8.5.3 . Am Ende dieses Artikels haben wir eine voll funktionsfähige GUI-Anwendung erstellt, die anhand der ElasticSearch-Instanz-URL und des Ordnerspeicherorts den Inhalt dieses Ordners in dieser ELK-Instanz indiziert. Der Indexname kann über die Benutzeroberfläche eingegeben werden und kann ein vorhandener oder ein neuer sein.

Elastic Cluster kann wie gewohnt mit mehreren Instanzen eingerichtet werden. Da wir die Instanz jedoch auf einem einzelnen Computer ausführen, können wir die Instanz so konfigurieren, dass sie im Einzelknotenmodus ausgeführt wird . Die Konfigurationsdetails zu diesem Schritt werden in den folgenden Abschnitten beschrieben.

Um die GUI-Anwendung und den Indexer zu erstellen, habe ich mich für Python entschieden , aufgrund seiner umfangreichen Bibliotheken, der kurzen und einfachen Entwicklung. In weniger als 250 Codezeilen ist ein voll funktionsfähiger Indexer mit docx-, pdf- und Textindizierungsfunktionen geschrieben.

ElasticSearch-Setup

Nach dem Herunterladen der ZIP-Datei elasticsearch habe ich sie in ein Verzeichnis entpackt. Der erste Schritt besteht darin, die Sicherheit zu deaktivieren, da die Instanz immer lokal ausgeführt wird. Für Remote-Umgebungen auf Produktionsebene ist es jedoch nie eine gute Praxis, die Sicherheit außer Acht zu lassen. Dann geben wir an, dass der Knoten in localhost ausgeführt werden soll und nur einen einzigen Knoten in localhost erkennen soll. Die Ports werden nicht geändert. (9200 für http, 9300 für tcp)

Die yml-Konfiguration (elasticsearch.yml) finden Sie unten:

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

Da der Zweck dieses Artikels nicht darin besteht, zu diskutieren, wie eine Elasticsearch-Instanz ausgeführt wird, sondern wie sie verwendet wird, um einen Ordner und seine Unterordner mit einer höheren Programmiersprache wie Python zu indizieren, gehen wir zum „lustigen Teil“ über.

Gestaltung der Benutzeroberfläche

Der Elasticsearch-Ordnerindexer war zunächst eine Befehlszeilenanwendung, aber als die Entwicklung fortschritt, entschied ich mich, eine einfache GUI dafür zu erstellen. Um die GUI einzurichten, habe ich tkinter verwendet, aber jede andere Python-GUI-Bibliothek reicht aus.

ElasticSearch-Ordner-Indexer

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()

Wenn auf die Index-Schaltfläche geklickt wird, beginnt die ElasticSearch-Indizierung. Wenn wir jedoch etwas in der Konsole anzeigen möchten (Textobjekt in unserer Implementierung), müssen wir in der Lage sein, Echtzeittext zu senden, der in den Code eingefügt wird. Wenn sowohl die GUI als auch die Indizierungslogik im selben Thread ausgeführt werden, wird die GUI-Verarbeitung erst fortgesetzt, nachdem der gesamte Indizierungsprozess abgeschlossen ist, und wir bleiben mit einer einfrierenden Benutzeroberfläche hängen und fragen uns, was während des gesamten Prozesses falsch ist. Um dies zu vermeiden, führen wir die Indizierung in einem Hintergrund-Thread aus. Dies ist der Zweck der unten definierten Funktion start_combine_in_bg . Das Ziel des Threads ist die aufgerufene auszuführende Funktion, die die Indexfunktion ist.

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

Es gibt zwei Hauptfunktionen, bei denen die Indizierungslogik ausgeführt wird. Die erste ist die Indexfunktion, die auch eine Art Wrapper ist. Seine Logik ist einfach und wird unten gezeigt. Diese Funktion wird aufgerufen, wenn auf die Indexschaltfläche geklickt wird.

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
                }

Schlußbemerkungen

Der ElasticSearch-Ordnerindexer hat viele anwendbare Anwendungsfälle, nicht nur für Entwickler, sondern auch für andere Personen. Diese kann zu einer voll funktionsfähigen Plattform für die Indexierung und Suche von Ordnern ausgebaut werden. Im Moment kann der Suchteil mit einer externen Anwendung oder über den Postboten durchgeführt werden. Es kann verwendet werden, um nach einer bestimmten Datei im Ordner , bestimmten Texten in Dokumenten oder bestimmten Codeteilen in Codierungsdateien zu suchen.

Eine Distribution dieser Anwendung, die auf Windows-PCs ohne vorinstalliertes Python ausgeführt werden kann, finden Sie im folgenden GitHub-Link im dist-Ordner. Um den Ordnerindexer zum Laufen zu bringen, reicht es aus, die ui.exe im ui-Ordner auszuführen.

Github:https://github.com/joanjanku2000/elk-folder-indexer/tree/latest

Vielen Dank für Ihr Interesse, wenn Sie es bis hierher geschafft haben. Prost.

© Joan Janku 2022