In this video, look at the Redis commands related to a geohash—GIS or latitude and longitude based data—as implemented in the StackExchange.Redis library.
- [Instructor] Back in Visual Studio, let's take a look at how to manipulate geo information or geographic information within Redis from the C++ client. I'll copy and paste the last file, drag and drop, F2 to rename it, this becomes 08_02, enter to open the file, rename the class, 08_02, scroll down, delete all of the content of the method, and rename the method.
This is all about GEO. So from this method, we will play around with geographic information, and for your convenience, I've got a file ready. So within this method we will manipulate some geographic information data, and to do so I have a CSV file, a text file, with some geographic information already pre-processed. So the idea is to provide latitude, longitude, and names of places, and I have a list of waterfalls for you. So I will drag and drop this on to the project, and so here we have WaterfallsAt.csv.
So we'll use that file to import a lot of data into Redis, and when I say a lot, it's not really. Redis is so fast, we don't even have to optimize this process. So first thing you'll do is load the file WaterfallsAt.csv, and let's see what's inside. So as a string filename, one, two, three levels up from where our test is running, and I'll just check that I got that right.
So Check.That(File) from System.IO, so Ctrl + ., System.IO, File.Exists(filename)).IsTrue();, and so I'm just making sure that I got this text correct for the placement of the waterfalls file. If you choose a different location, do make sure you check that as well; easily mistaken item. We're running a unit test of course, so that structure is perhaps slightly different, than if you do a console application or something like that.
Now the good news is, this is really simple to read, it's all just text. We'll keep count of how many lines just for mind sake, and then we'll ask foreach (string, line of text, in File.ReadAllLines()). So this gave me back an enumerable of string, and so we can process each line in turn. So I'll go open this filename, copy and paste the name into here, and then the IEnumerable will be setup for us, and we can just process each line in turn.
So if you want, you can write this all out into your debug window, so I'll hit Ctrl + ., and System.Diagnostics, and you'll see it in your output window if you debug your tests. So I'll leave that there for your convenience. I already know that the first line is going to be the column heading names in the CSV. So if the first line, first character, is an L, yes I've opened the file before, I'll just continue, right. So we'll Debug.WriteLine all of the file out into the debug window, but when I'm starting to do the processing, I'll just skip the first line.
So it is a CSV, so each of the columns can be easily obtained by just doing a line.Split, and we'll split by comma of course, being that comma separated values file, and then we'll extract information from those columns as we want to process the file. Let's just make sure it works up to this point. So the number of columns is greater than columns (mumbles), length is the property, IsGreaterThan(0), and off we go; save all, build, and right-click, run our test.
So first I want to make sure the filename is correct, then I want to make sure that the columns actually have, that the lines actually have multiple columns, and success. So now that we know we're getting columns worth of data for each line, let's create a little object for ourselves, and in this case, the object will live in Redis. So we have the native data type, the hash that contains the latitude, longitude, and a name for that particular entry. Now let's just take a quick look at the file, starting it yourself.
So we have latitude, longitude, and if you scroll to the right, some interesting characters in there, but yes there are waterfall names, and included in the waterfalls are the location where their found by country. So AE is for Arab Emirates, and then of course we have all sorts of countries in there of interest, my personal favorite being Iceland, so I'll focus on those. So we'll ask Redis to GeoAdd, based on a key, this will be by country, the longitude, the latitude, and the name, it's a l-o-c, short for location name.
So what I'm trying to do is to say for GeoAdd, I'll give you a key for the hash, and I'll give you longitude and latitude, and I'll give you a name for that location. So remember our geohash will be produced, so latitude, longitude will be combined into hash, and the name is essentially going to be the member of the element in the hash table, and the key will be the key for our hash table. Okay, so let's process our columns and actually extract those variables.
So the key we're going to use is going to be the country, and we'll get that from the columns, I happen to know it's the 7th column, trust me on that one, I'll just trim the quotation marks off the ends of those names. So the latitude, we'll ask double.Parse, give us an actual number from column 0, again we need that to be trimmed, let me just copy and paste for a moment.
So latitude, longitude, so 0, and 1, just left this ordering in here, latitude is the first column in the file, longitude is the second, but longitude is the first parameter for GeoAdd, latitude is the second parameter. I'm going to copy and paste the Trim, and then the name, is actually the name of the waterfall itself.
So we're going to go all the way down the columns list to column number 14, and just do the same Trim, and so what I'm doing is I'm creating a key value pair in Redis where the key is the key we use to access the hash table. Within the hash table, all of the entries in the hash table are geohash's of this latitude and longitude, and each geohash is matched to a name, a location name, in this case the name of the waterfall. So we can save all that, keep track of the count, and let's just make sure this is actually happened, Check.That(count).IsGreaterThan(0);, save all, build, right-click, run the test.
While you wait for the several thousand round-trips to go over the network, do take a look at your portal, and from the console, we can see what's happening. We'll just type info, and then right at the end, you can see for the Keyspace, 137 keys have been added. 137 countries listed in this geographic database, listing waterfalls.
So once your test completes, you'll know that we've inserted many thousand into Azure, into the Redis database, but of course, each of these GeoAdd operations was network round-trip, and so that was relatively expensive to do one at a time. There are ways to optimize this; keep watching this course. For now what I'll do, is I'll just trim this down to only include Iceland. So if (keyCountry == "IC"), then we'll do the GeoAdd, and so I'll run my test again, and so now it should be a lot faster than the two minute runtime I had initially, so now it's down to two seconds.
So you can pick a country for yourself as well, take a look at the file, and add the filter. So here I'm just filtering by IC, and I'm still doing several thousand, probably several hundred round-trips to the server, but that's okay for my network connection for this example. Do you remember, that because we copy and paste the previous file, we are doing a FlushDatabase every time we run the test, so being patient one time is not really ideal, you do want to bring down the total time it takes to run your test, and every time you run will flush, so this load will happen every time you run this test.
So let's create a new method. (keyboard tapping) And so now we'll try and find some waterfalls. Just to correct the way I phrased it before, FlushDatabase is in the ClassInitialize method, so this happens once every time this type is loaded, and so this will happen once for all of the tests in this class.
Previously we had only the one test, but now that we've got two tests, if I first run the first test, and then run the second test, then the data would have already been loaded. The trouble is I don't have control over the sequence here, and so I'll have to load this data every time I want to rely on it being present. So let's (mumbles) a little bit. We'll select the first part of the method, so just before the Check, press Ctrl + R + N, or you can right-click and extract method, and we'll call this LoadWaterfalls(), and so we still have the original test to just check that the loading is working, and that we get a good number of items loaded, but we now also have the method LoadWaterfalls that we can use to populate Redis from any other method.
So scrolling down, here's the Waterfalls, so first thing we'll do is actually load the waterfalls from the file, and now we will query against it. So let's just go back to the console for a moment. So remember our structure of our data is no longer a string, so I can't say GET IC, which is the country and so the key, it will complain, it's the wrong type, it's not a string, it's actually a hash.
So I can say HGET, the trouble for me is hash get implies that we will get the hash based on the key, in this case the key is IC for Iceland, but I'm supposed to provide a member, so this is not going to work either, right. Wrong number of arguments, and so when I say HGET IC, I know there's a gullfoss in Iceland, but again, not quite going to work. So we have to go and look at the Geo Commands, and actually use the Geo Commands to query against this geo specific hash set.
So we've been calling GeoAdd from the (mumbles), so let's try and use one of the queries and get some information out. So GEOHASH allows us to query for the hash that was computed for the latitude and longitude, and we can say GEOHASH IC, and then specify the member, so the name of one of the entries in the hash table, and there we go, capitalization matters.
So Gullfoss is the name of one of the waterfalls in Iceland, and there is the geohash for it's location. Now you might want to use LAT, LONG, that is also possible. Remember we have GEOPOS for position, and so here we get the longitude, latitude, for the same location. So if you have a library using the geohash algorithm, you can use that, or you can go directly to the latitude, longitude as well.
Just remember the order in here is longitude, latitude, so the -22 is left from Greenwich, so 0-, so westbound, and then 65 is north, so plus from the equator. We can also query for distance. GEODIST, so this is going to be all in Iceland, but between Gullfoss and one in the north, Dettifoss, and so it gives us the distance based on its computation of the shape of the planet and all that, you do need to go look at the implementation, if this important from an accuracy point of view, but it says there are 267 kilometers, and if you look at your favorite mapping tool, you'll see that it's pretty close as the crow flies, but if you're doing navigation, and certainly automation of driving vehicles or planes, please make sure it's accurate enough for your purposes.
So let's go back to C++, and let's run the same queries. So once we've loaded the waterfalls, we can ask Redis for information about those data points. Now bear in mind, I filtered my load to only Iceland, but of course, if you remove the if statement above, than you can query for really anything. So we've done plenty of GeoAdd already, so let's get the GeoDistance. So the key will be IC for Iceland, member1 and member2, and these two members have to be in Iceland, it only works within that hash.
(keyboard tapping) And the distance comes back as a double. We didn't specify which unit, depending on how you want to do this, you can say GEOUNIT, I'd like this to be in kilometers please. (keyboard tapping) GeoDistance comes back as a double, denote this as nullable, we know it'll have a value, I'm not going to check that it has value, and so dist.Value).IsGreaterThan, and from interactive command line, we know it should be above 250.
Ctrl + Shift + B for build, call Waterfalls, it will take a few seconds to load all of the data into Redis on Azure, and then execute the query, and so we get yes, confirmed, the returned distance from Redis is greater than 250, as we saw in the interactive command. So, the same is possible in C++ for everything you've seen in the console, drop + space for list, things like Distance, Position, Radius, and so forth, makes it easy for you to build geospatial aware applications by being aware of people's distance from each other, objects within a certain location, or a range of distance within a location, and even finding the distance between locations, all within Redis itself.
- What is Redis?
- Installing Redis on a Windows machine
- Deploying the latest version of Redis into a container
- Connecting with C# clients
- Working with data types in Redis
- Key expiration and expiration policies
- Working with lists, sets, and hashes in Redis
- Working with batches and transactions
- Using Redis with ASP.NET web apps