Search products by category parent

Hi

I have a category entity which has
parent: category
name: string
and I create a tree of categories

I than have product with associated field: category
How can I filter (in a browse screen) products which belong to one category AND ALL its child categories?

Thank you

Hi,
Let’s begin with a screen XML descriptor. We need two data sources:

  1. first data source to load Products
  2. second data source to load Categories
<dsContext>
    <groupDatasource id="productsDs"
                     class="com.company.filterentitiesconditionhierarchy.entity.Product"
                     view="product-view">
        <query>
            <!&#91;CDATA&#91;select e from filterentitiesconditionhierarchy$Product e&#93;&#93;>
        </query>
    </groupDatasource>
    <hierarchicalDatasource id="categoriesDs"
                            class="com.company.filterentitiesconditionhierarchy.entity.Category"
                            hierarchyProperty="parentCategory"
                            view="category-view">
        <query>
            <!&#91;CDATA&#91;select e from filterentitiesconditionhierarchy$Category e&#93;&#93;>
        </query>
    </hierarchicalDatasource>
</dsContext>

The screen contains only LookupField and Table:

<lookupField id="categoriesField"
             caption="Choose category:"
             optionsDatasource="categoriesDs"/>
<groupTable id="productsTable"
            width="100%">
    <actions>
        ...
    </actions>
    <columns>
        <column id="name"/>
        <column id="price"/>
        <column id="category"/>
    </columns>
    <rows datasource="productsDs"/>
    <rowsCount/>
    <buttonsPanel id="buttonsPanel"
                  alwaysVisible="true">
        ...
    </buttonsPanel>
</groupTable>

Then we subscribe to LookupField value change:

@Inject
private HierarchicalDatasource<Category, UUID> categoriesDs;
@Inject
private GroupDatasource<Product, UUID> productsDs;
@Inject
private LookupField categoriesField;

@Override
public void ready() {
    // save all products
    final List<Product> allProducts = new ArrayList<>(productsDs.getItems());

    categoriesField.addValueChangeListener(e -> {
        Category category = (Category) e.getValue();

        if (category == null) {
            // show all prducts if category is not set
            putProductsToDs(allProducts);
        } else {
            // filter suitable products by the selected category
            List<Product> suitableProducts = collectSuitableProducts(category, allProducts);
            // update data source
            putProductsToDs(suitableProducts);
        }
    });
}

Filtering and collecting Products:

private List<Product> collectSuitableProducts(Category category, List<Product> allProducts) {
    List<Category> categories = collectCategoriesRecursively(category);

    return allProducts.stream()
            .filter(p -> categories.contains(p.getCategory()))
            .collect(Collectors.toList());
}

Walking through Categories tree and collecting items:

private List<Category> collectCategoriesRecursively(Category parentCategory) {
    List<Category> categories = new ArrayList<>();
    categories.add(parentCategory);

    for (UUID uuid : categoriesDs.getChildren(parentCategory.getId())) {
        Category category = categoriesDs.getItem(uuid);

        //noinspection ConstantConditions
        if (categoriesDs.getChildren(category.getId()).isEmpty()) {
            categories.add(category);
            continue;
        }

        categories.addAll(collectCategoriesRecursively(category));
    }

    return categories;
} 

Updating data source:

private void putProductsToDs(List<Product> products) {
    productsDs.clear();
    products.forEach(productsDs::addItem);
    ((DatasourceImpl) productsDs).setModified(false);
}

Best regards,
Daniil.