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 add and update 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 = add(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 = add(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 = update(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 catch-all category for all other parts
  • wild - a special category which allows overriding the usual category rules

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 new query object first using add or update on the same keys, except that it is less picky: if the query already has the key it will be updated and otherwise it will be added.

create-[cat]

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

create-[cat] takes

  • one optional positional argument qo: [Cat]Query|BaseQuery|WildQuery
  • 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 using whatever combination of add and update is necessary to effect all the given values.

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-wild

insert-[cat] takes

  • mandatory positional argument pin-a: JITXObject
  • mandatory positional argument pin-b: JITXObject
  • optional positional argument qo: [Cat]Query|BaseQuery|WildQuery
  • 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.

search-[cat]s

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

search-[cat]s takes

  • one optional positional argument qo: [Cat]Query|BaseQuery|WildQuery
  • 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 three main ways to modify them:

  • the add function
  • the update function
  • passing the query object to the constructor of another query type
    • this behaves similarly to add 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 add and update functions which are 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 = update(my-resistor-query, resistance = 500.)
val yet-another-query = add(my-modified-query, rated-power = 12.0)
add requires that the key did not already have a value, while update requires that it did; using the wrong one is an error. 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 add .

[Cat]Query

Actual functions defined: ResistorQuery CapacitorQuery InductorQuery PartQuery WildQuery

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

  • one optional positional argument of type [Cat]Query|BaseQuery|WildQuery
  • 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 add (so it is an error if the key already had a value).

add

add 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 the key-value pairs of the original and the key-value pairs specified as keyword arguments additionally, provided that none of those keys had values in the original object. If any of the keys already had values, add throws an error.

update

update 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 the key-value pairs of the original except that each keyword argument updates the corresponding key to a new value. If any of the keys specified as keyword arguments were not already present, then update throws an error. (It is opposite to add in this regard.)

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.)
  • They do not interact with add/update semantics. Any mention of the key in add or update calls will quietly override the global default.

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.

Clarification: difference between base, part, and wild

These are three different kinds of query objects that all have generic sounding names.

  • 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 general keys as well as specialized keys that don't belong to any standard category. For example currently you need to use this kind of query to access the specialized keys for MCUs, though that may be broken out into its own category in the future. This corresponds to the Part category with its own query functions: create-part, insert-part, search-parts. These all have the usual category discipline: you cannot add specialized keys for resistors, for example.
  • A WildQuery can have any keys whatsoever. It should only be used when there is a specific need to override the usual category discipline. When a query function is called using a wild query, the parameters which are inapplicable (e.g. resistance for a capacitor) are quietly filtered out and have no effect. Wild is also a category with its own query functions create-wild, insert-wild, search-wild. The reason to use these would be if you wanted to create parts programmatically when the part category is only known at runtime. Then you may also need to ensure that the category field is appropriately set or the low-level dbquery will complain. (Normally the category field is automatically set by the query function to its own category.)

There are some common ways that both base and wild queries can be used. For example any query function can accept a base or wild query instead of a query of the specific category. In the latter case, inapplicable keys are filtered out as just mentioned.

The global default keys are yet another way of setting parameters distinct from all of these. They behave somewhat similar to a WildQuery 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. You only need to set it by hand if you are a query function of the wild category, such as create-wild, in which case you must specify a category if one is applicable. The value would be the category name as a string, for example "resistor".
  • 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))
    
  • Special keys are discussed in a separate 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 which is documented separately. 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:

The following packages, intended for advanced use, are also forwarded by jitx/parts: