I am using Mongoid to store a series of geocoded listings. These listings need to be sorted by price and proximity. The price of every listing is a field in the database whereas distance is a dynamic property that is unique for every user.
class Listing
include Mongoid::Document
field :price
def distance
get_distance(current_user.location,coordinates)
end
end
How can I sort these documents by distance? I tried #listing.desc(:distance) and that didn't work.
The short (and unhelpful) answer is: you can't.
Mongoid does have the ability to query based on 2d co-ordinates though, then you could update your controller to do something like this:
#listings = Listing.near(current_user.location)
Which I believe will return your listings in order of distance.
On a side note, I noticed that your Listing model is referring to your current_user object, which kinda breaks the MVC architecture, since your models shouldn't know anything about the current session.
Related
I'm updating a data feed export, which links a Product to a given Category. I want to also include that product's merchandising position within that category, which currently exists in Business Manger, and is used to control sorting on Product listing pages:
I'm digging through the API docs, and the logical place for this information to be exposed in in dw.catalog.CategoryAssignment, but it's not there. I'm currently inferring the position by essentially doing this:
// assume var product, category
var position = category.products.firstIndex(p => p.ID == product.ID);
However, this tells me where the Product got sorted to, not what the actual Position value is within Demandware. It works for now as an expedient hack, but I really want to replace it with something that pulls the actual value from DW.
Where in the Commerce Cloud API can I find the merchandising position for a given Product in a given Category?
I think you would'nt get the actual position of the product index as you may have multiple sorting rules to display different outputs on the category listing pages. These sorting rules can be created as and when required based on certain rules. I don't think this can be reflected on the product feed.
It took some digging, but I managed to find that the "Position" field for Products in the BM is stored as Product.searchPlacement. To find it, you have to look in Category.products, find the Product you want, and grab the searchPlacement property of that product.
In effect, I used:
// assume var product, category
var position = category.products.find(p => p.ID == product.ID).searchPlacement;
For Products that don't have a Position assigned in the Business Manager, searchPlacement is 0. Otherwise, it reflects the value entered in the BM.
I'm trying to figure out how to query with filter with Geofire.
Suppose I have restaurants with different category. and I want to add that category to my query. How do I go about this?
One way I have now is querying the key with Geofire, run the for loop through each key and get the restaurant, and insert the appropriate restaurant to the array.
These seems so inefficient. Is there any other way to go about this?
Ideally I will have the filtered results, and only load each item when they're about to be shown.
Cheers!
Firebase queries can only filter by one condition. Geofire already does quite some "magic" to allow it to filter on both longitude and latitude. Adding another property to that equation might be possible, but is well beyond what Geofire handles by default. See GeoFire: How to add extra conditions within the query?
If you only ever want to access one category at a time, you can put the restaurants in a top-level node per category and point Geofire to one category.
/category1
item1
g: "pns0h0mf2u"
l: [-53.435719, 140.808716]
item2
g: "u417k3dwub"
l: [56.83069, 1.94822]
/category2
item3
g: "8m3rz3s480"
l: [30.902225, -166.66809]
/items
item1: ...
item2: ...
item3: ...
In the above example, we have two categories: category1 with 2 items and category2 with just 1 item. For each item, we see the data that Geofire uses: a geohash and the longitude and latitude. We also keep a single list with the other properties of these 3 items.
But more commonly, you simply do the extra filtering in client-side code. If you're worried about the performance of that: measure it, share the code, JSON data and measurements.
This is an old question, but I've seen it in a few places on the web, so I thought I might share one trick I've used.
The Problem
If you have a large collection in your database, maybe containing hundreds of thousands of keys, for example, it might not be feasible to grab them all. If you're trying to filter results based on location in addition to other criteria, you're stuck with something like:
Execute the location query
Loop through each returned geofire key and grab the corresponding data in the database
Check each returned piece of data to see if it matches the other criteria
Unfortunately, that's a lot of network requests, which is quite slow.
More concretely, let's say we want to get all users within e.g. 100 miles of a particular location that are male and between ages 20 and 25. If there are 10,000 users within 100 miles, that means 10,000 network requests to grab the user data and compare their gender and age.
The Workaround:
You can store the data you need for your comparisons in the geofire key itself, separated by a delimiter. Then, you can just split the keys returned by the geofire query to get access to the data. You still have to filter through them, but it's much faster than sending hundreds or thousands of requests.
For instance, you could use the format:
UserID*gender*age, which might look something like facebook:1234567*male*24. The important points are
Separate data points by a delimiter
Use a valid character for the delimiter -- "It can include any unicode characters except for . $ # [ ] / and ASCII control characters 0-31 and 127.)"
Use a character that is not going to be found elsewhere in your database - I used *, but that might not work for you. Do not use any characters from -0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz, since those are fair-game for keys generated by firebase's push()
Choose a consistent order for the data - in this case, UserID first, then gender, then age.
You can store up to 768 bytes of data in firebase keys, which goes a long way.
Hope this helps!
I'm designing a REST API where you can search for data in different countries, but since you can search for the same thing, at the same time, in different countries (max 4), am I unsure of the best/correct way to do it.
This would work to start with to get data (I'm using cars as an example):
/api/uk,us,nl/car/123
That request could return different ids for the different countries (uk=1,us=2,nl=3), so what do I do when data is requested for those 3 countries?
For a nice structure I could get the data one at the time:
/api/uk/car/1
/api/us/car/2
/api/nl/car/3
But that is not very efficient since it hits the backend 3 times.
I could do this:
/api/car/?uk=1&us=2&nl=3
But that doesn't work very well if I want to add to that path:
/api/uk/car/1/owner
Because that would then turn into:
/api/car/owner/?uk=1&us=2&nl=3
Which doesn't look good.
Anyone got suggestions on how to structure this in a good way?
I answered a similar question before, so I will stick to that idea:
You have a set of elements -cars- and you want to filter it in some way. My advice is add any filter as a field. If the field is not present, then choose one country based on the locale of the client:
mydomain.com/api/v1/car?countries=uk,us,nl
This field should dissapear when you look for a specific car or its owner
mydomain.com/api/v1/car/1/owner
because the country is not needed (unless the car ID 1 is reused for each country)
Update:
I really did not expect the id of the car can be shared by several cars, an ID should be unique (like a primary key in a database). Then, it makes sense to keep the country parameter with the owner's search:
mydomain.com/api/v1/car/1/owner?countries=uk,us
This should return a list of people who own a car with the id 1... but for me this makes little sense as a functionality, in this search I'll only allow one country:
mydomain.com/api/v1/car/1/owner?country=uk
I am querying a Model*, which has a field that refers to another Model. Is it possible to order the results using a field in the linked Model?
So for example:
Car contains a field which refers to its OWner. I want to show all Cars sorted by their owner.
(I don't want to use the statement() method since in that case I would have to write the whole query myself)
(*) using http://typo3.org/api/typo3cms/class_t_y_p_o3_1_1_c_m_s_1_1_extbase_1_1_persistence_1_1_generic_1_1_query.html
Yes, it is possible (don't care for the $constraint for the moment):
$query->matching($constraint)->setOrderings(
array('owner.sorting' => Tx_Extbase_Persistence_QueryInterface::ORDER_ASCENDING)
)->execute();
Assuming the field in you Car model is named owner and you want to sort by sorting field of the Owner model/table.
When working with 6.2
->setOrderings(Array('model.yourfield' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING))
#Michael and #rob-ot are right. There is one pitfall that took me many hours and that I'd like to mention here:
If your sort field in your related table contains underscores, you have to provide its name to setSortOrderings in lowerCamelCase:
// with database column name my_car_sorting you must define:
$query->matching($constraint)->setOrderings(
array('owner.myCarSorting' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING)
)->execute();
I'm using search logic to filter and order my results but it removes records from my results when I order by a association and when that association is not always present for all records.
For example say I have a user model which can have one vehicle model but does not have to, if I have a results table where you can order by the users vehicles make I would hope all users without a vehicle record would be considered empty strings and therefore ordered all at the beginning followed by the other user records which have vehicles ordered by the make name.
Unfortunately all the user records which do not have a vehicle are removed from the results.
Is there anyway round this and still use search logic as I find it extremely useful
I think you'll have to explicitly assign a default vehicle that has an empty name