ALL YOU EVER

wanted to know

about

filters

AND

facets

in SPHERE.IO

API endpoints


Products: by ID, query

  • Full product (both current and staged)
  • Request to MongoDB


Product-projections: by ID, query, search

  • Partial product (current or staged only)
  • Request to MongoDB (query) and ElasticSearch (search)

products by ID


GET /products/{id}
{  "id": "04b8c147-0065-4ed6-8d30-b145215d4dd6",
  "masterData": {
    "staged": {
    	"masterVariant": { ... },
    	"variants": [ ... ],
    },
    "current": {
    	"masterVariant": { ... },
    	"variants": [ ... ],
    },
    "hasStagedChanges": false,
  },
}

products query


GET /products where={predicate}
sort={criteria} & limit=20 & offset=0
{
  "results": [
    {
      "id": "04b8c147-0065-4ed6-8d30-b145215d4dd6",     
      "masterData": {
        "staged": {
	  "masterVariant": { ... },
	},
	"current": {
	  "masterVariant": { ... },
	},
        "hasStagedChanges": false,
      },
    }
  ],
  "total": 100, "count": 20, "offset": 0
}

product-projections by ID


GET /product-projections/{id} ? staged=false
{
  "id": "04b8c147-0065-4ed6-8d30-b145215d4dd6",
  "masterVariant": { ... },
  "hasStagedChanges": false,
}

product-projections query


GET /product-projections staged=false
where={predicate} & sort={criteria}
limit=20 & offset=0

 {
  "results": [
    {      
      "id": "04b8c147-0065-4ed6-8d30-b145215d4dd6",
      "masterVariant": { ... },
      "hasStagedChanges": false,
    }
  ],
  "total": 100, "count": 20, "offset": 0
}

product-projections search


GET /product-projections/search ? lang=en  staged=false  text={search} &
facet={expression}  filter={expression} &
filter.query={expression} filter.facets={expression}
& sort={criteria} & limit=20 & offset=0
 {
  "facets": { ... },
  "results": [
    {
      "id": "04b8c147-0065-4ed6-8d30-b145215d4dd6",
      "masterVariant": { ... },
      "hasStagedChanges": false,
    }
  ],
  "total": 100, "count": 20, "offset": 0
}

search query parameters

 
  

search query parameters


search query parameters


search query parameters


search query parameters


search expressions


On product attributes:

  • category categories.id
  • price variants.price.centAmount
  • custom attribute variants.attributes.{name}
  • custom enum attribute variants.attributes.{name}.key


With expression types:

  • terms {x}, {y}, {z}
  • ranges {from} to {to}

term facets

GET /product-projections/search lang=de
facet=variants.attributes.color.key

{
  "facets": {
    "variants.attributes.color.key": {
      "terms": [
        { "count": 7, "term": "red" },
        { "count": 2, "term": "blue" }
      ],
      "total": 9,       // variants matching some returned term
      "missing": 3,     // variants with no matching term (no value)
      "other": 0,       // variants excluded from total (limit 50)
      "type": "terms"
    }
  }
}

range facets

GET /product-projections/search lang=de
facet=variants.price.centAmount: range (0 to *)
{
  "facets": {
    "variants.price.centAmount": {
      "ranges": [
        {
          "mean": 16758.941464116207,
          "max": 590000, "min": 50,
          "from": 0, "fromStr": "0",
          "to": 0, "toStr": "",
          "count": 4165, "totalCount": 6953,
          "total": 116524920,
        }
      ],
      "type": "range"
    }
  }
}

facet alias

GET /product-projections/search lang=de
facet=variants.attributes.color.key as myFacet
{
  "facets": {
    "myFacet": {
      "terms": [
        { "count": 7, "term": "red" },
        { "count": 2, "term": "blue" }
      ],
      "total": 7,       // variants matching some returned term
      "missing": 3,     // variants with no matching term (no value)
      "other": 0,       // variants excluded from total (limit 50)
      "type": "terms"
    }
  }
}

facet alias: what to expect


facet alias: filtering alias

GET /product-projections/search ? lang=de
& facet=variants.attributes.color.key as myFacet
& filter.facets=myFacet: "red"
{
  "facets": {
    "myFacet": {
      "terms": [],
      "total": 0,       // variants matching some returned term
      "missing": 0,     // variants with no matching term (no value)
      "other": 0,       // variants excluded from total (limit 50)
      "type": "terms"
    }
  }
}

facet alias: filtering facet

GET /product-projections/search ? lang=de
& facet=variants.attributes.color.key as myFacet
& filter.facets= variants.attributes.color.key : "red"
{
  "facets": {
    "myFacet": {
      "terms": [
        { "count": 7, "term": "red" },
        { "count": 2, "term": "blue" }
      ],
      "total": 9,       // variants matching some returned term
      "missing": 3,     // variants with no matching term (no value)
      "other": 0,       // variants excluded from total (limit 50)
      "type": "terms"
    }
  }
}

SDK filter & facet expressions


SearchRequest<Product> searchRequest = sphere.products
                                      .filter(List<FilterExpression>)
                                      .facet(List<FacetExpression>);



SDK filters & facets



Conclusions

  • The search API is the most important component
    of SPHERE after the shopping cart logic.

  • When a developer is not able to do a small thing with the search API, there is no painless alternative.

  • Alternatives mean repeating pagination logic in the frontend and simulate sorting and filtering: expensive calls and expensive logic.

Conclusions

  • API documentation is confusing: own section, better structure, some diagrams, more examples will help.

  • SDK is very confusing: dozens of classes, some cases of bad naming, not intuitive relation with API.

  • The search API is very powerful, yet altogether makes it very hard to use it with complex cases.




That's all folks!

FF old

By Laura Luiz

FF old

  • 1,894