Assign weight to attribute based on there value is grater or lesser then some value: algolia - algolia

Hi I am trying to find a search solution where I can assign a weight (x point) to an attribute if its value is greater or smaller then some value ( Y value)
Like if the price is greater then 10 USD then assign 5 points to the item, and I am assigning points on multiple attribute, then get the list of item on the bases of total points in asc or desc order, how can i do this in algolia

Algolia doesn't work with weights, but with a tie-breaking strategy that decides how to rank results based on their attributes. This strategy is static, and set at indexing time.
In your case, you're willing to rank results by a multitude of criteria, including by price. The easiest way to do this is to use the customRanking attribute and set each attribute that should play a role in the ranking strategy. For example, if you want more expensive items to be ranked higher, you can do the following (JavaScript example, but you have a choice between 11 different languages):
index.setSettings({
customRanking: [
'desc(price)'
]
});
Notice the customRanking property takes an array. You can pass several criteria for your custom ranking, and they will be taken into account in the defined order, if the engine can't break the tie.
Since you're working with prices, you may end up in a case where two prices are so close that it makes no sense to break the tie on them; and you'll want to move on to the next criterion. In this case, you can add a new attribute with a rounded price and use this one as the custom ranking attribute. There's a guide in the documentation on that topic.

Related

Doctrine : PostgreSQL group by different than select

I have two tables :
user and activityHistory (that has a key to the user)
I am trying to query activityHistory while grouping by user and postgreSQL does not allow me to do that WHILE Sqlite does allow me
return $qb
->select('a.id')
->leftJoin('a.user', 'user')
->leftJoin(
ActivityHistory::class,
'b',
'WITH',
'a.id = b.id AND a.createdAt > b.createdAt'
)
->groupBy('user.id')
->orderBy( 'a.createdAt','ASC' )
->getQuery()->getArrayResult();
I am getting this error only with postgreSQL : Grouping error: 7 ERROR: column "a0_.id" must appear in the GROUP BY clause or be used in an aggregate function
The point is I don't want to groupBy activityHistory id, I only want it to be selected, how can I do ? (I heard about aggregate but I think this works only with functions like SUM etc)
First of all, let's clarify how aggregation works. Aggregation is the act of grouping by certain field(s) and selecting either those fields or calling aggregation functions and passing ungrouped fields.
You misunderstand how this works - hence the question -, but let me provide you a few very simple examples:
Example 1
Let's consider that there is a town and there are individuals living in that town. Each individual has an eye color, but, if you are wondering what the eye color of the people of the town is, then your question does not make sense, because the group itself does not have an eye color, unless specified otherwise.
Example 2
Let's modify the example above by grouping the people of the town by eye color. Such an aggregation will have a row for all existent eye colors and you can select the eye color, along with the average age, number of individuals, etc. as you like, because you are grouping by eye color
Your example
You have users and they are performing actions. So, an activity is performed by a single user, but a user may perform many activities. So, if you want to group by your user id, then the "eye color" that you are not grouping by here is the history id.
You will have a single record for any user, so you are grouping multiple history items into the same row and after the grouping, asking about the history item's id does not exist.
But, you can use string_agg(some_column, ',') which will take all the values you have and put them all into a string of values separated by comma.
You can explode(',', '$yourvalues) in PHP to convert such a value into an array.

Field Calculator Sum Across Other Layer

I'm hoping to reach into another layer and sum the relevant fields with Field Calculator, but am having some trouble.
I have the layers "Countries", and "Settlements". I want to create a new field in Countries called Population, which is the sum of all the matching Settlements. That is, the sum of all Settlements where Settlements->Owner_Name matches Countries->Name. Any advice on how to go about this? I haven't found a lot of detailed tutorials about reaching across layers.
Add a field to the Countries layer, named Population with this expression:
aggregate(
layer:='Settlements',
aggregate:='count',
expression:="id",
filter:="Owner_Name"=attribute(#parent, 'Name')
This should work.

how to create multiple filters from one dimension tableau (10)?

I have a two dimensions with the following data set
Original Dimension
I would like to create two filters that can be used to filter my dashboard. These two filter would be called - "Product_Type" and "Product" and should have the following drop downs:
Filter Breakdown
To summarize, I am looking for a way to take my original dimension and create the filter breakdowns so that in my dashboard I can have two filter (Product_type and Product). When i click on the Product_type filter, i should be able to see "overall", "fruits", "vegetable" and "leafy. Based on my selection , the Product filter should automatically show only the relevant values - i.e. if i picked "leafy" it should only show "spinach" and "kale". Finally if i select "kale" my entire dashboard should change accordingly.
Looking for a way to do this without drastically affecting the performance.
update: i was able to achieve this by creating multiple parameters and then using the pop window technique (https://vimeo.com/107352893) - but this has affected my workbook performance very badly. Need another solution!!
You should be able to accomplish your desired functionality by creating a calculated field for product type and a CASE statement to classify the products based on the product type you specified your image.
Further, filters can be configured to display available values based on the other filters in place. I would recommend having your Product Type filter a the "context" or "data-source" level, and your product filter at the lowest level, so that the selectable options for the product filter can be displayed using the "only relevant Values" option and limits the products that can be chosen.
Please see the following link for some more information on filtering hierarchies
Performance Related
General Filtering
Blog Post
"Only relevant values - Specifies which values to show in the filter. When you select this option other filters are considered and only values that pass these filters are shown. For example, a filter on State will only show the Eastern states when a filter on Region is set. You can use the toggle at the top of the filter card to switch between this option and the All Values in Database option.
All values in database - Specifies which values to show in the filter. When you select this option all values in the database are shown regardless of the other filters on the view.
All values in context (Tableau Desktop only) - When one of the filters in the view is a context filter, select this option on a different filter to only display values that pass through the context filter. For more information, see Improve View Performance with Context Filters."

Geofire TableView - CircleQuery Users for leaderboard [duplicate]

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!

Sphinx: Show all results order by previous searches

I use SphinxQL for searching and filtering in product database and I store last x search phrases of each user. I wonder if is it possible to show all products (all rows) to every user but with relevance on previous search.
Let's say one user sought for mobile phones (iphone, galaxy s7...), ie. electronics category. I want to show him all products randomly, but products from category electronics more often and products with those searched keywords even more often.
Is it even possible with Sphinx?
Thanks and sorry for english.
An alternative, would be perhaps to create random numbers attached to each result. A high and a low number, with an overlapping range.
sql_query = SELECT id, RAND()*100 AS rand_low, (RAND()*100)+50 AS rand_high, ...
sql_attr_uint = rand_low
sql_attr_uint = rand_high
Can then arrange the ranking expression to pick either of these numbers depending on if matches or not, and sort by the result.
SELECT id FROM index WHERE MATCH('_all_ MAYBE electronics MAYBE (galaxy s7)')
OPTION ranker=expr('IF(doc_word_count>1,rand_high,rand_low)');
Will be mixed up. But results that match one of the words, have a greater chance of showing up first (because use the weighted random number) - its still only a chance, because a rand_high CAN still be smaller than rand_low.
... can change the size of the number 'overlap' to tweak the mix of matching/non matching results.
(added as a new answer as its a quite differnt idea, although uses the same 'all' keyword)
Sphinx doesn't have a 'mode' to just do that. But can get very close...
Can use MAYBE operator
MATCH('_all_ MAYBE electronics MAYBE (galaxy s7)')
The complication is need a way to match all products. Depending on your data you may already have a word can use (eg word like 'the' in every single product), or add the word to every document, during indexing.
... using MAYBE allows the matching results to have a higher weight.
But you dont want to sort strictly by weight. So need a different alogithm, something to shuffle the results a bit (as you not really wanting 'random'!)
SELECT id, IDIV(id/10000) AS int,WEIGHT() AS w
FROM index WHERE MATCH('_all_ MAYBE electronics MAYBE (galaxy s7)')
ORDER BY int DESC, w DESC;
This creates banding by ID, as in theory results can be spread over all the id-space will mix them up a bit. But the category results will still tend to be shown first within each band.
If you have one a different attribute other than ID might be better, something more spread out. Or can add a deliberate random attribute to results)
... there are all sort so variations, your imagination is the only limitation, this basic techqiue can be used to mix things up quote a bit.
(There are other possiblities, Sphinxes little known GROUP N BY function, can be used to produce a sampling search result. This is isnt random, but it might give the similar enough result - ie just mixing up results)