Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filter out-of-stock child items from configurable products in category view #4111

Closed
2 of 5 tasks
cewald opened this issue Feb 20, 2020 · 6 comments
Closed
2 of 5 tasks
Labels
feature request Requests for new features. Please be as specific as possible and provide proposal API if it you can P3: Normal Priority mark - normal priority

Comments

@cewald
Copy link
Contributor

cewald commented Feb 20, 2020

Current behavior

If you enable to hide out-of-stock products in category list with products.listOutOfStockProducts config value, it hides items that are out-of-stock. But its only working seriously for simple products. If I have a product with four configurable options which are all out of stock, the "parent" product will still be visible. Also the filter for super-attributes like size will work but not really fulfill it's purpose to show just items with an available size or whatever.

This isn't really a bug, more a missing feature – but I think it's important for a consistent shopping experience.

I already did some research. It should be possible to query this in ES by using nested properties for configurable_children and configurable_children.stock in product index. The problem is that they are both just array/object types yet. So, the indexer/data-pump had to be modified to create the correct mapping. Then it would be best to add a new property for stock-check in the VSF actions and extend the DivanteLtd/storefront-query-builder to handle it (in first place for ES) at the API side.

I will post an example index and query for ES below to explain my idea.
I think this #3780 is heading in the same direction – but I'm aiming especially at a stock filter feature.

Expected behavior

If I enable to hide out-of-stock items the filter in category-view for super-attributes like size should only show available items.

Repository

Latest develop

Can you handle fixing this bug by yourself?

  • YES
  • NO

Which Release Cycle state this refers to? Info for developer.

Pick one option.

  • This is a bug report for test version on https://test.storefrontcloud.io - In this case Developer should create branch from develop branch and create Pull Request 2. Feature / Improvement back to develop.
  • This is a bug report for current Release Candidate version on https://next.storefrontcloud.io - In this case Developer should create branch from release branch and create Pull Request 3. Stabilisation fix back to release.
  • This is a bug report for current Stable version on https://demo.storefrontcloud.io and should be placed in next stable version hotfix - In this case Developer should create branch from hotfix or master branch and create Pull Request 4. Hotfix back to hotfix.

Environment details

  • Browser: All
  • OS: All
  • Node: All
  • Code Version: develop
  • Data-Source: Magento1 /w DivanteLtd/magento1-vsbridge-indexer
@cewald cewald added the bug Bug reports label Feb 20, 2020
@cewald
Copy link
Contributor Author

cewald commented Feb 20, 2020

Okay I made a small test index with some sample products (sorry for the long post).

The query (at the bottom) should only show items where the child is available or simple-products – this way we could achieve the desired behavior to filter products with out-of-stock super-attributes.

PUT /test-index-for-product-stock
{
  "mappings": {
    "product": {
      "properties": {
        "configurable_children": {
          "type": "nested",
          "properties": {
            "size": {
              "type": "long"
            },
            "stock": {
              "type": "nested",
              "properties": {
                "is_in_stock": {
                  "type": "boolean"
                },
                "min_qty": {
                  "type": "float"
                },
                "notify_stock_qty": {
                  "type": "float"
                },
                "qty": {
                  "type": "float"
                },
                "stock_status": {
                  "type": "long"
                },
                "use_config_notify_stock_qty": {
                  "type": "boolean"
                }
              }
            }
          }
        },
        "name": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        },
        "type_id": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        }
      }
    }
  }
}

POST /test-index-for-product-stock/product
{
  "type_id": "configurable",
  "name": "Test-Product #1",
  "configurable_children": [
    {
      "size": 20,
      "stock": {
        "is_in_stock": true,
        "qty": 3
      }
    },
    {
      "size": 21,
      "stock": {
        "is_in_stock": true,
        "qty": 8
      }
    },
    {
      "size": 22,
      "stock": {
        "is_in_stock": true,
        "qty": 8
      }
    },
    {
      "size": 23,
      "stock": {
        "is_in_stock": true,
        "qty": 5
      }
    },
    {
      "size": 24,
      "stock": {
        "is_in_stock": true,
        "qty": 2
      }
    }
  ],
  "stock": {
    "is_in_stock": true,
    "qty": 0
  },
  "status": 1,
  "visibility": 4,
  "category_ids": [
    1,
    2,
    3
  ]
}

POST /test-index-for-product-stock/product
{
  "type_id": "configurable",
  "name": "Test-Product #2",
  "configurable_children": [
    {
      "size": 23,
      "stock": {
        "is_in_stock": true,
        "qty": 8
      }
    },
    {
      "size": 24,
      "stock": {
        "is_in_stock": true,
        "qty": 5
      }
    },
    {
      "size": 25,
      "stock": {
        "is_in_stock": true,
        "qty": 2
      }
    }
  ],
  "stock": {
    "is_in_stock": true,
    "qty": 0
  },
  "status": 1,
  "visibility": 4,
  "category_ids": [
    1,
    2,
    3,
    4
  ]
}

POST /test-index-for-product-stock/product
{
  "type_id": "simple",
  "name": "Test-Product #3",
  "category_ids": [
    2,
    3
  ],
  "stock": {
    "is_in_stock": true,
    "qty": 4
  },
  "size": 20,
  "status": 1,
  "visibility": 4
}

POST /test-index-for-product-stock/product
{
  "type_id": "simple",
  "name": "Test-Product #4",
  "category_ids": [
    1,
    2
  ],
  "stock": {
    "is_in_stock": true,
    "qty": 1
  },
  "size": 21,
  "status": 1,
  "visibility": 4
}

POST /test-index-for-product-stock/product
{
  "type_id": "simple",
  "name": "Test-Product #5",
  "category_ids": [
    2,
    3
  ],
  "stock": {
    "is_in_stock": false,
    "qty": 1
  },
  "status": 1,
  "visibility": 4
}

GET /test-index-for-product-stock/product/_search

GET /test-index-for-product-stock/product/_search
{
  "query": {
    "bool": {
      "must": [
        // {
        //   "terms": {
        //     "category_ids": [1,2]
        //   }
        // },
        // Default filters
        {
          "terms": {
            "status": [0,1]
          }
        },
        {
          "terms": {
            "visibility": [2,3,4]
          }
        }
      ],
      "should": [
        // Filter for configurable products
        {
          "bool": {
            "must": [
              {
                "term": {
                  "stock.is_in_stock": true
                }
              },
              {
                "nested": {
                  "path": "configurable_children",
                  "query": {
                    "bool": {
                      "must": [
                        {
                          "terms": {
                            "configurable_children.size" : [20,21]
                          }
                        },
                        {
                          "nested": {
                            "path": "configurable_children.stock",
                            "query": {
                              "bool": {
                                "must": [
                                  {
                                    "term": {
                                      "configurable_children.stock.is_in_stock": true
                                    }
                                  },
                                  {
                                    "range": {
                                      "configurable_children.stock.qty": {
                                        "gt": 0
                                      }
                                    }
                                  }
                                ]
                              }
                            }
                          }
                        }
                      ]
                    }
                  }
                }
              }
            ]
          }
        },
        // Filter for simple products
        {
          "bool": {
            "must": [
              {
                "terms": {
                  "size": [20,21]
                }
              },
              {
                "term": {
                  "stock.is_in_stock": true
                }
              },
              {
                "range": {
                  "stock.qty": {
                    "gt": 0
                  }
                }
              }
            ],
            "must_not": [
              {
                "term": {
                  "type_id": "configurable"
                }
              }
            ]
          }
        }
      ],
      "minimum_should_match": 1
    }
  }
}

@pkarw pkarw added the P3: Normal Priority mark - normal priority label Feb 20, 2020
@cewald
Copy link
Contributor Author

cewald commented Feb 26, 2020

I added an optional stock filter to the buildQueryBodyFromSearchQuery method of DivanteLtd/storefront-query-builder – I refactored the method to create the above query:
https://github.com/icmaa/storefront-query-builder#feature/add-stock-filter-for-configurable-products

But there are some pitfalls and minor changes needed:

  • As we aim an agnostic approach for the storefront-query-builder, the bodybuilder query-chain-object is just added as a parameter to the buildQueryBodyFromSearchQuery. And as we use nested queries, we need a plain bodybuilder object to build them. That's why I wanted to use the build in clone method – but this comes with version 2.2.21 and the API is using 2.2.13 or something I think. So I needed to upgrade the API's bodybuilder version.
  • I also needed to upgrade my data-pump to map the fields configurable_options, configurable_children and configurable_children.stock as nested fields. I did this by extending the DivanteLtd/magento1-vsbridge-indexer – but this should been done for all data-pumps.
  • We need to implement it into the VSF – this is the easiest part. I just added my new custom filter to the desired query like searchQuery.applyFilter({ key: 'stock', scope: 'catalog', value: null }). In my case it was enough to put it into the loadCategoryProducts actions – but this sure should be configurable and need to be added at other places as well.
  • And I needed to add all available configurable product filter attributes to a config value of vue-storefront-api: "products": { configurable_options: [ "size", "color", ... ] } to let the storefront-query-builder know which filter are for children and which are regular catalog-scoped-filters for parent products.

It is working – I can now filter for only available products and its configurable child-products by availability too.

This is a lot – I'm not sure if just us are in need of this feature – or this is good feature for others too.

If so, I would could go on to create a more configurable and implemented approach.

@pkarw
Copy link
Collaborator

pkarw commented Feb 26, 2020

Hi @cewald I really appreciate the effort you took to make this feature happen - it's not anything simple. Some questions:

  1. Is there a way to make this child-queries more general (not limited to configurable_children but to let the users just query any nested structure?) I wouldn't like to bind the storefront-query-builder to the specific Magento-like data structures we're currently using. I guess we should stick to applyFilter query interface for any type of queries (?)

  2. Please create a PRs to other indexers as well - especially mage2vuestorefront which is still default choice for many of the users + magento2-vsbridge-indexer

That's it from my side. After this generalization + applying the shema change to the indexers I'm OK with merging this in

@pkarw pkarw added the feature request Requests for new features. Please be as specific as possible and provide proposal API if it you can label Feb 26, 2020
@pkarw
Copy link
Collaborator

pkarw commented Feb 26, 2020

I've added feature request label as this is actually a new feature - extending the query mechanism

@pkarw pkarw removed the bug Bug reports label Mar 2, 2020
@TerrorSquad
Copy link

What's the status on this?

Is it merged?

@cewald
Copy link
Contributor Author

cewald commented Apr 9, 2020

It's not – but there is a project in merging process which supports this kind of filter as an extension.
Means, you can then extend the storefront-query-builder from your storefront-api or vue-storefront-api, see: vuestorefront/storefront-query-builder#6.

There is a cookbook-recipe in the docs of this PR #4234 that describes how it works.

So, once you are using the api-search-query adapter and storefront-query-builder you are able to add the above filter inside the storefront-api or vue-storefront-api on your own.

If it helps, this is how our custom stock-filter looks like (like described above): StockFilter.ts.

I think we can close this issue in favour of #4234

@pkarw pkarw closed this as completed Apr 9, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request Requests for new features. Please be as specific as possible and provide proposal API if it you can P3: Normal Priority mark - normal priority
Projects
None yet
Development

No branches or pull requests

3 participants