Vulnerability in EPiServer.Forms
I have a property that is of type List<List<GeoLocation>> on the pagetype Im doing my search on.I also have a variable that each is of type GeoLocation.
What I would like to do is something like query.Filter(x => [GeoLocationVariable].WithinAnyOf(x.[ListProperty]) and add that as a filter.
I foudn the Within() method on List<GeoLocation> but how would one go about doing the enumeration over the outer List in [ListProperty]?
If possible given what you are building I would recommend flattening the list, that is, adding a property that converts the List<List<GeoLocation>> to an IEnumerable<GeoLocation>. By doing so you'll have data in your index which is easier to query.
I don't think flattening the list would help me in what I am trying to acomplish, but I could be wrong.Let me explain: I am trying to find out if the variable (GeoLocation) fits into (Within()) any of the areas (ie one area is a List<GeoLocation>).In other words each item in the outer list is describing an area made up of GeoLocation:s for each point.So if I make the list flat List<GeoLocation> and use Within() on it it wouldn't be the same as asking Within() on each item as a List<GeoLocation> in the outer list, or would it?
Aha, I see. Interesting problem. Find has a lot of functionality for geo search but pretty much all of it works in the other direction - ie you have single coordinates indexed and you filter by such a coordinate being within a certain range of a coordinate that you supply at query time, or within a bounding box made up of several coordinates which you supply at query time (that's what the Within method is for). What you want to do though is the opposite. I can think of several possible solutions but none of them is obvious.
In order for me and others to help you, would it be possible for you to describe the use case in more detail? Keep in mind that some solutions may involve drastically changing what is indexed so as much context as you can possibly give would be great.
Heh, yeah it might not be the most common of use cases. :)
Ok, here goes:So I have this scenario of a site that has alot of sub pages, each sub page representing a company, each company has subpages representing an area. Each area represent a geographical area that the company is active inside.The areas are crated using a custom built property vitalizing Google Maps so you can plot an area and save it.
So basicly the page for each company has a property like so:
public IEnumerable<IEnumerable<GeoLocation>> Areas
return this.GetChildrenByPageType(typeof(ShopLocationPage).Name, true)
.Where(childPage => childPage.Area != null))
.Select(childPage => childPage.Area));
The indexing of all pages is done using the default indexer but I have a relation added to conventions so the Areas propery will be updated when one area is updated like so:
var relatedContent = new List<IContent>();
if (!(model.ContentLink.CompareToIgnoreWorkID(ContentReference.StartPage) ||
So when a visitor does a search for a company it has an option to write a location (Like a city or such) wich is then translated using Google Maps to a GeoLocation. This GeoLocation will be the variable in the Filter I am trying to create.
The REST API has functionality that probably can be used for this but the .NET API doesn't have that feature yet unless we can come up with some "creative" solution. Wheels are turning :)
Hi,One approach I would do in this case is to index the exact location of the shop and then in the query set the ordering to the distance from the location from where the user "searching". To not get results to far away maybe also use a filter with some sort of relevant max distance.Regards,Henrik
That wouldn't really work since a company can be active in multiple areas, not multiple adresses.The areas are not adresses in that meaning of the word that they have one single adress, think of them as areas that are represented and each made up of a polygon of geolocations. And the areas themselves are not necceray in the form of circle around any location so using "WithinDistanceFrom" wouldn't work.
I have been trying to find anyhting like this in the documentation but without any luck.
Every aspect of the geographical search seem focused on one indexed location vs many/one variable not the other way round.
This is sort of what I was hoping for:
query = query.Filter(x => variableAsGeoLocation.WithinAny(x.Points))
query = query.Filter(x => x.Points.Any(variableAsGeoLocation.Within))
The locations could be indexed separatly pointing to the same shop but unfortunately you would still rely on the circular shape.
Correct, currently all supported methods revolves having a single (or multiple unrelated) coordinates in the index and filtering based on those. I *think* (that's really Henrik) it would be possible to add support for filtering based on indexed geographical shapes/areas but that's on a level where both the REST API and .NET API would need to be updated - in other words, a feature EPiServer has to deliver.
Is there some other possible solution for your problem? Ie, can we enforce some constraints in terms of number of coordinates that describe the shape or something like that?
Ok, well as far as constraints concern I could possibly enforce the companies to one area each so that Areas returns IEnumerable<GeoLocation>, do you think that would make any difference?
I'm afraid I don't think so. A workable (though impractical) constraint could be if they described each area as a number of coordinates where each coordinate had a fixed radius. In other words, describing each area as one or more circles. Other than that I can't really think of anything that's currently supported other than doing more of the querying on the web server.
In the documentation I found this "multipolygon" type (It's in the bottom of the page):http://www.elasticsearch.org/guide/reference/mapping/geo-shape-type.html
So if any developer on Find in here is reading this: Please add support for the multipolygon type but even more so, please also add support for a "reversed" Within, that is Filer(x => variable.Within(x.Property).I acctually think that is a more common use-case than Filter(x => x.Property.Within(variable)) if you think about it.Lets for example take mobile positioning for a mobile web, you'd have the variable as a single location and probably the indexed postions not only as sinngle locations but also as polygons from map services.
Do you remember if you found another way to achieve the scenario that you described above re. a reverse Within search? I have the exact same task and I have found no improvement in the Find/search+nav API that enable this.
This might inspire someone from the future.
The scenario, that I needed to implement for my client, is described fairly precisely here by Mattias.
I added a few constraints to make my task simpler:
I added 100 properties, to my company content type, that each store north(lat), east(lng), south(lat), west(lng) of an area.
When a user search for company content inside an area eg. "Zealand, Denmark", I first ask Google Map for the Lat/Lng coordinate of this search area. Then I compose a Find filter using GreaterThan/LessThan with the latitude and longitude numbers. My search is really a ranged search on latitude from South to North and on longitude from West to East.
I stack 100 "Or" filters in my query filter = filter.Or(_ => _.Area0.South.LessThan(searchLat) & _.Area0.North.GreaterThan(searchLat) & _.Area0.West.LessThan(searchLng) & _.Area0.East.GreaterThan(searchLng));filter = filter.Or(_ => _.Area1.South.LessThan(searchLat) & _.Area1.North.GreaterThan(searchLat) & _.Area1.West.LessThan(searchLng) & _.Area1.East.GreaterThan(searchLng));filter = filter.Or(_ => _.AreaX.South.LessThan(searchLat) & _.AreaX.North.GreaterThan(searchLat) & _.AreaX.West.LessThan(searchLng) & _.AreaX.East.GreaterThan(searchLng));
filter = filter.Or(_ => _.Area0.South.LessThan(searchLat) & _.Area0.North.GreaterThan(searchLat) & _.Area0.West.LessThan(searchLng) & _.Area0.East.GreaterThan(searchLng));
filter = filter.Or(_ => _.Area1.South.LessThan(searchLat) & _.Area1.North.GreaterThan(searchLat) & _.Area1.West.LessThan(searchLng) & _.Area1.East.GreaterThan(searchLng));
filter = filter.Or(_ => _.AreaX.South.LessThan(searchLat) & _.AreaX.North.GreaterThan(searchLat) & _.AreaX.West.LessThan(searchLng) & _.AreaX.East.GreaterThan(searchLng));
This seems to work.