Recently I had an issue regarding a site that runs 2 stores, but shared the same ROOT category between the stores, but had seperate categories per store (categories are enabled at store level)

So, I posted a question on the Algolia community website.

https://discourse.algolia.com/t/multiple-stores-shared-catalog-separated-categories/2433

Unfortunately, as per answer given, there is no 'out-the-box' solution, but the required functionality was not difficult to acheive

As per the answer provided by jan.petr of the Algolia team, the usage of an event is what is needed.

The issue (again) was that just limiting by store id in the collection woudl not work, since all the producst belong to both stores.

The product collection thus had to be limited by specific category ids. To make that happen, I would need to get the correct (ACTIVE) categories per store.

In the store setup, all categories are disabled at the Default Level, and each store has the categories (for that store) enabled at Store Level.This also facilitates menu generation, so as to not have Categories from one store appear in dropdown menu of another. As noted before, all products are shared between the stores, to facilitate cart hopping between stores, and retaining products from both stores in the cart at all times.

The first step was to get the event going. This is placed in the relevant EVENTS tag in a module config.xml

<algolia_before_products_collection_load>
<observers>
<enjo_catalog_algolia_before_products_collection_load>
<class>enjo_catalog/observer</class>
<method>algolia_before_products_collection_load</method>
</enjo_catalog_algolia_before_products_collection_load>
</observers>
</algolia_before_products_collection_load>

This will simply place an observer on the given event `algolia_before_products_collection_load`

Nothing special there, very standard magento convention

The code in the observer is as follows:

public function algolia_before_products_collection_load($observer)
{
$categories = Mage::helper('catalog/category')->getStoreCategories($observer->getStore(), true);
$categoryIds = $categories->getAllIds();
$collection = $observer->getCollection();
$collection->joinField('category_id','catalog/category_product','category_id','product_id=entity_id',null,'left')
->addAttributeToFilter('category_id', array('in' => $categoryIds));
$collection->getSelect()->group('e.entity_id');
$collection->load();
}

You will notice one oddity here. The method

getStoreCategories

requires an intial param to enable sorting. This is supposed to be a bool (true or false)

But, as you can see I am passing the given store id here.

The reason is that I am bypassing the built-in object cache that builds the given store categories in the routine.

You can see the cache_id consists of a few params, of whichone is the sorting flag:

$cacheKey = sprintf('%d-%d-%d-%d', $parent, $sorted, $asCollection, $toLoad);
if (isset($this->_storeCategories[$cacheKey])) {
return $this->_storeCategories[$cacheKey];
}

So, on subsequent calls to the routine, the cache is resulted, thus improving performance.

However, if the next store request to get the categories are passed, I will simply get back the cache from the previosu store, since there is no change in the given cache key.

Since the first param is not limited to being a boolean, I am passing the store id, thus creating a unique cache id per store, still retaining performance improvements on multiple calls for that stores category data during the indexing process.

It does not matter if the results are ordered or not.

The last thing you need to ensure is that each store has its own unique 'Index Name Prefix' at the Store Level config, else the indexers will overwrite each other!