Abfragen für eine MongoDB zu formulieren macht keinen wirklichen Spaß. Über verschachtelte BSON-Strukturen müssen die Daten in der Datenbank abgefragt werden. MongoDB sagt zwar selbst: “Die Abfrage-API. Eine natürliche Art, mit Daten zu arbeiten.“, aber so ganz natürlich hat es sich für uns nicht angefühlt.
In diversen Projekten hatten wir das Gefühlt MongoDB Zugriffscode zu produzieren, der schwer lesbar und dadurch auch schwer wartbar ist. Jetzt könnte man an dieser Stelle sagen: “Ok, ist ja nur an der einen Stelle,….“, aber genau das wollten wir nicht.
Wir haben begonnen eine Abfragemöglichkeit, ähnlich zur Java-basierten Lösung QueryDSL, zu bauen, mit der man Daten aus einer MongoDB über eine statische API abfragen kann. Sie unterstützt aktuell die meisten Operatoren der MongoDB API und die Abfrageobjekte können von einem kleinen Generator aus den BSON-Mappings erstellt werden.
Das ganze haben wir dann noch etwas angepasst und in eine Golang Open Source Bibliothek gepackt: https://github.com/SourceFellows/mongo-query
Folgende Abfrage liefert zum Beispiel alle Dokumente bei denen 15 Elemente in
‘Amenities’ gespeichert sind und die ListingUrl <value>
enthält, sowie das
verschachtelte Feld PictureUrl
ebenfalls den Wert <value>
besitzt:
Listing.Amenities.ArraySize(15).
And(Listing.ListingUrl.Equals("<value>"),
Listing.Images.PictureUrl.Equals("<value>"))
Der Ausdruck ist:
bson.D
oder bson.M
StrukturWenn man die Abfragen über die reine MongoDB API der mongo Query API gegenüberstellt, sieht man recht gut, dass die Abfragen mittels mongo Query einfacher zu lesen und dadurch auch zu warten sind. Auch Änderungen am BSON-Mapping schlagen hier nicht so stark zu Buche, da dies nicht mehr in jeder Abfrage, sondern an einer zentralen Stelle, in den Filter-Objekten, stattfindet.
//MongoDB API
err = findWithFilter(ctx, collection, bson.D{{"size.uom", "in"}})
//mongo Query
err = findWithFilter(ctx, collection, InventoryFilter.Size.Uom.Equals("in"))
Ausgangsbasis für den Einsatz der ‘mongo Query’ Bibliothek ist ein Struct, das die Datenstruktur der Dokumente in einer Datenbank wiederspiegelt und für das ein BSON-Mapping vorhanden ist.
Zum Beispiel folgendes Struct:
type ListingAndReview struct {
Id string `bson:"_id"`
ListingUrl string `bson:"listing_url"`
Name string `bson:"name"`
Amenities []string `bson:"amenities"`
Images struct {
ThumbnailUrl string `bson:"thumbnail_url"`
MediumUrl string `bson:"medium_url"`
PictureUrl string `bson:"picture_url"`
XlPictureUrl string `bson:"xl_picture_url"`
} `bson:"images"`
}
Mit Hilfe des Command-Line Generators kann man im Anschluss ein entsprechendes Filterobjekt generieren. Zuerst muss dieser allerdings installiert werden.
Die Installation des Generators erfolgt, wie meist, über einen einfachen go
install
Aufruf:
go install github.com/sourcefellows/mongo-query/cmd/mongo-query-gen@latest
Mit folgendem Kommando können die entsprechenden Filterobjekte erstellt werden.
Im Beispiel muss sich das Struct von oben in der Daten Types.go
befinden.
mongo-query-gen -in Types.go -outDir .
Jetzt fehlt nur noch die Abfrage. Die generierten Filter-Objekte können direkt als BSON-Filter genutzt werden, so dass kein zusätzlicher, spezieller Call nötig ist.
filter := Listing.ListingUrl.Equals("https://www.airbnb.com/rooms/10009999")
collection := client.Database("airbnb").Collection("listingsAndReviews")
cursor, err := collection.Find(ctx, filter)
mongo Query unterstützt bereits die meisten Query Optionen der MongoDB API. Einfach ausprobieren!
Wir sind offen für weitere Anwendungsfälle und Feedback! Viel Spaß mit der Bibliothek.
12.01.2024
Der Author auf LinkedIn: Kristian Köhler und Mastodon: @kkoehler@mastodontech.de
Der praktische Soforteinstieg für Developer und Softwarearchitekten, die direkt mit Go produktiv werden wollen.
zur Buchseite beim Rheinwerk Verlag Rheinwerk Computing, ISBN 978-3-8362-7559-0 (als PDF, EPUB, MOBI und Papier)
Source Fellows GmbH
Lerchenstraße 31
72762 Reutlingen
Telefon: (0049) 07121 6969 802
E-Mail: info@source-fellows.com