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
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)
pcb-component my-resistor :
...
- 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)
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 componentinsert-[cat]
- convenience wrapper forcreate-[cat]
which also makes an instance and nets it to two given pinssearch-[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. fromsearch-[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
.
insert-[cat]
first creates a component definition as if bycreate-[cat]
, usingqo
, the standard keyword args, andcomp-name
in exactly the same way.insert-[cat]
then instantiates this component, respectinginst-name
if supplied. Note that this name (whether user-specified or not) is not guaranteed to be unique within a pcb-module instance.insert-[cat]
identifies two pins of the instance to use by using theget-element-ports
function:- If pins named
a
andc
are found, they are used in that order. - If pins named
p[1]
andp[2]
are found, they are used in that order. - Otherwise an error is thrown.
- If pins named
insert-[cat]
nets the first pin topin-a
and the second pin topin-b
- 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
- bothShortTraceAnode
- first pin only (regardless of name)ShortTraceCathode
- second pin only (regardless of name)ShortTraceNeither
- noshort-trace
is applied, same as default behavior
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 of1000
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, forresistance
it would beTuple<Double>
representing all the possible resistances in ohms. (Note thatDouble <: 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 asResistor
) usingto-component
, which in turn can be converted to component definitions usingto-jitx
. See documentation for thecomponent-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.)
[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 likecreate-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.)
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 correspondingdbquery
key. If this is not present then thedbquery
key is the same as the API key, except that it should be quoted as a string. For example the dbquery key fortrust
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<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 ascreate-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 bydbquery
.
- A query function of the
- 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 specifystock
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
becomessellers!
.
Advanced usage: special keywords and values¶
stock!
,sellers!
, andquantity-needed
are general keys that can go anywhere except that they will be ignored if theignore-stock
key is also specified.ignore-stock
can go anywhere and has no underlyingdbquery
key, serving only as a convenient way to override these keys. The intended use is to includeignore-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 forcreate-[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 insearch-[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))
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)
_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))
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 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)
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: