Repository navigation
Device Discovery Filter and Sort
Status: Draft — needs decisions on the open questions
-
Ask: which filter/sort params are needed beyond the ones available
today (
hostname,friendlyName,tags,activated,discovered)? #1077 proposesskuType,currentMode,lmsInstalled,osName, plussort/order.
| Issue / PR | What it is |
|---|---|
| #858 | Original device-discovery request. |
| #1093 | Parent issue: dashboard + filter/sort + export. Says sort should work "by any column". |
| #1077 |
This task. Adds filter[skuType], filter[currentMode], filter[lmsInstalled], filter[osName], sort, order. |
| #1210 | Same problem for currentMode/discovered. Solved by denormalizing into columns. |
| #1211, #1214 | #1210 implementation (new columns; stats + activated/discovered filters). |
The new filter fields are not columns on devices; they are nested inside
deviceInfo. We support three databases (Postgres, SQLite, MongoDB) behind
one Repository interface, and deviceinfo is stored as a plain string on
all three (not a native document even on Mongo). There is no single portable
way to filter or sort on values inside it.
Source: internal/controller/httpapi/v1/devices.go (get), odata.go,
export.go.
| Query param | Values | Behaviour |
|---|---|---|
hostname |
string | Exact match on the hostname column (GetByColumn). |
friendlyName |
string | Exact match on the friendlyname column (GetByColumn). |
tags |
comma-separated list, e.g. tags=lab,floor2
|
Matches devices whose tags contain the given tags (GetByTags). |
activated |
true |
Managed devices: discovered is not true, or currentmode has a value other than "not activated" (GetActivated). Includes legacy devices with no recorded mode. |
discovered |
true |
Discovered-but-not-activated devices: discovered = true and currentmode is empty/NULL/not activated (GetDiscovered). |
Notes / current limitations:
-
Filters don't combine. The handler checks them in a fixed order and
uses only the first one that is set:
hostname→friendlyName→tags→activated=true→discovered=true→ no filter. Every other filter param is ignored. -
activated/discoveredonly react to the literal valuetrue; any other value (includingfalse) falls through to the unfiltered list. - There is no
sort/ordersupport yet. Results are ordered byguid.
No filter params. Returns every device for the tenant, capped at 500 records.
No filter params. Returns totalCount, activatedCount, and
discoveredCount for the tenant, using the same activated / discovered
rules as above.
No filter params. Returns the distinct tags used by the tenant's devices.
| Query param | Backed by | Type |
|---|---|---|
filter[skuType] |
new skutype column, derived from the fwSku bitmask |
string (vPro / ISM / …) |
filter[currentMode] |
currentmode column (from #1210) |
string (ACM / CCM / …) |
filter[lmsInstalled] |
new lmsinstalled column |
bool |
filter[osName] |
new osname column |
string |
sort + order
|
any of the above, plus existing top-level columns (hostname, friendlyName, …) |
— |
Provisioned State (from #1093) is covered by currentMode.
The same three options were weighed for #1210 in API contract update & DB query mechanism to fetch Device Stats. They apply equally here.
| Option | Pros | Cons | Verdict |
|---|---|---|---|
Denormalize — copy each field from deviceInfo into its own column on write |
Plain queries, same behaviour on all 3 DBs, indexable, DB-side paging | One migration + write-path code to keep columns in sync | Recommended (used by #1210) |
| Query the JSON blob directly | No new columns | Postgres (::jsonb ->>) and SQLite (json_extract) need different SQL; not possible on Mongo (blob is a string) |
Rejected |
Filter in Go — load rows, parse deviceInfo, filter in memory |
No schema change | Loads every device into memory; paging can't be pushed to the DB; doesn't scale | Rejected |
Follow the #1210 pattern:
- Add nullable columns to
devices:skutype,lmsinstalled,osname(currentmodeanddiscoveredalready exist). - Fill them from
deviceInfoon every insert/update.deviceInfostays the source of truth; the columns are a queryable copy. - Derive
skutypefrom thefwSkubitmask (below). - Implement filter/sort twice behind the same
Repositorymethod: SQLWHERE/ORDER BYininternal/usecase/sqldb/device.go,bson.Mfilter ininternal/usecase/nosqldb/mongo/device.go.
fwSku is a bitmask stored as a decimal string. The frontend already decodes it this way (sample-web-ui devices.component.ts, branch device-type-column):
func skuTypeFromFWSku(fwSku string) string {
n, err := strconv.Atoi(fwSku)
if err != nil {
return ""
}
switch {
case n&0x10 != 0:
return "ISM"
case n&0x08 != 0:
return "vPro"
default:
return "" // or "Non-vPro" — see open question below
}
}This runs once when deviceInfo is written and the result is stored in the
skutype column.
- Is the #1077 list (
skuType,currentMode,lmsInstalled,osName,sort,order) the full set, or do we need any more filter/sort params (e.g. Provisioned State, AMT enabled status from #1093) in this same pass? -
sort=currentMode— alphabetical, or lifecycle order (Pre-Prov→CCM→ACM)? Lifecycle order needs an explicit rank mapping; plain alphabetical can also differ between databases. - Do multiple
filter[...]params combine with AND? Should they also combine with the existingtags/hostname/friendlyNameparams (today only one filter is applied)?
- Denormalize every filter/sort field, or only the ones inside
deviceInfo? - Invalid
sortcolumn orordervalue — return 400, or fall back to the default sort?