So erstellen Sie Ihre eigene Uber-for-X-Anwendung

In den Top Ten NodeJS-Artikeln von Mybridge vom Oktober 2016 und den Top Ten NodeJS-Artikeln des Jahres (v.2017)

Update: Checke die neueste Version in meinem Tech-Blog!
Dieser Artikel ist jetzt ein paar Jahre alt - und aufgrund des sich schnell ändernden JavaScript-Ökosystems ist der Artikel etwas veraltet. Klicken Sie auf den obigen Link, um die aktualisierte Version dieses Artikels und das Projekt anzuzeigen.

Uber (falls Sie noch nichts davon gehört haben) ist eine praktische App, mit der Sie ein Taxi erwischen können, ohne herumlaufen zu müssen, um ein Taxi zu suchen. Und vor allem löst es die Nachfrage- und Angebotsprobleme von Taxifahrern und Taxisuchenden.

Heutzutage gibt es eine Vielzahl von Startups, die sich auf Uber-for-X-Apps konzentrieren. Man geht davon aus, dass Uber das, was er für Taxis getan hat, mit Sicherheit für andere Probleme mit Angebot und Nachfrage tun kann.

Während eines Hackathons beschlossen ich und mein Freund, eine Citizen-Cop-App zu erstellen. Wir dachten, es wäre cool, etwas zu bauen, das Ihren Freunden in schwierigen Zeiten helfen kann!

Nach einigem Überlegen waren sich die folgenden Merkmale einig:

  1. Auf Knopfdruck können Zivilisten den nächstgelegenen Polizeibeamten in ihrer Nachbarschaft anfragen. Es wird ein "Notsignal" ausgelöst und die Polizei in der Nähe alarmiert.
  2. Jede Polizei in der Nähe erhält sofort den Standort des Benutzers und kann die Anfrage annehmen und das Problem lösen.
  3. Ein Bewertungssystem
  4. Daten, die von Orten gesammelt, Verbrechensfälle beseitigt usw. wurden, können auf einer Karte visualisiert oder mit einigen anderen coolen Widgets für Benutzeroberflächen grafisch dargestellt werden

In diesem Tutorial werde ich Ihnen Schritt für Schritt erklären, wie wir es erstellt haben, damit Sie Ihre eigene Uber-for-X-App erstellen können.

Bevor Sie beginnen, sollten Sie folgende Punkte beachten:

  • In diesem Tutorial wird nicht darauf eingegangen, wie die App für den Maßstab erstellt wird. Oder für die Leistung. Es ist im Grunde so konzipiert, dass Sie Spaß beim Erstellen haben und etwas erstellen können, das Uber nachahmt. Stellen Sie sich dies so vor, als ob Sie ein Produkt mit minimaler Verfügbarkeit erstellen, um Ihre Idee oder Ihr Startup zu demonstrieren und einen Proof-of-Concept zu erhalten.
  • Da ich nicht viel mit Android- oder iPhone-Apps gearbeitet habe, werde ich dies so entwickeln, dass es in einem Browser funktioniert.

Jetzt hat jede App, die Sie erstellen, einige wichtige Teile:

  • eine Client-App (die Sie in einem Browser oder auf Ihrem Telefonbildschirm sehen)
  • im Backend ein Webserver, der eingehende Anfragen des Clients bearbeitet und Informationen weiterleitet
  • und eine Datenbank zum Speichern und Abfragen von Informationen.

Im Backend verwenden Sie MongoDB als Ihre Datenbank. Es ist einfacher zu erlernen und bietet zahlreiche Abfragetechniken für den Umgang mit Geodaten, die Sie für Ihre App benötigen.

Sie verwenden NodeJS für Ihre Back-End-Logik. Da es sich sowohl für das Front-End als auch für das Back-End um die gleiche Sprache handelt, müssen Sie sich keine Gedanken mehr über das Erlernen einer neuen Sprache oder Syntax machen.

Im Front-End verwenden Sie HTML5, CSS3, JavaScript sowie die Google Maps and Places-APIs.

Ich gehe davon aus, dass Sie bereits über Kenntnisse in JavaScript verfügen und zumindest theoretisch wissen, wie NodeJS und MongoDB funktionieren.

Hier sind die Inhalte dieses Tutorials:

Teil 1 (was Sie gerade lesen):

  • MongoDB Schema Design
  • Verwenden der Mongo Shell zum Abfragen von Informationen
  • Verbinden Sie Ihre Datenbank mit Ihrem Node-Express-Server und schreiben Sie RESTful-APIs

Teil 2:

  • Verwenden von Socket.IO, um zu ermöglichen, dass die Cop- und Civilian-Geräte miteinander kommunizieren
  • Verwenden der Google Maps-API, um Zivilisten und Polizisten auf einer Karte anzuzeigen

Lass uns anfangen!

Entwickler verwenden MongoDB seit einiger Zeit, um Anwendungen zu erstellen. Es hat eine flache Lernkurve und seine Vielseitigkeit ermöglicht es Entwicklern, Anwendungen schnell und einfach zu erstellen.

Ich persönlich mag MongoDB, weil es mir ermöglicht, schnell Prototypen für eine Idee zu erstellen, um Proof-of-Concept zu demonstrieren.

Bevor Sie beginnen, stellen Sie sicher, dass MongoDB und NodeJS installiert sind. Zum Zeitpunkt des Schreibens dieses Artikels ist die aktuelle Version von MongoDB 3.2.

Schema entwerfen

Da Sie MongoDB verwenden, ist alles, was Sie darin speichern, eine Sammlung von Dokumenten.

Erstellen wir eine Sammlung mit dem Namen "citizensData" zum Speichern von Bürgerinformationen und eine andere Sammlung mit dem Namen "policeData" zum Speichern von Polizeiinformationen. Öffnen Sie also Ihr Terminal und geben Sie mongo ein, um die Mongo-Shell zu starten. Sobald es geöffnet ist, können Sie vorhandene Datenbanken in MongoDB anzeigen, indem Sie Folgendes eingeben:

zeige dbs

Sie benötigen eine neue Datenbank, um Ihre App-Daten zu speichern. Nennen wir es myUberApp. Um eine neue Datenbank zu erstellen, können Sie Folgendes eingeben:

benutze myUberApp

Der Befehl use bewirkt, dass eine neue Datenbank erstellt wird, wenn sie nicht vorhanden ist. In diesem Fall wird Mongo angewiesen, alle folgenden Befehle auf diese Datenbank anzuwenden.

Mongo speichert Dokumente in Sammlungen. Sammlungen sind wie Tabellen. Geben Sie Folgendes ein, um vorhandene Sammlungen anzuzeigen:

Kollektionen anzeigen

Für den Cop könnte der Benutzername auch die Ausweis-ID sein. Sie können zu Authentifizierungszwecken ein Feld für die E-Mail-Adresse und ein Feld für das Passwort hinzufügen (das nicht angezeigt wird).

Gehen Sie zu diesem Link und speichern Sie den JSON-Datensatz für cop-bezogene Informationen.

Geben Sie Folgendes in Ihr Terminal ein, um Daten aus dieser JSON-Datei zu importieren:

mongoimport --db myUberApp --collection policeData --drop --file ./path/to/jsonfile.json

Bevor Sie mit der Abfrage Ihrer Datenbank beginnen, müssen Sie sich zunächst mit der Funktionsweise von Indizes in MongoDB (oder einer anderen Datenbank) vertraut machen.

Ein Index ist eine spezielle Anordnung von Daten oder Datenstrukturen, mit der Sie Informationen sehr effizient abfragen können. Auf diese Weise können Sie schnell Ergebnisse abrufen, ohne die gesamte Datenbank durchsuchen zu müssen.

Angenommen, Sie haben Informationen zu Schülern in aufsteigender Reihenfolge ihres Namens in einem Buch gespeichert. Dies bedeutet, dass Sie einen Index im Namensfeld haben. Auf diese Weise können Sie, wenn Sie Informationen zu einer Person namens Tyrion abrufen müssen, diese Informationen schnell finden, ohne zuerst die anderen Schüler durchgehen zu müssen.

Wenn Sie jedoch dieselben Informationen in aufsteigender Reihenfolge ihrer Größe speichern, wird es schwierig, Informationen für eine Person zu suchen, die ihren Namen verwendet. Dies kann viel Zeit in Anspruch nehmen, da die Schüler jetzt nicht in der Reihenfolge ihrer Namen gespeichert werden. Daher müssen Sie möglicherweise mehrere Zeilen scannen und durchsuchen.

Es sind jedoch auch andere Abfragen möglich. Rufen Sie beispielsweise Informationen zu Schülern ab, deren Höhe zwischen 2 und 3 Metern liegt. In diesem Fall konnten Tyrions Informationen schnell abgerufen werden, weil:

Verschiedene Datenbanken unterstützen verschiedene Indextypen. Die vollständige Liste der Indizes, die MongoDB unterstützen, finden Sie hier.

Also, jetzt, wenn Sie diesen Befehl eingeben:

 db.policeData.find (). pretty ()

Dadurch erhalten Sie alle Dokumente zurück, die in der Sammlung policeData vorhanden sind - das ist die gesamte Liste der Polizisten. (Die hübsche Funktion erleichtert das Lesen der Ausgabe).

Wenn Sie Informationen zu einem bestimmten Cop abrufen möchten, dessen Benutzer-ID 01 lautet, können Sie db.policeData.find ({Benutzer-ID: “01”}) eingeben. Pretty ()

{
    "_id": ObjectId ("57e75af5eb1b8edc94406943"),
    "userId": "01",
    "displayName": "Cop 1",
    "phone": "01",
    "email": "cop01@gmail.com",
    "earnRatings": 21,
    "totalRatings": 25,
    "Standort" : {
        "Typ": "Punkt",
        "Adresse": "Kalyan Nagar, Bengaluru, Karnataka 560043, India",
        "Koordinaten": [
            77.63997110000003,
            13.0280047
        ]
    }
}

Verwenden von MongoDB-Geodatenindizes

Mit Geodatenindizes können Sie GeoJSON-Objekte in Dokumenten speichern.

GeoJSON-Objekte können unterschiedlichen Typs sein, z. B. Point, LineString und Polygon.

Wenn Sie die Ausgabe Ihres .find () -Befehls beobachten, werden Sie feststellen, dass jeder Ort ein Objekt ist, in dem sich das Typfeld und das Koordinatenfeld befinden. Dies ist wichtig, da Sie, wenn Sie Ihr GeoJSON-Objekt als Punkttyp speichern, mit dem Befehl $ near Punkte in einer bestimmten Nähe für einen bestimmten Längen- und Breitengrad abfragen können.

Um dies zu verwenden, müssen Sie einen 2dsphere-Index (ein Geodatenindex) für das Standortfeld erstellen und ein Typfeld darin haben. Der 2dsphere-Index unterstützt Abfragen, mit denen Geometrien auf einer erdähnlichen Kugel berechnet werden. Dies schließt geografische Abfragen von MongoDB ein: Abfragen nach Einschluss, Schnittmenge und Nähe.

Geben Sie dies in Ihre Mongo-Shell ein:

db.policeData.createIndex ({"location": "2dsphere"})

Um Dokumente aus einem gegebenen Koordinatenpaar vom nächsten bis zum entferntesten abzurufen, müssen Sie einen Befehl mit der folgenden Syntax eingeben:

db.  .find ({
    : {
        $ near: {
            $ geometry: {
                Typ: "Punkt",
                Koordinaten: [, ]
            },
            $ minDistance: ,
            $ maxDistance: 
        }
    }
}).ziemlich()

$ minDistance und $ maxDistance sind optionale Felder. Um nun alle Polizisten zu erhalten, die sich innerhalb von 2 Kilometern von Breitengrad 12.9718915 und Längengrad 77.64115449999997 befinden, führen Sie Folgendes aus:

db.policeData.find ({
    Standort: {
        $ near: {
            $ geometry: {
                Typ: "Punkt",
                Koordinaten: [77.64115449999997, 12.9718915]
            },
            $ maxDistance: 2000
        }
    }
}).ziemlich()

Und das war's - Sie finden eine Liste der Dokumente, die in der Ausgabe zurückgegeben wurden!

Perfekt! Versuchen wir jetzt dasselbe mit einem Webserver. Laden Sie diese package.json-Datei herunter und speichern Sie sie im Stammverzeichnis Ihres Projektordners (vergewissern Sie sich, dass Sie sie package.json genannt haben). Wechseln Sie dann auf Ihrem Terminal zu dem Verzeichnis, das die Datei enthält, und führen Sie sie aus

sudo npm install

Eine kurze Erklärung zu einigen Paketen, die Sie verwenden werden:

  • Express ist ein Webframework für NodeJS. Es verfügt über zahlreiche APIs, Dienstprogramme und Middlewares in seinem Ökosystem, die Sie bei der Erstellung Ihrer Anwendung unterstützen.
  • body-parser analysiert eingehende Anforderungskörper in einer Middleware vor Ihren Handlern, die unter der Eigenschaft req.body verfügbar ist. Sie benötigen dies, um POST-Anfragen bearbeiten zu können.
  • Der Unterstrich vereinfacht das Schreiben von JavaScript. Wenn Sie möchten, können Sie auch eine andere Bibliothek verwenden.
  • Mit socket.io können Sie Web-Sockets in Ihrer Node-Anwendung verwenden.
  • mongodb ist der offizielle NodeJS-Treiber für MongoDB. Es hilft Ihrer Node-App, mit Ihrer Datenbank zu kommunizieren.

Die package.json-Datei enthält auch andere Module. Sie benötigen sie beim Erstellen einer vollständigen App, aber ich werde mich darauf konzentrieren, wie Sie den Mongodb-Treiber in Ihrer Express-App zum Ausführen von Abfragen verwenden. Einige der anderen Module haben folgende Funktionen:

  • async ist ein Dienstprogramm für den Umgang mit asynchronem Code in NodeJS. Es hilft Ihnen, Rückrufhölle zu vermeiden.
  • Debug ist eine Debugging-Bibliothek. Dieses praktische Tool hilft beim Debuggen Ihrer Programme ohne die Verwendung von hässlichen Ausgaben der Anweisung console.log.
  • redis ähnelt dem Mongodb-Treiber. Damit kann Ihre NodeJS-App mit Ihrer Redis-Datenbank kommunizieren.
  • connect-redis ist ein Sitzungsspeicher, der Redis zum Verwalten von Sitzungen verwendet. Sie benötigen dies später, wenn Sie sich für ein Benutzerkonto entscheiden.

Bevor Sie Code schreiben, ist es hilfreich, ihn zuerst zu organisieren. Im Moment können Sie zwei Dateien verwenden:

  • Eine Datei zum Schreiben Ihrer API-Endpunkte
  • Eine Datei, die Datenbanktreiber für datenbankbezogene Vorgänge verwendet. Der Routehandler würde entscheiden, welche Funktion aus der Datenbankdatei aufgerufen werden soll. Sobald eine Abfrage durchgeführt wurde, werden die Ergebnisse mithilfe einer Rückruffunktion an Ihren Routen-Handler zurückgesendet.

Mal sehen, wie das aussieht, wenn Sie Ihren Code schreiben:

In diesem Beispiel erstellen Sie eine neue Instanz des MongoClient-Objekts aus dem Mongodb-Modul. Sobald der Webserver gestartet ist, stellen Sie mit der von Ihrer MongoClient-Instanz bereitgestellten Verbindungsfunktion eine Verbindung zu Ihrer MongoDB-Datenbank her. Nachdem die Verbindung initialisiert wurde, wird im Rückruf eine Db-Instanz zurückgegeben.

Sie können jetzt sowohl die app- als auch die db-Instanz an die Initialisierungsfunktion Ihrer routes.js-Datei übergeben.

Als Nächstes müssen Sie eine neue Datei mit dem Namen routes.js erstellen und diesen Code hinzufügen:

Funktion initialisieren (App, DB) {
    // Eine GET-Anfrage an / cops sollte die nächstgelegenen Cops in der Nähe zurückgeben.
    app.get ('/ cops', function (req, res) {
    / * Extrahiere die Längen- und Breitengrade aus der Anfrage. Dann holen Sie die nächstgelegenen Polizisten mit den Geodatenabfragen von MongoDB und senden Sie sie an den Kunden zurück.
    * /
    });
}
exports.initialize = initialize;

Damit dies funktioniert, müssen Sie die Koordinaten in Ihrer Anfrage als Abfragezeichenfolgen übergeben. Sie schreiben Ihre Datenbankvorgänge auch in eine andere Datei. Erstellen Sie also eine neue Datei db-operations.js und schreiben Sie Folgendes:

Funktion fetchNearestCops (DB, Koordinaten, Rückruf) {
    db.collection ('policeData'). createIndex ({
        "location": "2dsphere"
    }, function () {
        db.collection ("policeData"). find ({
            Standort: {
                $ near: {
                    $ geometry: {
                        Typ: "Punkt",
                        Koordinaten: Koordinaten
                    },
                    $ maxDistance: 2000
                }
            }
        }). toArray (Funktion (Fehler, Ergebnisse) {
            if (err) {
                console.log (err)
            } else {
                Rückruf (Ergebnisse);
            }
        });
    });
}
exports.fetchNearestCops = fetchNearestCops;

Diese Funktion akzeptiert drei Argumente: eine Instanz von db, ein Array mit Koordinaten in der Reihenfolge [, ] und eine Rückruffunktion, an die die Ergebnisse Ihrer Abfrage zurückgegeben werden.

Der createIndex stellt sicher, dass ein Index für das angegebene Feld erstellt wird, falls er nicht vorhanden ist. Sie können diesen Index überspringen, wenn Sie vor der Abfrage bereits einen Index erstellt haben.

Jetzt müssen Sie diese Funktion nur noch in Ihrem Handler aufrufen. Also ändere deinen routes.js Code wie folgt:

var dbOperations = require ('./ db-operations');
Funktion initialisieren (App, DB) {
    // '/cops?lat=12.9718915&&lng=77.64115449999997'
    app.get ('/ cops', function (req, res) {
        // Konvertiere die Query Strings in Numbers
        var width = Number (req.query.lat);
        var longitude = Number (req.query.lng);
        dbOperations.fetchNearestCops (db, [Längengrad, Breitengrad], Funktion (Ergebnisse) {
        // Die Ergebnisse in Form von JSON an den Client zurückgeben
            res.json ({
                Bullen: Ergebnisse
            });
        });
    });
}
exports.initialize = initialize;

Und das ist es! Lauf

node app.js

Öffnen Sie in Ihrem Terminal Ihren Browser und drücken Sie http: // localhost: 8000 / cops? lat = 12.9718915 && lng = 77.64115449999997

Abhängig von den übergebenen Abfragezeichenfolgen sollten Sie entweder eine JSON-Antwort mit einem leeren Array oder einem Array mit cop-Daten erhalten!

Dies ist das Ende von Teil 1. In Teil 2 werden Sie versuchen, ein Notsignal an die Bullen in der Nähe zu senden. Dann werden Sie herausfinden, wie ein Cop mit socket.io auf das Signal reagieren könnte. Außerdem sehen Sie, wie Sie den Standort des Bürgers auf einer Karte anzeigen.

Schauen Sie sich in der Zwischenzeit den Quellcode auf Github an!

Vielen Dank an Quincy Larson, die mir geholfen hat, diesen Artikel zu verbessern.

UPDATE: Sie können diesen Artikel und nachfolgende Teile auch in meinem Blog lesen!