Skip to content

The Parts Database Query API

JITX comes with an optional high-level framework to manage queries to the parts database in a JITX design. To use this framework, add

import jitx/parts
to your package imports. Note that this package is not forwarded by jitx, so it is not sufficient to have import jitx.

This article mainly documents the jitx/parts/query-api package, which is forwarded by jitx/parts.

Overview: query objects and query functions

The Query API defines various types of query objects such as BaseQuery , ResistorQuery, CapacitorQuery, and so on. These serve as re-usable containers of query parameters (key-value pairs) that can used in various query functions such as create-resistor, insert-resistor, search-capacitors, etc. For example, you can write:

val my-query = ResistorQuery(resistance = 1000.,
                             mounting = "smd",
                             min-stock = 100)
pcb-module my-module :
  inst r : create-resistor(my-query)
  ...

create-resistor returns a component definition, not an instance

create-resistor and other create-[cat] query functions return component definitions, which are objects of type Instantiable, not instances. In other words,

val my-resistor = create-resistor(my-query)
operates similarly to
pcb-component my-resistor :
  ...
We have two layers of abstraction here:

  • Query objects can be reused to create multiple components.
  • Components can be reused to create multiple instances.

The latter is an intrinsic capability of JITX so we will focus mainly on the former in this article. Note that a usage like inst r : create-resistor(my-query) skips the second layer of abstraction and uses a query object to create a single instance, throwing away the intervening component definition.

Query objects are immutable

The set operations can be used to create new query objects. The old object remains unchanged. For example:

val my-query = ResistorQuery(resistance = 1000.,
                             mounting = "smd",
                             min-stock = 100)
val other-query = set(my-query, rated-power = AtLeast(0.1))
inst r1 : create-resistor(my-query)
inst r2 : create-resistor(other-query)
Here we instantiate two resistors. Both r1 and r2 will have resistance (within some tolerance) of 1kΩ and be SMD-mounted. The second one will have a rated power of at least 0.1W but the first one may or may not.

Extended example

This example offers a tour through various features of the API. All the features used will be explained in detail in later sections.

; These params will be used in all queries without needing to pass a
; query object arg.
set-global-query-defaults!(min-stock = 1, quantity-needed = 10)

; A basic query object to build off of.  Note the usage a special
; value marker, FindMinimum.  Instead of setting the parameter it is
; given with, it sets the ‘sort!’ parameter (aka ‘_sort’ in low-level
; query interface).  This is meant to replace the OPTIMIZE-FOR
; functionality from OCDB.
val general-query = BaseQuery(price = FindMinimum
                              sellers! = [JLCPCB LCSC DigiKey Future
                                          Mouser Arrow Avnet Newark])
; A more specialized, but still basic query object to build off of.
; This includes all parameters from the previous one.
val smd-query = set(general-query, mounting = "smd"
                                   case = valid-smd-pkgs("0402"))

pcb-module main :
  ; Create a ResistorQuery, which allows us to set keys specific to
  ; resistor category.
  val q1 = ResistorQuery(smd-query, resistance = 8000. +/- 500.
                                    rated-power = AtLeast(0.1)
                                    precision = (5 %)
                                    )

  ; Variant with different resistance.
  val q2 = set(q1, resistance = 1.0e3 +/- 500.0)

  ; If the query already has everything, component creation can be
  ; very terse.
  inst r1 : create-resistor(q1)
  ; We can also override any key we want at the last minute.
  inst r2 : create-resistor(q2, resistance = 12000.)
  ; Here we create a resistor directly from a base query and some
  ; explicit resistor keys.
  inst r3 : create-resistor(smd-query, resistance = 100.,
                                       rated-power = 0.25)
  ; Caution: here we do not use a query object, so the only keys
  ; respected are the global defaults and the ones explicitly given.
  ; This may well return a through-hole resistor from a seller not on
  ; our list.
  inst r4 : create-resistor(resistance = 100.)

  net (r1.p[2] r2.p[1])
  net (r2.p[2] r3.p[1])
  net (r3.p[2] r4.p[1])

  val cap-query = CapacitorQuery(smd-query, type = "electrolytic")
  insert-capacitor(r1.p[1], r4.p[2], cap-query, capacitance = 1.e-6)

; Here we show use of the special value marker FindDistinct, which
; maps to ‘_distinct’ in the low-level dbquery interface.  This will
; give us all the possible values of rated-power for 1000Ω resistors
; with our basic smd-query parameters.

println("searching for possible values of rated-power...")
println(search-resistors(smd-query, rated-power = FindDistinct,
                                    resistance = 1000.))
; prints output:
; [0.0625 0.063 0.1 0.125 0.25 0.2 0.5 0.333333 0.75 0.333 0.6]

; We can also just do a raw search.  This will return the JSON from
; the lower-level dbquery call directly.  In this case the ‘limit’
; keyword is helpful.
println("Searching for 1MΩ resistors...")
println(search-resistors(smd-query, resistance = 1.e6, limit = 1))
; prints output:
;  a boatload of json which I will not copy here

Basic structure of the Query API

Most of the functions defined by the API are subdivided into families based on the category of component they operate on. The current component categories are:

  • resistor
  • capacitor
  • inductor
  • part - a special generic category which allows working with arbitrary parts including those in other categories

Terminological note: when referring to the function families generically, [Cat] stands in for the capitalized category name like Resistor while [cat] stands for the uncapitalized category name like resistor. So we have ResistorQuery and create-resistor or PartQuery and create-part.

The standard functions defined for each category are:

  • create-[cat] - run the query against the database and use the results to define a component
  • insert-[cat] - convenience wrapper for create-[cat] which also makes an instance and nets it to two given pins
  • search-[cat]s - run the query against the database and return raw results

The query object types are: - BaseQuery - a general type of query object that can be used anywhere but cannot hold category-specific keys - [Cat]Query - a type of query object specialized for a category which can hold general keys or keys specific to the category

Each query object type has a constructor of the same name which accepts optional keyword arguments for all the kinds of keys that the query object can hold. For [Cat]Query, this is the same set of keys that are available to the query functions of that category. (There are no query functions associated to BaseQuery.)

Future version of JITX may add more categories as well as more query function in each category.

Besides these families, we have a few other standard functions:

  • BaseQuery - construct a query object which can be used as a base for any other query objects
  • to-component - convert a raw search result, e.g. from search-[cat]s, into a component definition

In addition, certain special keys have helper functions specific to those keys; these will be documented alongside the special keys.

Query functions

All query functions accept a query builder as a (usually optional) positional argument, and additionally accept keyword arguments for all the keys legal for the given category. That generally means all base keys as well as any keys specific to that category.

Passing keys directly to the query function is equivalent to creating a temporary query object first using set on the same keys, and then calling the query function on the temporary query object.

create-[cat]

Actual functions defined: create-resistor create-capacitor create-inductor create-part

create-[cat] takes

  • one optional positional argument qo: [Cat]Query|BaseQuery|PartQuery
  • any number of optional standard keyword arguments for all query keys legal for category [Cat]
  • optional keyword argument: comp-name: String

and returns an object of type Instantiable.

create-[cat] first constructs internally a final query object. This proceeds in two steps: 1. The given query object qo is converted to an [Cat]Query if it wasn't one already. If this argument is absent, a new one is created by [Cat]Query(). Note that in this case, the key-values from set-global-query-defaults! will be used. 2. All standard keyword arguments are used to extend the query object as if by set .

create-[cat] then runs this query against the database, extracts the top result while ignoring all others, internally uses to-component to define an instantiable satisfying all of the query parameters, and returns this instantiable.

If comp-name was specified, the component will use this name as if by the name = syntax in pcb-component.

insert-[cat]

Actual functions defined: insert-resistor insert-capacitor insert-inductor insert-part

insert-[cat] takes

  • mandatory positional argument pin-a: JITXObject
  • mandatory positional argument pin-b: JITXObject
  • optional positional argument qo: [Cat]Query|BaseQuery|PartQuery
  • any number of optional standard keyword arguments for all query keys legal for category [Cat]
  • additional optional keyword arguments:
    • short-trace?: TwoPinShortTrace
    • comp-name: String
    • inst-name: String

and returns an object of type Instance.

Note that pin-a and pin-b must be SinglePin instances, not Bundle or PortArray.

  1. insert-[cat] first creates a component definition as if by create-[cat], using qo, the standard keyword args, and comp-name in exactly the same way.
  2. insert-[cat] then instantiates this component, respecting inst-name if supplied. Note that this name (whether user-specified or not) is not guaranteed to be unique within a pcb-module instance.
  3. insert-[cat] identifies two pins of the instance to use by using the get-element-ports function:
    • If pins named a and c are found, they are used in that order.
    • If pins named p[1] and p[2] are found, they are used in that order.
    • Otherwise an error is thrown.
  4. insert-[cat] nets the first pin to pin-a and the second pin to pin-b
  5. if short-trace? was specified, one or both of these nets will additionally have the short-trace statement applied to it. TwoPinShortTrace is an enum with these possible values:
    • ShortTraceBoth - both
    • ShortTraceAnode - first pin only (regardless of name)
    • ShortTraceCathode - second pin only (regardless of name)
    • ShortTraceNeither - no short-trace is applied, same as default behavior
  6. insert-[cat] returns the instance.

Unlike with other query functions, after insert-[cat] is called the queried part is already completely defined and instantiated in the module. The returned object can be referred to and introspected on if needed but you do not need to add your own inst statement. If you add net statements to make more connections to the returned objects' pins, the short-trace behavior does not propagate automatically - manual short-trace statements would need to be added.

Example usage of insert-resistor and insert-capacitor

Supposing we have an instance EEPROM with pins ORG, VCC, and VSS, we might add needed passives like this:

  insert-resistor(
    EEPROM.ORG EEPROM.VCC
    helpers/R-query
    resistance = 10.e3)

  insert-capacitor(
    EEPROM.VCC EEPROM.VSS
    helpers/C-query
    capacitance = 2.2e-6
    short-trace? = ShortTraceAnode)

Notice here: - These calls will instantiate the resistor and capacitor components and create all the necessary nets. Nothing further needs to be done. - This example assumes we have a Stanza package helpers which defines query objects R-query and C-query that have all our standard search parameters for this design for resistors and capacitors, respectively. - The second call makes use of the short-trace? option which is special to insert-[cat].

search-[cat]s

Actual functions defined: search-resistors search-capacitors search-inductors search-parts

search-[cat]s takes

  • one optional positional argument qo: [Cat]Query|BaseQuery|PartQuery
  • any number of optional standard keyword arguments for all query keys legal for category [Cat] and returns an object of type Instantiable.
  • optional keyword argument: limit:Int with default value of 1000

and returns an object of type Tuple<JSON>.

search-[cat]s constructs a final query object and runs the query against the database in exactly the same way as create-[cat]. However it does not create any components, but instead just returns the raw results, up to the specified limit. The limit may not be greater than 1000.

The return value has two different possible meanings

  • If the distinct! key was specified by any method, the return value is a tuple of values whose type depends the requested distinct key. For example, for resistance it would be Tuple<Double> representing all the possible resistances in ohms. (Note that Double <: JSON.)
  • Otherwise, the return value is a tuple of raw JSON objects representing all the information needed to construct component definitions. These can be converted to Component objects of the appropriate type (such as Resistor) using to-component, which in turn can be converted to component definitions using to-jitx. See documentation for the component-types package .

exceptions

If a part search fails to find a part, a NoComponentMeetingRequirements error will be raised. This is a subtype of Exception and can be caught. For errors related to the API itself, a runtime error is thrown which normally should not be caught.

Query objects

A query object is a record of key-value pairs. Different types of query objects accept different categories of keys, to enforce type discipline. There is a query object type, [Cat]Query , for each part category [Cat], and there is also BaseQuery which is another distinct type of query object.

Although query objects are the main method of reusing key-value pairs between queries, there is another method which is to use the function set-global-query-defaults! at the beginning of the design, which bypasses the need to pass query objects into functions but can only be used once per design.

If reuse is not needed, key-value pairs can also be passed directly to query functions.

When working with query objects, there are two main ways to modify them:

  • the set function
  • passing the query object to the constructor of another query type
    • this behaves similarly to set except it also allows changing the type of query.

As mentioned in the overview, “modifying” a query object means to create a new query object using the first one as a template. The original query object still exists; individual query objects are immutable.

Base queries

A BaseQuery cannot be used directly to call any of the query functions, but you can reuse its keys by passing it as the first non-keyword argument to [Cat]Query :

val my-resistor-query = ResistorQuery(my-base-query, resistance = 1000., ...)

Queries can be persistently modified (not mutated) using the set which is defined for all of the query types. This means the result is a new query, and the original one is left unchanged:

val my-modified-query = set(my-resistor-query, resistance = 500.)
Similarly, using a base query in a [Cat]Query constructor does not modify the base query - besides making a new type of Query it has semantics similar to set .

[Cat]Query

Actual functions defined: ResistorQuery CapacitorQuery InductorQuery PartQuery

[Cat]Query, the function, constructs a query object of type [Cat]Query . [Cat]Query takes

  • one optional positional argument of type [Cat]Query|BaseQuery|PartQuery
  • optional standard keyword arguments for all query keys legal for category [Cat]

and returns

If no positional argument was given, then [Cat]Query simply constructs a query object with the given key-values. If a positional argument was given, an existing query object, then [Cat]Query first converts it to type [Cat]Query if necessary and then adds all the given keys as if by set .

set

set takes

  • one mandatory positional argument, a query object of any type
  • optional standard keyword arguments for all query keys legal for the type

and returns a new query object of the same type. The new object has values for all keys present in the given query object and additionally has all the new values specified by keyword argument. If a new value is specified for a key that was already present, it overwrites the old value.

Note that add and update are aliases for set . Stylistically, set should be preferred in new code, but there is no behavioral difference.

add

add is an alias for set .

update

update is an alias for set .

set-global-query-defaults!

set-global-query-defaults! takes

  • optional standard keyword arguments for all query keys for any category

and has no meaningful return value.

set-global-query-defaults! may only be run once per design; if it has already been run then it throws a runtime error.

Each of the given key-values will then apply to every query function for which that key is not otherwise given. So for example if min-stock is set in the global defaults, then it will apply to every query by default, but if the query builder has a different min-stock in it, or the query function itself has min-stock specified by keyword, then that value will take precedence.

The function set-global-query-defaults! accepts all keywords for all categories, and the values given will be used in any query for which no value was given for that key. This differs from the use of query objects because there is no object that has to be passed around. It is globally scoped so third-party libraries which call any of the query functions will also be affected.

The practical differences of setting key-values in the global defaults versus using query objects include:

  • They can take effect without specifying any additional argument to the query function (even a query builder).
  • They can take effect in query functions called in third-party libraries where you have no direct control over the parameters used. (But only if the library doesn't explicitly set the key.)

set-default-[cat]-query!

set-default-[cat]-query! takes

  • one mandatory (positional) argument, a query object of type [Cat]Query .

Sets the default query object for the category. Note that unlike the global defaults, this default object does not apply to anything automatically. It is intended to support convenience interfaces for libraries whose behavior depends on default query parameters for each category. The parameters in this default object can only be accessed by get-default-[cat]-query() .

The default query object for the category can only be set once per category per design.

get-default-[cat]-query

get-default-[cat]-query takes no arguments and returns the default query object for the category, which will have type [Cat]Query .

Specifying intervals for keys with numerical values

Almost all numeric parameters accept an Interval argument so that a range of acceptable values can be expressed. This is a supertype of Toleranced so any Toleranced value can be used, but you can also use AtLeast or AtMost to conveniently specify a bound on only one side.

Further remarks about base queries and the generic part category

Both BaseQuery and PartQuery have generic sounding names, but they function differently.

  • A BaseQuery can only have general keys that are accepted by all categories. So they cannot have any specialized keys at all. They do not correspond to a category. There is no “base” version of create-[cat] for example.
  • A PartQuery can have any keys whatsoever. There is also a corresponding category of [cat] functions like create-part and so on. There are two main scenarios where these should be used: when working with parts that don't fit into one of the regular categories, or when writing generic code that needs to support a component whose category is not statically known. Avoid using this category if it isn't necessary because there is less error detection; the API cannot tell if you use an inappropriate key for example.

There is one other special behavior here: a PartQuery can also function as a kind of wildcard BaseQuery in the sense that it can be used an argument to other query constructors. For example, you can write:

val my-wild-query = PartQuery(mounting = "smd", capacitance = 1.e-6)
val my-resistor-query = ResistorQuery(my-base-query, resistance = 1000.)
Any inappropriate keys for the new category, such as capacitance in this example, will be filtered out automatically. This feature is intended to make it easier for programmatically building further automation or interface conveniences on top of the Query API. It is not expected to be used for directly building queries for specific parts in a design.

By the way, the global default keys are another special method of setting search parameters distinct from all of these. They have the same wild quality as a PartQuery in that there are no restrictions on which keys are allowed, and inapplicable ones for a given query function call will be quietly ignored.

Table of Query Keys

This table lists all the standard keys that can be used with the Query API. Meanings of columns:

  • category - indicates which of the categories can use this key. If the entry is italicized such as general or passive then it is not a true category (i.e. there is no [Cat]Query , create-[cat]) but is a meta-category for keys which can be used in several different standard categories, as detailed in the following table.
  • keyword - the name of the Stanza keyword argument used to give a value for this key in any of the query functions which accept it.
  • type - the Stanza type of the value expected for this key. If this entry is blank, it means the current implementation does not type-check this value at the API level, but it will still be checked for validity by dbquery. In most cases these accept string values.
  • dbquery key name - the name of the corresponding dbquery key. If this is not present then the dbquery key is the same as the API key, except that it should be quoted as a string. For example the dbquery key for trust is "trust". Caution: most but not all of the dbquery key names are determined programatically from the API key by replacing underscores with periods.
category keyword type dbquery key name
general trust String
general category String
general mpn String
general mounting String
general manufacturer String
general description String
general case String|Tuple<String>
general min-stock Int
general price Double|Interval
general x Double|Interval
general y Double|Interval
general z Double|Interval
general area Double|Interval
general rated-temperature_min Double|Interval "rated-temperature.min"
general rated-temperature_max Double|Interval "rated-temperature.max"
general operating-temperature Interval
passive type String
passive tolerance Double
passive precision Percentage
passive tolerance_min Double|Interval "tolerance.min"
passive tolerance_max Double|Interval "tolerance.max"
passive component_datasheet "component.datasheet"
passive metadata_image "metadata.image"
passive metadata_digi-key-part-number String "metadata.digi-key-part-number"
passive metadata_description String "metadata.description"
passive metadata_packaging "metadata.packaging"
resistor resistance Double|Interval
resistor rated-power Double|Interval
resistor composition String
resistor tcr_pos Double|Interval "tcr.pos"
resistor tcr_neg Double|Interval "tcr.neg"
resistor metadata_series String "metadata.series"
resistor metadata_features "metadata.features"
resistor metadata_supplier-device-package String "metadata.supplier-device-package"
resistor metadata_number-of-terminations Int "metadata.number-of-terminations"
capacitor capacitance Double|Interval
capacitor anode String
capacitor electrolyte String
capacitor esr Double|Interval
capacitor esr-frequency Double|Interval "esr_frequency"
capacitor rated-voltage Double|Interval
capacitor rated-voltage-ac Double|Interval
capacitor rated-current-pk Double|Interval
capacitor rated-current-rms Double|Interval
capacitor temperature-coefficient_code "temperature-coefficient.code"
capacitor temperature-coefficient_raw-data "temperature-coefficient.raw_data"
capacitor temperature-coefficient_tolerance "temperature-coefficient.tolerance"
capacitor temperature-coefficient_lower-temperature "temperature-coefficient.lower-temperature"
capacitor temperature-coefficient_upper-temperature "temperature-coefficient.upper-temperature"
capacitor temperature-coefficient_change "temperature-coefficient.change"
capacitor metadata_lifetime-temp Double|Interval "metadata.lifetime-temp"
capacitor metadata_applications "metadata.applications"
capacitor metadata_ripple-current-low-frequency Double|Interval "metadata.ripple-current-low-frequency"
capacitor metadata_ripple-current-high-frequency Double|Interval "metadata.ripple-current-high-frequency"
capacitor metadata_lead-spacing Double|Interval "metadata.lead-spacing"
inductor inductance Double|Interval
inductor material-core String
inductor shielding String
inductor current-rating Double|Interval
inductor saturation-current Double|Interval
inductor dc-resistance Double|Interval
inductor quality-factor Double|Interval
inductor quality-factor-frequency Double|Interval
inductor self-resonsant-frequency Double|Interval
mcu core
mcu core-architecture
mcu data-width
mcu flash
mcu frequency Double|Interval
mcu io
mcu line
mcu mfg-package
mcu eeprom
mcu rated-esd
mcu series
mcu supply-voltage_min Double|Interval "supply-voltage.min"
mcu supply-voltage_max Double|Interval "supply-voltage.max"
special stock! Int "_stock"
special sellers! Tuple&lt;String|AuthorizedVendor> "_sellers"
general quantity-needed Int "max-minimum_quantity"
special ignore-stock True|False none
special sort! SortKey|Tuple<SortKey> "_sort"
special exist! ExistKeys "_exist"
special distinct! DistinctKey "_distinct"

The metacategories refer to the following sets of actual categories:

metacategory categories
general usable anywhere including base queries
passive resistor, capacitor, inductor
mcu part
special special rules apply in each case

For the mcu keys, a future version of JITX will likely put these in a separate actual category of their own.

Most of the special keys can go anywhere, including base queries, but have special restrictions on how they're used or other special semantics to be cautious of. See the advanced usage section for details.

Keys with special considerations

  • The category key is normally set automatically by the query-builder constructor for the category. There is one specific scenario where it has to be set by hand:
    • A query function of the part category, such as create-part, is used, and
    • no query object of another category is given (either no query object at all or only a PartQuery), and
    • a key specific to another category is used. In this scenario the category needs to be specified manually. The value is the lower-case category name in quotes, such as "resistor", "capacitor", or "inductor". Otherwise a runtime error will be raised by dbquery.
  • The case key can be used to specify a minimum package size. For example:
    val my-min-pkg = "0402"
    val my-base-query = BaseQuery(case = valid-smd-pkgs(my-min-pkg))
    
  • Other keys with special syntax or semantics are discussed in the advanced usage section.

Relationship to underlying dbquery interface

This article describes the high-level query interface that is recommended as the tool of first resort for managing queries to the parts database. Internally it is currently implemented as a wrapper around the older, lower-level dbquery interface. In some cases dbquery will encounter an error state due to an issue that was not caught by the API, so it is useful to know how they relate.

Most of the keys here correspond to dbquery keys on a one-to-one basis. In simple cases, the keyword name is the same as the underlying dbquery key, but there are several kinds of exceptions:

  • keyword named differently because they don't translate directly, such as min-stock (instead of having to specify stock as an interval), quantity-needed
  • keywords which do something special in the new API and don't translate at all, such as ignore-stock
  • dots are usually replaced by underscores, because dots cannot be used in a keyword. e.g. metadata_packaging.
  • special keys starting with an underscore have keyword names which instead end in a exclamation mark. for example _sellers becomes sellers!.

Advanced usage: special keywords and values

  • stock!, sellers!, and quantity-needed are general keys that can go anywhere except that they will be ignored if the ignore-stock key is also specified.
  • ignore-stock can go anywhere and has no underlying dbquery key, serving only as a convenient way to override these keys. The intended use is to include ignore-stock in the global query defaults when unrelated design work is in progress so as to speed up queries and not introduce unnecessary instability in the parts chosen when recompiling the design.
  • sort! is special in that it refers to other keys and affects the ordering of results but does not filter in any other way. Of course for create-[cat] this can have a big influence on which part is chosen. It can go in any query.
  • exist! is special because it refers to other keys and filters based on their existence in the database (i.e., do they have any value at all), but not the value itself. It can go anywhere.
  • distinct! is special in that it changes the output semantics. It refers to another key and returns distinct possible values for that key instead of parts. It can only go in search-[cat]s.

The last three of these: sort!, exist!, and distinct! have special value types because they need to refer to other keys. The method for specifying these values is a little bit indirect because of a technical limitation. The keywords themselves cannot be used to refer to themselves in Stanza code because they need to refer to the values passed in function call. The implementation does have a standard key object, defined as an enum, corresponding to each keyword, but to avoid confusion these are not exposed publicly. So we use intermediate functions: ExistKeys, SortKey, and DistinctKey, to create the relevant values, and these functions use the familiar keywords.

For example:

val my-query =
  ResistorQuery(exists! = ExistKeys(rated-power = true,
                                    metadata_packaging = true))
shows how to require that the rated-power and metadata.packaging keys exist in the returned part data. (Side note: don't try to do this by specifying an infinite interval, that won't work.)

That is the basic method, but for sort! and distinct! there is yet another shorthand which is to use a special value marker in the same function call instead of calling an intermediate function. The two special values for sort! are FindMinimum and FindMaximum and the sole special value for distinct! is FindDistinct. For example:

val rated-powers = search-resistors(resistance = 1500.,
                                    rated-power = FindDistinct)
shows how to set the underlying _distinct parameter by using the FindDistinct special value marker instead of the distinct! keyword. Note that this does not in any way supply a value for the rated-power key. The method of using special value markers has the limitation that it blocks the regular use of that keyword, which is undesirable in some cases. It also can't be used more than once on the same keyword, which you might want for example with distinct! and sort!.

Here are all the details for each special key:

exists!

exists! has only one method. The value must be a ExistKeys object which can be built by calling ExistKeys with keyword arguments for each of the desired keys. The value assigned to each key is always true. Example:

val my-query =
  ResistorQuery(exists! = ExistKeys(rated-power = true,
                                    metadata_packaging = true))

distinct!

distinct! has two methods. The first is to specify the value as a DistinctKey object created by calling DistinctKey and specifying exactly one keyword argument with a value of true. It is a runtime error to pass any other number of keyword arguments. Example:

val rated-powers =
  search-resistors(resistance = 1500.,
                   distinct! = DistinctKey(rated-power = true))
The second method is to use the FindDistinct special value marker with the keyword corresponding to the desired key. Example:
val rated-powers = search-resistors(resistance = 1500.,
                                    rated-power = FindDistinct)

It is a runtime error to use both methods in the same query function call.

sort!

sort! has two methods. The first is to specify the value as either a SortKey or Tuple<SortKey> object. Each SortKey object is created by calling SortKey and specifying exactly one keyword argument. The value of the keyword argument should be either Increasing or Decreasing (an enum defined just for this purpose). It is a runtime error to specify any other number of keyword arguments. It might be tempting to specify multiple search columns by passing multiple keyword arguments to SortKey but this is an error. The order of keyword arguments is ill-defined in Stanza so this is not a valid method. Make a tuple of SortKeys instead.

Examples:

ResistorQuery(my-base-query, sort! = SortKey(price = Increasing))
ResistorQuery(my-base-query, sort! = [SortKey(price = Increasing)])
ResistorQuery(my-base-query, sort! = [SortKey(price = Increasing),
                                      SortKey(resistance = Decreasing)])
The first two ways of specifying a single sort key are equivalent.

The second method is to use the FindMinimum or FindMaximum special value markers. They are equivalent to specifying a single SortKey with Increasing or Decreasing respectively. The logic behind the naming is that these are intended for use with query functions which create a component based on the top result found so the only significance of the sort order is to optimize that field. This is especially intended to help replace the functionality of OPTIMIZE-FOR from OCDB.

Example:

ResistorQuery(my-base-query, price = FindMinimum)
This is equivalent to the first example above. There is no way to specify multiple sort columns with this method. It is a runtime error to use both methods in the same query function call.

Related packages

This article mainly documents the interface provided by the jitx/parts/query-api package. The jitx/parts package also forwards these other packages, intended for advanced use: