Go/Golang ist eine meiner Lieblingssprachen; Ich liebe den Minimalismus und wie sauber er ist, er ist syntaktisch sehr kompakt und bemüht sich sehr, die Dinge einfach zu halten (ich bin ein großer Fan des KISS-Prinzips).
Eine der größten Herausforderungen, vor denen ich in letzter Zeit stand, war der Aufbau einer schnellen Suchmaschine. Natürlich gibt es Optionen wie SOLR und ElasticSearch; Beide funktionieren wirklich gut und sind hoch skalierbar. Allerdings musste ich die Suche vereinfachen, indem ich sie schneller und einfacher bereitzustellen und mit wenig bis gar keinen Abhängigkeiten ausstatte.
Ich musste so weit optimieren, dass ich schnell Ergebnisse liefern konnte, damit sie neu eingestuft werden konnten. Obwohl C/Rust hierfür gut geeignet sein könnte, schätze ich Entwicklungsgeschwindigkeit und Produktivität. Golang ist meiner Meinung nach das Beste aus beiden Welten.
In diesem Artikel werde ich ein einfaches Beispiel dafür durchgehen, wie Sie mit Go Ihre eigene Suchmaschine erstellen können. Sie werden überrascht sein: Es ist gar nicht so kompliziert, wie Sie vielleicht denken.
Golang: Python über Steroide
Ich weiß nicht warum, aber Golang fühlt sich in gewisser Weise wie Python an. Die Syntax ist sehr leicht zu verstehen, vielleicht liegt es am Fehlen von Semikolons und Klammern überall oder am Fehlen hässlicher Try-Catch-Anweisungen. Vielleicht ist es der tolle Go-Formatierer, ich weiß es nicht.
Wie auch immer, da Golang eine einzige eigenständige Binärdatei generiert, ist die Bereitstellung auf jedem Produktionsserver super einfach. Sie gehen einfach zum „Build“ und tauschen die ausführbare Datei aus.
Das ist genau das, was ich brauchte.
Bleve?
Nein, das ist kein Tippfehler? Bleve ist eine leistungsstarke, benutzerfreundliche und sehr flexible Suchbibliothek für Golang.
Während Sie als Go-Entwickler im Allgemeinen Drittanbieterpakete wie die Pest meiden; Manchmal ist es sinnvoll, ein Paket eines Drittanbieters zu verwenden. Bleve ist schnell, gut gestaltet und bietet ausreichend Wert, um seine Verwendung zu rechtfertigen.
Außerdem ist hier der Grund, warum ich „Bleve“ habe:
Eigenständig, einer der großen Vorteile von Golang ist die einzelne Binärdatei, daher wollte ich dieses Gefühl beibehalten und keine externe Datenbank oder einen externen Dienst zum Speichern und Abfragen von Dokumenten benötigen. Bleve läuft im Speicher und schreibt auf die Festplatte, ähnlich wie SQLite.
Einfach zu erweitern. Da es sich nur um Go-Code handelt, kann ich die Bibliothek bei Bedarf problemlos optimieren oder in meiner Codebasis erweitern.
Schnell: Suchergebnisse in 10 Millionen Dokumenten dauern nur 50–100 ms, einschließlich Filterung.
Facettierung: Sie können keine moderne Suchmaschine ohne ein gewisses Maß an Facettierungsunterstützung erstellen. Bleve bietet volle Unterstützung für gängige Facettentypen: wie Bereiche oder einfache Kategorieanzahlen.
Schnelle Indizierung: Bleve ist etwas langsamer als SOLR. SOLR kann 10 Millionen Dokumente in 30 Minuten indizieren, während Bleve über eine Stunde benötigt, aber eine Stunde oder so ist für meine Bedürfnisse immer noch ziemlich anständig und schnell genug.
Gute Qualitätsergebnisse. Bleve schneidet mit den Keyword-Ergebnissen gut ab, aber auch einige semantische Suchvorgänge funktionieren in Bleve sehr gut.
Schneller Start: Wenn Sie neu starten oder ein Update bereitstellen müssen, dauert der Neustart von Bleve nur Millisekunden. Es gibt keine Blockierung von Lesevorgängen, um den Index im Speicher neu aufzubauen, sodass die Suche im Index nur Millisekunden nach einem Neustart ohne Probleme möglich ist.
Einen Index einrichten?
In Bleve kann man sich einen „Index“ als eine Datenbanktabelle oder eine Sammlung (NoSQL) vorstellen. Im Gegensatz zu einer regulären SQL-Tabelle müssen Sie nicht jede einzelne Spalte angeben, sondern können für die meisten Anwendungsfälle grundsätzlich mit dem Standardschema auskommen.
Um einen Bleve-Index zu initialisieren, können Sie Folgendes tun:
mappings := bleve.NewIndexMapping() index, err = bleve.NewUsing("/some/path/index.bleve", mappings, "scorch", "scorch", nil) if err != nil { log.Fatal(err) }
Bleve unterstützt einige verschiedene Indextypen, aber nach langem Hin und Her habe ich herausgefunden, dass der Indextyp „scorch“ die beste Leistung bietet. Wenn Sie die letzten drei Argumente nicht übergeben, verwendet Bleve standardmäßig BoltDB.
Dokumente hinzufügen
Das Hinzufügen von Dokumenten zu Bleve ist ein Kinderspiel. Grundsätzlich können Sie jede Art von Struktur im Index speichern:
type Book struct { ID int `json:"id"` Name string `json:"name"` Genre string `json:"genre"` } b := Book{ ID: 1234, Name: "Some creative title", Genre: "Young Adult", } idStr := fmt.Sprintf("%d", b.ID) // index(string, interface{}) index.index(idStr, b)
Wenn Sie eine große Menge an Dokumenten indizieren, ist es besser, die Stapelverarbeitung zu verwenden:
// You would also want to check if the batch exists already // - so that you don't recreate it. batch := index.NewBatch() if batch.Size() >= 1000 { err := index.Batch(batch) if err != nil { // failed, try again or log etc... } batch = index.NewBatch() } else { batch.index(idStr, b) }
Wie Sie feststellen werden, wird eine komplexe Aufgabe wie das Stapeln von Datensätzen und deren Schreiben in den Index durch die Verwendung von „index.NewBatch“ vereinfacht, das einen Container zum temporären Indizieren von Dokumenten erstellt.
Danach überprüfen Sie einfach die Größe, während Sie eine Schleife durchlaufen, und leeren den Index, sobald Sie die Stapelgrößenbeschränkung erreicht haben.
Durchsuchen des Index
Bleve stellt mehrere verschiedene Parser für Suchanfragen zur Verfügung, aus denen Sie je nach Ihren Suchanforderungen auswählen können. Um diesen Artikel kurz und bündig zu halten, verwende ich einfach den Standard-Query-String-Parser.
searchParser := bleve.NewQueryStringQuery("chicken reciepe books") maxPerPage := 50 ofsset := 0 searchRequest := bleve.NewSearchRequestOptions(searchParser, maxPerPage, offset, false) // By default bleve returns just the ID, here we specify // - all the other fields we would like to return. searchRequest.Fields = []string{"id", "name", "genre"} searchResults, err := index.Search(searchResult)
Mit nur diesen wenigen Zeilen verfügen Sie jetzt über eine leistungsstarke Suchmaschine, die bei geringem Speicher- und Ressourcenbedarf gute Ergebnisse liefert.
Hier ist eine JSON-Darstellung der Suchergebnisse. „Hits“ enthält die passenden Dokumente:
{ "status": { "total": 5, "failed": 0, "successful": 5 }, "request": {}, "hits": [], "total_hits": 19749, "max_score": 2.221337297308545, "took": 99039137, "facets": null }
Facettieren
Wie bereits erwähnt, bietet Bleve sofort vollständige Facettierungsunterstützung, ohne dass diese in Ihrem Schema eingerichtet werden muss. Um beispielsweise das Buch „Genre“ zu facettieren, können Sie Folgendes tun:
//... build searchRequest -- see previous section. // Add facets genreFacet := bleve.NewFacetRequest("genre", 50) searchRequest.AddFacet("genre", genreFacet) searchResults, err := index.Search(searchResult)
Wir erweitern unseren searchRequest von früher mit nur 2 Codezeilen. Die „NewFacetRequest“ akzeptiert zwei Argumente:
Feld: das Feld in unserem Index, auf das (String) facettiert werden soll.
Größe: die Anzahl der zu zählenden Einträge (Ganzzahl). In unserem Beispiel werden also nur die ersten 50 Genres gezählt.
Das Obige füllt nun die „Facetten“ in unseren Suchergebnissen aus.
Als nächstes fügen wir einfach unsere Facette zur Suchanfrage hinzu. Dazu gehören ein „Facettenname“ und die tatsächliche Facette. „Facettenname“ ist der „Schlüssel“, unter dem Sie diese Ergebnismenge in unseren Suchergebnissen finden.
Erweiterte Abfragen und Filterung
Mit dem Parser „QueryStringQuery“ können Sie zwar einiges erreichen; Manchmal benötigen Sie komplexere Abfragen wie „eine muss übereinstimmen“, bei der Sie einen Suchbegriff mit mehreren Feldern abgleichen und Ergebnisse zurückgeben möchten, solange mindestens ein Feld übereinstimmt.
Sie können dazu die Abfragetypen „Disjunktion“ und „Konjunktion“ verwenden.
Konjunktionsabfrage: Im Grunde ermöglicht es Ihnen, mehrere Abfragen zu einer einzigen riesigen Abfrage zu verketten. Alle untergeordneten Abfragen müssen mit mindestens einem Dokument übereinstimmen.
Disjunktionsabfrage: Damit können Sie die oben erwähnte „Eins muss übereinstimmen“-Abfrage durchführen. Sie können eine x-Anzahl an Abfragen übergeben und festlegen, wie viele untergeordnete Abfragen mit mindestens einem Dokument übereinstimmen müssen.
Beispiel für eine Disjunktionsabfrage:
mappings := bleve.NewIndexMapping() index, err = bleve.NewUsing("/some/path/index.bleve", mappings, "scorch", "scorch", nil) if err != nil { log.Fatal(err) }
Ähnlich wie wir zuvor „searchParser“ verwendet haben, können wir jetzt die „Disjunction Query“ an den Konstruktor für unsere „searchRequest“ übergeben.
Obwohl es nicht genau dasselbe ist, ähnelt es dem folgenden SQL:
type Book struct { ID int `json:"id"` Name string `json:"name"` Genre string `json:"genre"` } b := Book{ ID: 1234, Name: "Some creative title", Genre: "Young Adult", } idStr := fmt.Sprintf("%d", b.ID) // index(string, interface{}) index.index(idStr, b)
Sie können auch anpassen, wie unscharf die Suche sein soll, indem Sie „query.Fuzziness=[0 oder 1 oder 2]“ festlegen
Beispiel für eine Konjunktionsabfrage:
// You would also want to check if the batch exists already // - so that you don't recreate it. batch := index.NewBatch() if batch.Size() >= 1000 { err := index.Batch(batch) if err != nil { // failed, try again or log etc... } batch = index.NewBatch() } else { batch.index(idStr, b) }
Sie werden feststellen, dass die Syntax sehr ähnlich ist. Sie können die Abfragen „Konjunktion“ und „Disjunktion“ grundsätzlich austauschbar verwenden.
Dies wird in SQL etwa wie folgt aussehen:
searchParser := bleve.NewQueryStringQuery("chicken reciepe books") maxPerPage := 50 ofsset := 0 searchRequest := bleve.NewSearchRequestOptions(searchParser, maxPerPage, offset, false) // By default bleve returns just the ID, here we specify // - all the other fields we would like to return. searchRequest.Fields = []string{"id", "name", "genre"} searchResults, err := index.Search(searchResult)
Zusammenfassend; Verwenden Sie die „Konjunktionsabfrage“, wenn Sie möchten, dass alle untergeordneten Abfragen mit mindestens einem Dokument übereinstimmen, und die „Disjunktionsabfrage“, wenn Sie mit mindestens einer untergeordneten Abfrage, aber nicht unbedingt mit allen untergeordneten Abfragen übereinstimmen möchten.
Sharding
Wenn Sie auf Geschwindigkeitsprobleme stoßen, ermöglicht Bleve auch die Verteilung Ihrer Daten auf mehrere Index-Shards und die anschließende Abfrage dieser Shards in einer Anfrage, zum Beispiel:
{ "status": { "total": 5, "failed": 0, "successful": 5 }, "request": {}, "hits": [], "total_hits": 19749, "max_score": 2.221337297308545, "took": 99039137, "facets": null }
Sharding kann ziemlich komplex werden, aber wie Sie oben sehen, nimmt Bleve einen Großteil des Aufwands ab, da es automatisch alle Indizes und Suchvorgänge „zusammenführt“ und dann Ergebnisse in einem Ergebnissatz zurückgibt, genau so, als ob Sie gesucht hätten ein einzelner Index.
Ich habe Sharding verwendet, um 100 Shards zu durchsuchen. Der gesamte Suchvorgang dauert durchschnittlich nur 100–200 Millisekunden.
Sie können Shards wie folgt erstellen:
//... build searchRequest -- see previous section. // Add facets genreFacet := bleve.NewFacetRequest("genre", 50) searchRequest.AddFacet("genre", genreFacet) searchResults, err := index.Search(searchResult)
Stellen Sie einfach sicher, dass Sie für jedes Dokument eindeutige IDs erstellen oder über eine vorhersehbare Möglichkeit verfügen, Dokumente hinzuzufügen und zu aktualisieren, ohne den Index durcheinander zu bringen.
Eine einfache Möglichkeit, dies zu tun, besteht darin, ein Präfix zu speichern, das den Shard-Namen in Ihrer Quelldatenbank enthält, oder wo auch immer Sie die Dokumente beziehen. So dass Sie jedes Mal, wenn Sie versuchen, etwas einzufügen oder zu aktualisieren, das „Präfix“ nachschlagen, das Ihnen sagt, auf welchem Shard Sie „.index“ aufrufen müssen.
Apropos Aktualisieren: Durch den einfachen Aufruf von „index.index(idstr, struct)“ wird ein vorhandenes Dokument aktualisiert.
Abschluss
Wenn Sie nur diese grundlegende Suchtechnik oben verwenden und sie hinter GIN oder dem standardmäßigen Go-HTTP-Server einfügen, können Sie eine ziemlich leistungsstarke Such-API erstellen und Millionen von Anfragen bedienen, ohne eine komplexe Infrastruktur einführen zu müssen.
Eine Einschränkung allerdings; Bleve bietet jedoch keine Replikation, da Sie diese in eine API einbinden können. Führen Sie einfach einen Cron-Job aus, der aus Ihrer Quelle liest und mithilfe von Goroutinen ein Update auf alle Ihre Bleve-Server „ausspielt“.
Alternativ können Sie das Schreiben auf die Festplatte einfach für ein paar Sekunden sperren und dann die Daten einfach per „rsync“ auf die Slave-Indizes übertragen. Ich rate jedoch davon ab, da Sie wahrscheinlich jedes Mal auch die Go-Binärdatei neu starten müssten .
Das obige ist der detaillierte Inhalt vonBleve: Wie baue ich eine raketenschnelle Suchmaschine?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

GolangissidealforbuildingsCalablesSystemduetoitseffizienz und Konsumverkehr, whilepythonexcelsinquickScriptingandDataanalyseduetoitssimplication und VacevastEcosystem.golangsDesineScouragesCouragescournations, tadelcodedeanDitsGoroutaTinoutgoroutaTinoutgoroutaTinoutsGoroutinesGoroutinesGoroutsGoroutins, t

Golang ist in Gleichzeitigkeit besser als C, während C bei Rohgeschwindigkeit besser als Golang ist. 1) Golang erreicht durch Goroutine und Kanal eine effiziente Parallelität, die zum Umgang mit einer großen Anzahl von gleichzeitigen Aufgaben geeignet ist. 2) C über Compiler -Optimierung und Standardbibliothek bietet es eine hohe Leistung in der Nähe der Hardware, die für Anwendungen geeignet ist, die eine extreme Optimierung erfordern.

Gründe für die Auswahl von Golang umfassen: 1) Leistung mit hoher Genauigkeit, 2) statisches System, 3) Mechanismusmechanismus, 4) Reiche Standardbibliotheken und Ökosysteme, die es zu einer idealen Wahl für die Entwicklung einer effizienten und zuverlässigen Software machen.

Golang ist für schnelle Entwicklung und gleichzeitige Szenarien geeignet, und C ist für Szenarien geeignet, in denen extreme Leistung und Kontrolle auf niedriger Ebene erforderlich sind. 1) Golang verbessert die Leistung durch Müllsammlung und Parallelitätsmechanismen und eignet sich für die Entwicklung von Webdiensten mit hoher Konsequenz. 2) C erreicht die endgültige Leistung durch das manuelle Speicherverwaltung und die Compiler -Optimierung und eignet sich für eingebettete Systementwicklung.

Golang erzielt eine bessere Kompilierungszeit und gleichzeitige Verarbeitung, während C mehr Vorteile bei der Ausführung von Geschwindigkeit und Speicherverwaltung hat. 1. Golang hat eine schnelle Kompilierungsgeschwindigkeit und ist für eine schnelle Entwicklung geeignet. 2.C läuft schnell und eignet sich für leistungskritische Anwendungen. 3. Golang ist einfach und effizient in der gleichzeitigen Verarbeitung, geeignet für die gleichzeitige Programmierung. 4. C Manual Memory Management bietet eine höhere Leistung, erhöht jedoch die Komplexität der Entwicklung.

Die Anwendung von Golang in Webdiensten und Systemprogrammen spiegelt sich hauptsächlich in seiner Einfachheit, Effizienz und Parallelität wider. 1) In Webdiensten unterstützt Golang die Erstellung von Hochleistungs-Webanwendungen und APIs durch leistungsstarke HTTP-Bibliotheken und gleichzeitige Verarbeitungsfunktionen. 2) Bei der Systemprogrammierung verwendet Golang Funktionen in der Nähe von Hardware und Kompatibilität mit der C -Sprache, um für Betriebssystementwicklung und eingebettete Systeme geeignet zu sein.

Golang und C haben ihre eigenen Vor- und Nachteile im Leistungsvergleich: 1. Golang ist für hohe Parallelität und schnelle Entwicklung geeignet, aber die Müllsammlung kann die Leistung beeinflussen. 2.C bietet eine höhere Leistung und Hardwarekontrolle, weist jedoch eine hohe Entwicklungskomplexität auf. Bei der Entscheidung müssen Sie Projektanforderungen und Teamkenntnisse auf umfassende Weise berücksichtigen.

Golang eignet sich für Hochleistungs- und gleichzeitige Programmierszenarien, während Python für die schnelle Entwicklung und Datenverarbeitung geeignet ist. 1. Golang betont Einfachheit und Effizienz und eignet sich für Back-End-Dienste und Microservices. 2. Python ist bekannt für seine prägnante Syntax und reiche Bibliotheken, die für Datenwissenschaft und maschinelles Lernen geeignet sind.


Heiße KI -Werkzeuge

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool
Ausziehbilder kostenlos

Clothoff.io
KI-Kleiderentferner

Video Face Swap
Tauschen Sie Gesichter in jedem Video mühelos mit unserem völlig kostenlosen KI-Gesichtstausch-Tool aus!

Heißer Artikel

Heiße Werkzeuge

ZendStudio 13.5.1 Mac
Leistungsstarke integrierte PHP-Entwicklungsumgebung

SAP NetWeaver Server-Adapter für Eclipse
Integrieren Sie Eclipse mit dem SAP NetWeaver-Anwendungsserver.

DVWA
Damn Vulnerable Web App (DVWA) ist eine PHP/MySQL-Webanwendung, die sehr anfällig ist. Seine Hauptziele bestehen darin, Sicherheitsexperten dabei zu helfen, ihre Fähigkeiten und Tools in einem rechtlichen Umfeld zu testen, Webentwicklern dabei zu helfen, den Prozess der Sicherung von Webanwendungen besser zu verstehen, und Lehrern/Schülern dabei zu helfen, in einer Unterrichtsumgebung Webanwendungen zu lehren/lernen Sicherheit. Das Ziel von DVWA besteht darin, einige der häufigsten Web-Schwachstellen über eine einfache und unkomplizierte Benutzeroberfläche mit unterschiedlichen Schwierigkeitsgraden zu üben. Bitte beachten Sie, dass diese Software

VSCode Windows 64-Bit-Download
Ein kostenloser und leistungsstarker IDE-Editor von Microsoft

SublimeText3 Mac-Version
Codebearbeitungssoftware auf Gottesniveau (SublimeText3)