Personalized restaurant ranking without the popularity trap

Personalized Restaurant Ranking is where most food delivery apps and large WooCommerce catalogs still cut corners. The standard advice is to sort by most sales or highest rating. That reads fine in a spec and it costs you conversions. I have seen catalogs with thousands of items underperform because every discovery widget was a static list that ignored what the user came in for.

The reason is that users are lazy. If what they want is not in the first 10 to 12 positions, they leave. When your Italian shelf and your Healthy shelf both open with the same pizza chain because it has the most orders, that is not discovery, it is a popularity trap. The way out is a Two-Tower Embedding variant.

Why static popularity ranking fails

On a high-traffic discovery surface, a global popularity sort is optimized across the whole catalog and blind to context. McDonald’s can be the most popular restaurant under both “Burgers” and “Ice Cream.” Someone browsing the Ice Cream category probably wants a dedicated dessert shop, not a Big Mac. Popularity sorting pins the heavy hitters to the top and buries the niche candidates that actually match.

These collections also move around. Seasonal campaigns and new business lines mean you cannot train a dedicated model for every tag-based slice of the catalog. The system has to generalize to new data on day one, which is what a Two-Tower model buys you for Personalized Restaurant Ranking.

The two-tower embedding strategy

A two-tower model trains two encoders side by side, a query tower for the user and an item tower for the restaurant. The value is in that split. Restaurant embeddings get precomputed offline and only the user vector is built at request time, which keeps latency where a high-traffic app needs it.

You do not need Uber-scale infrastructure to make this work. Rather than fine-tune a large language model, reuse a frozen encoder such as TinyBERT for the restaurant side. That gives you semantic coverage without paying for a training run. Add explicit features like price and rating and you have a usable representation of the catalog.

As I argued in the post on why vector similarity fails, raw similarity is not enough. For ranking, filter the user’s interaction history by the current context, the “Ice Cream” tag in this example, before you average embeddings. Skip that and your Personalized Restaurant Ranking keeps answering with a long-term love of burgers instead of what the user wants right now.

Refining the ranking head

Multi-task learning is what stabilizes the scores. Instead of predicting an order on its own, have the model predict clicks, add-to-basket and orders jointly. That gives you a funnel constraint: P(order) ≤ P(add-to-basket) ≤ P(click). The scores come out more consistent and far less jumpy when the data is sparse.

// Naive PHP Popularity Approach (What to avoid)
$query = "SELECT * FROM restaurants 
          WHERE category = 'Burgers' 
          ORDER BY total_orders DESC 
          LIMIT 12;";

// Pragmatic Approach: Scoring via pre-computed embeddings
// bbioon_get_personalized_ranking( $user_id, $candidate_ids, $context_tag )

Once the logic sits in its own ranking layer, it gets reused. The same model that orders your home screen widgets can order search results and internal ad placements too.

If Personalized Restaurant Ranking is eating your dev hours, I can take it on. I have been wrestling with WordPress and high-traffic discovery layers since the 4.x days.

What to take away

  • Filter user history by the current tag before averaging, so context drives the score.
  • Reuse frozen TinyBERT embeddings for the item tower instead of training your own encoder.
  • Predict the whole funnel, click to basket to order, to keep the ranking stable.
  • Keep the towers decoupled so restaurant vectors precompute offline and online scoring stays fast.
author avatar
Ahmad Wael
I'm a WordPress and WooCommerce developer with 15+ years of experience building custom e-commerce solutions and plugins. I specialize in PHP development, following WordPress coding standards to deliver clean, maintainable code. Currently, I'm exploring AI and e-commerce by building multi-agent systems and SaaS products that integrate technologies like Google Gemini API with WordPress platforms, approaching every project with a commitment to performance, security, and exceptional user experience.