- A Shooju View: An Introduction
- Visualizing Series: The Viz Widget
- Viz Settings: Query Tab
- Viz Settings: Points Tab
- Viz Settings: Table Tab
- Viz Settings: Chart Tab
- Filtering for Series: The Filter Widgets
- Query Bar Widget (search)
- Multi-Select Widget (textfacet)
- Dropdown Widget (dropdown_facet)
- Field Date Picker Widget (field-date-picker)
- Custom Widgets: Going Beyond Shooju-Managed Widgets
- Views Reference: A Cookbook
- Using the Highcharts widget: fine-grained visualization beyond the Shooju Viz widget
- Configuring Shooju-managed widgets beyond what's in the View Editor UI
- Writing a custom widget (developers)
- Editing View templates
- Using widget.service: common widget services (developers)
- sjclient
- getGlobalOptions(): any
- getWidgetById(id: any): Widget
- getAllWidgets(): Widget[]
- getGlobalQuery(widgetComponentToIgnore?: WidgetComponent): string
- setState(skipSearch?: boolean): void
- addMessage({ message: string; reload?: true; type?: 'info' | 'warning' | 'error' })
- formatQueryParts(queryParts: string[]): string
- helpers.escapeQueryText(str: string): string
- helpers.formatQueryPart(field: string, val: string): string
- sharedServices.format.moment
- sharedServices.format.date
- sharedServices.format.number
- Making custom calls to the Shooju API
- Saving and editing state in Views
- Using third party plugins
- Making use of URL parameters in a View
- Accessing a widget from a different widget
- Charting an expression that refers to the Global Query
- Custom File Upload
- Upload file to callable
- Use of facets in views
A Shooju View: An Introduction
A View is a self-contained single page in Shooju with a unique URL used to visualize and filter for Series . It can be used within Shooju by yourself and teammates or embedded onto other websites without Shooju branding for use by internal or external parties.
A Shooju View consists of several key parts:
- The View description must be unique across your entire account.
- The Global Query is a Queries that determines which series are accessible to this View. This is the most important decision when starting or editing a View. Note that it can be edited using the Query Builder as shown above or in SjQL:
- The Actions menu at the top controls creation of new Views, Saving, etc.
- Widgets are backbone of displaying and filtering for Series (covered below) and can be created and edited here.
- View Settings have advanced View features and more fine-grained controls.
- The Embed menu shows where this View has been embedded and shows quick links to embed.
- Views are saved into a Category that has the same write permissions as Shooju Series (by default you can save to
users\your.username
orteams\your_team
.
Visualizing Series: The Viz Widget
To visualize Series points or unaggregated fields on a chart or table, use the Viz widget. Add it by chosing it from New widget → Viz:
Adding a widget opens the widget settings on the left side. You can always find these settings by finding the widget you added in the Widgets
menu:
The Viz widget is the most important Shooju widget because it is built specifically around the Shooju Series data structure (fields and points).
The Viz widget settings control the authored (Edit Mode) configuration of the widget. The end-user will not see these settings outside of Edit Mode. To allow the end-user some control and options, the Viz widget has the
menu on the top-right. The contents of this menu can be customized in the widget settings:
Viz Settings: Query Tab
Controls per-series formatting and refinement of the query - useful for when multiple Viz widgets are used in one View or when Series are to be chosen one by one (instead of relying on only the Global Query).
- Viz widgets must be in one of two modes:
- Query many series - useful for displaying multiple series using one query (can be the Global Query, a refined version of it, or completely different)
- Query one by one - useful only when series should be chosen one by one
- Both modes can interact with the Global Query in a few ways:
- Apply global – applies the global query to any overrides (so the two of them work together, as a AND of the two)
- Apply current – applies any filters as well;
current
refers to any interactive filters (see below) - Affect others – affects other widgets on the View if overriden
- Override global sort – overrdies any global sort options
- The Query tab shows the selected series and allows fine-grained control in a Series controller, including charting, label, and other options:
Viz Settings: Points Tab
Contains settings for points, including default date ranges (df/dt), localization options, point date formatting options, number formatting, and other point-related options.
Viz Settings: Table Tab
The Table is one of the two optional ways to display data using the Viz widget. The Table can show both Points and Fields. Hence the first option in the Table widget is whether or not to Show Points:
The rest of the options are used to select the fields to display as Columns in the Table:
- The Table is optional. Use the checkbox to control whether or not it is displayed by default. The end-user may be able to change it if
Show data table
is enabled in the menu options.
- The field selector adds fields to be displayed as columns in the table. This is the only way to add Shooju to display in a Viz table.
- Each field has a column title which can be edited here.
- The non-editable field name.
- Fields can be deleted from the Columns list.
Viz Settings: Chart Tab
The Chart is one of the two optional ways to display data. It generally shows points, but numeric fields are also supported. If unchecked, the table will only display a Table.
The settings in the Chart tab are specific to one of the 7 chart types chosen:
Filtering for Series: The Filter Widgets
Views are authored in the Edit Mode to be used by internal users outside of Edit Mode or by external users as embedded Views. As the author, you may want to give your end-users some ability to filter Series to find Series of interest to the end user. For example, the following Global Query has close to two thousand series:
As the View author, you may want to let your end-users select which of those 1.78k series they want to view in a Chart or in a Table. To do this, Shooju offers a few Widgets for filtering. Add them using the New widget
menu.
Query Bar Widget (search)
The Query Bar is the most versatile widget for general Google-like unstructured queries as well as for building structured queries using the Query Builder. Note a few of the key configuration options below:
- Placeholder – use this to give hints to your users as to what they should do
- Autocomplete Options - autocomplete helps the user by suggesting keywords / fields / values to query for
- Fields & Values - finds strucured matches, such as fields and values (e.g. typing in cou suggests country, and country=Be suggests the three countries starting with Be)
- Text Matches - finds unstructured matches, such as terms (e.g. typing in arg suggests argentina)
- Mode
- Simple - generally used for unstructured queries; simpler UI without the frills of structured query options
- Advanced - meant for more technical users who might be trying to build a structured query
- Show Query Builder - important option in the Advanced mode to allow the more intuitive Query Builder to be used
Multi-Select Widget (textfacet)
Simple to configure and easy for end-users to understand. Allows single or multiple selection of a field. To use it, just chose a field for the end-user to be able to select values from:
Selecting
country
configures the widget to present country options to the end-user:
Note that by default the sorting is descending by count. The widget settings has this and other configuration options.
To pre-select a default, just chose the selection, and watch the orange tip next to the Widgets list indicating that selections have been made:
Next, open the widgets list to confirm the widget(s) that have selections, and confirm that you want to save them to the View:
Dropdown Widget (dropdown_facet)
Similar to the Multi-Select widget, but used when you want the end-user to select only one choice. Useful in combination with default selection saving as described above to force the widget to always have one and only one value selected (for example, when you want a single country to always be selected and then visualize flows into and out of that specific country).
Field Date Picker Widget (field-date-picker)
Useful for date-based field selection. The field must end with _date.
Custom Widgets: Going Beyond Shooju-Managed Widgets
- The five widgets we have covered above are built and managed by the Shooju team
- You will find other widgets when you click Add Widgets that you can experiment with
- Note that each widget has an
Id
and aType
- The Id refers to the identifier within this View
- The Type refers to the identifier across all of Shooju, so when Shooju makes an upgrade to the
textfacet
widget it will affect alltextfacet
widgets across all Shooju Views - You are not limited to the Widgets that Shooju manages;
- You can create Custom Widgets that can be used to display, filter, or do almost anything else that you can think of as long as it fits on a web page
- To get started, chose the Custom widget from the list of Widgets, and refer to the Cookbook below for more information
Views Reference: A Cookbook
Shooju Views are extensible and powerful beyond the basic above. Below is a collection of use-case-specific tips and reference.
Using the Highcharts widget: fine-grained visualization beyond the Shooju Viz widget
The Shooju Viz widget does not support extensible field-based aggregation, nor chart types beyond the few that Viz Chart tab offers. To allow much more extensible charting, Shooju integrates Highcharts. To get started:
- Find the chart type closest to what you want on the Highcharts Demos page
- If you can't find what you want, use Google because highcharts is super popular and there's a high chance that someone has already asked the question
- Once you find the chart you want, open it in jsFiddle from the Highcharts interface
- Experiment with the data options in jsFiddle to get to know how Highcharts expects the data
- Add a Highcharts widget in Shooju
- Map the Shooju response to how Highcharts wants the data. For facets a common reciple is:
myFacets[0].terms.map(function(t){
return {
y:t.count, //maps Shooju facet's count to Highchart's y
name:t.term //maps Shooju facet's term to Highchart's name
};
});
Configuring Shooju-managed widgets beyond what's in the View Editor UI
Shooju widgets are dynamically documented at the following URLs. Please note that some options are documented but not production-ready and may be removed. If you are using options not in the Shooju interface for production use cases, please check in first with Shooju Support.
- https://cdn.shooju.com/sjsearch/docs/stable.html
- https://cdn.shooju.com/sjsearch/docs/beta.html
- https://cdn.shooju.com/sjsearch/docs/alpha.html
Writing a custom widget (developers)
Adding Custom widget adds a simple example widget with placeholders for all methods:
{
"settings": {
"fields": [
"description"
]
},
"definition": {
"defaultState": {
"timestamp": "not set"
},
"init": function (widget) {
// widget created (other widgets may not be created yet)
widget.data = {
widget : widget,
loading : true,
refresh : function () {
this.widget.service.setState(); // make search request
}
};
},
"afterInit": function (widget) {
// template was rendered, all widgets have created and have actual state
},
"getQuery": function (widget) {
// widget query part
return 'set:description';
},
"getFields": function (widget) {
// array of required field names
return widget.settings.fields;
},
"getFacets": function (widget) {
// array of required facets
},
"beforeSearch": function (widget) {
// called before search request
widget.data.loading = true;
},
"onData": function (widget, data) {
// handle search response
widget.data.response = data;
widget.state.timestamp = new Date().getTime();
widget.service.setState(true); // push state, but without search request
widget.data.loading = false;
},
"destroy": function (widget) {
// called before widget destroy
},
"styles": ".sj-custom-desc { color: #4F89FF }",
"template": "<div *ngIf=\"widget.data.loading\">Loading...</div>\n<div *ngIf=\"!widget.data.loading\">\n\tResults: {{ widget.data.response.success ? widget.data.response.total: '-' }}\n\tTimestamp:{{ widget.state.timestamp }}\n\t<div *ngIf=\"widget.data.response.success && widget.data.response.series\">\n\t\t<div *ngFor=\"let series of widget.data.response.series; let i = index\">\n\t\t\t<strong>{{ i+1 }}</strong>.\n\t\t <span class=\"sj-custom-desc\" *ngIf=\"series?.fields?.description\">\n\t\t\t\t{{ series.fields.description }}\n\t\t\t</span>\n\t\t\t<i *ngIf=\"!series?.fields?.description\">\n\t\t\t\tempty\n\t\t\t</i>\n\t\t</div>\n\t</div>\n\t<button type=\"button\" (click)=\"widget.data.refresh()\">Refresh</button>\n</div>"
},
"id": "custom-1",
"type": "custom",
"defaultState": {
"widgetName": "Custom 1"
}
}
Once added, use Script Editor and Template Editor to edit the widget html and css.
Here is the structure of view definition script:
{
// API request GET params
"series_params": {
"max_facet_values": 20, // 'Max Facets Values' from View Settings
"per_page": 40, // 'Per Page' from View Settings
"sort": null // 'Sort' from View Settings
// manually can be added other request params
},
"global": {
"extra_params": null, // raw initial params from url (e.g. for url "/views/zzzz?id=444" it will be "id=444")
"filter_query": null, // Global Query
"grids": { // Grid settings (widgets positions and sizes)
"main": {
"items": {}
}
},
"initial_search": true, // internal flag which is set by view editor (make or not SJSearch request after creating view. e.g. set to "false" when only Chart widget)
"linker": null, // 'Links' from View Settings
"live": null, // 'Refresh' from View Settings
"onInit": function (rootComponent) {
// callback before widgets initialization
},
"onSetState": function (app, state, widgetId, skipSearch) {
// callback after external state set (e.g. from routing)
},
"open_view": function (view_name, state) {
// call to open view
},
"new_view": function (mode, view_name, viewDefinition, state) {
// call to create view
},
"new_view_with_chart": function (mode, view_name, viewDefinition, state) {
// call to create view with chart
}
},
"template": {
// view template. can be overridden for Template view
"layout": "<view-grid></view-grid>"
},
"styles": "", // view CSS
"widgets": [] // widget definitions
}
Every widget is defined as ES5 javascript object with the following structure. Only id and type fields are required.
{
"settings": {}, // make sense only for predefined or customer widgets
"definition": { // definition required only for custom widgets, for other widgets can be used to override existing definition
"defaultState": {}, // initial widget state, should be valid JSON (no method) as it saved to API
"init": function (widget) {
// widget created (other widgets may not be created yet)
widget.data = {
widget : widget,
// methods, properties, ...
};
},
"afterInit": function (widget) {
// template was rendered, all widgets have created and have actual state
},
"getQuery": function (widget) {
// widget query part, added to global query, called every time SJSearch makes a request
// also called when some other widgets want to get current query
},
"getFields": function (widget) {
// array fields which should be requested from api
},
"getFacets": function (widget) {
// array facets which should be requested from api
},
"beforeSearch": function (widget) {
// called before search request
},
"onData": function (widget, data) {
// handle search response
},
"destroy": function (widget) {
// called before widget destroy
// used only to remove some global registrations, detach event, destroy thridparty widgets and etc...
},
"styles": "", // widget CSS
"template": "" // widget template (Angluar 4), see more details in Templates section below
},
"id": "custom-1", // unique id
"type": "custom", // widget type
"defaultState": { // initial widget state, should be valid JSON (no method) as it saved to API. merged with 'definition.defaultState'
"widgetName": "Custom 1" // widget name shown on UI in view editor only
}
}
Editing View templates
Shooju Views use Angular 4, so all templates are Angular 4 templates. See https://angular.io/guide/template-syntax for a reference.
All widget templates has one entry point named widget which is used for all actions, bindings, helpers and etc... widget is WidgetComponent which is defined in SJSearch:
<input [(ngModel)]="widget.state.search" <!-- 'widget.state' is widget state object -->
(ngModelChange)="widget.service.setState()"/> <!-- 'widget.service' means 'WidgetService' from SJSearch -->
<div *ngIf="widget.settings.showTitle"> <!-- 'widget.settings' means 'settings' objects from Widget definiton -->
{{ widget.data.title }} <!-- 'widget.data' means 'data' object from widget definition (usually created in 'init' method) -->
</div>
Internally we also define <html-outlet> Angular component which allows to pass Angular template as string and model. This component is used in many places all over Views and in many widgets. Example:
<html-outlet *ngIf="widget.settings.template" [widgetComponent]="widget" [data]="{series: series, widget: widget}" [html]="widget.settings.template"></html-outlet>
Using widget.service: common widget services (developers)
WidgetService (available using widget.service) is a global built-in service which is used for all Views operations (ajax calls, formatters, events, helpers, access other widgets, etc).
Most important members:
sjclient
Wrapper for Shooju AJAX API requestor - ShoojuClient (https://www.npmjs.com/package/shooju-client)
class RequestOptions {
ignore_errors?: boolean | { [key: string]: true }; // allow to not show builtin warning at the top of page for API errors (for all errors or for specified errors)
}
...
sjclient: {
ws(options): {
subscribe(topic: string, query: string, callback: (data: any) => void): () => void,
close(): void
},
request(url, method, url_args, data, success_callback, error_callback, options, requestOptions: RequestOptions): XMLHttpRequest,
download(url, method, url_args, data, success_callback, error_callback, fileName, requestOptions: RequestOptions): XMLHttpRequest,
raw: {
get(method_url, args, success, error, requestOptions: RequestOptions): XMLHttpRequest,
post(method_url, url_args, post_data, success, error, requestOptions: RequestOptions): XMLHttpRequest
}
}
getGlobalOptions(): any
Returns view definition 'global' object (or empty object) which can be also used to define there some members (fields or methods) which should be shared between few widgets.
getWidgetById(id: any): Widget
Returns widget by id.
getAllWidgets(): Widget[]
Returns all widgets
getGlobalQuery(widgetComponentToIgnore?: WidgetComponent): string
Returns actual query (full, or without widget query part for passed widget).
setState(skipSearch?: boolean): void
Saves current state to state provider (to localstorage or API). Also run search if 'skipSearch' is not 'true'.
addMessage({ message: string; reload?: true; type?: 'info' | 'warning' | 'error' })
Shows message at the top of the page
formatQueryParts(queryParts: string[]): string
Correctly builds query based of query parts (adds brackets only if they are really necessary)
helpers.escapeQueryText(str: string): string
Wraps field name or field value with double quotes if necessary - to use it as part of query
helpers.formatQueryPart(field: string, val: string): string
Returns <field>=<val> query part escaping them using escapeQueryText if necessary.
sharedServices.format.moment
Wrapper for moment js, can be used only when momentjs is additionally loaded on the page (e.g. in SJWeb).
sharedServices.format.date
Wrapper for jquery-dateFormat plugin (https://github.com/phstc/jquery-dateFormat). Built-in in SJSearch.
sharedServices.format.number
Wrapper for jquery-number plugin (https://github.com/customd/jquery-number). Built-in in SJSearch.
Making custom calls to the Shooju API
All calls to Shooju should be made using ShoojuClient which additionally adds necessary authorization headers, uses binary SJTS optimization (can be disabled with 'forceJsonRequest=1' in localstorage), handle errors and etc.
this.widget.service.sjclient.raw.get('/series', {
query: 'set:description',
facets: 'units',
max_facet_values: 1000,
per_page: 30
}, function (data) {
if (data.success) {
// success, handle data
} else {
// some api error (see 'error' and 'description' fields in response)
}
}, function(error) {
// some network error
});
this.widget.service.sjclient.raw.get is just a shortcut for this.widget.service.sjclient.request. Here is exactly the same logic:
this.widget.service.sjclient.request('/series', 'GET', {
query: 'set:description',
facets: 'units',
max_facet_values: 1000,
per_page: 30
}, null, function (data) {
if (data.success) {
// success, handle data
} else {
// some api error (see 'error' and 'description' fields in response)
}
}, function(error) {
// some network error
});
All sjclient members can be found in Service section above.
Saving and editing state in Views
Views have state which is always saved to API or localstorage. Hash of serialized state object is shown at the end of url. This way page reload, back/forward buttons always represent unique state. State hash only shown in non-editor mode.
State object is a valid JSON object (no methods) where keys are widget ids and values are states of widgets.
{
"textfacet-1": {
"selected": {
"40": true
}
},
"search-1": {
"searchString": "set:description"
}
}
Custom widgets require to define and correctly handle state fields to be able restore UI state based on state object.
If some "data" field depends on "state" field widget should have extra code to update it on 'stateSet' event which is triggered when widget state changed.
// for example there is array 'items' in state, but on UI used object map for better performance
// this way 'state.items' is automatically restored on page refresh/back/forward, but 'data.itemsMap' should be refreshed manually
restoreState() {
var itemsMap = {};
this.widget.state.items.forEach(item => {
itemsMap[item] = true;
});
this.widget.data.itemsMap = itemsMap;
});
...
afterInit() {
this.restoreState(); // for page refresh
this.widget.service.on('stateSet', this.restoreState); // for back/forward
}
There are few common rules:
- 'widget.data' - should be used for fields which doesn't affect UI state (of if 'data' field can be restored from 'state' field, but this require extra logic)
- 'widget.state' - should be used for fields which affect UI
- if 'data' field depends on 'state' field widget requires extra 'restoreState logic
- 'widget.service.setState(true/false)' should be called after changing every state property. 'true' should be passed if no need in making new SJSearch request. 'false' is optional, same as don't pass variable.
Using third party plugins
There is [sj-plugin] directive for Angular integration with thirdparty plugins (jquery or plain js). It is pretty simple and just wraps plugin in Angular directive. This way we can be sure that plugin will be created/updated/destroyed when this will be really necessary.
// Directive:
export class PluginDirective implements OnChanges, OnInit, DoCheck, OnDestroy, AfterContentInit, AfterContentChecked, AfterViewInit, AfterViewChecked {
@Input('sj-plugin') settings: PluginDirectiveSettings;
constructor(public $element: ElementRef) {
}
ngAfterContentChecked(): void {
this.settings && this.settings.afterContentChecked && this.settings.afterContentChecked(this);
}
ngAfterViewChecked(): void {
this.settings && this.settings.afterViewChecked && this.settings.afterViewChecked(this);
}
ngAfterViewInit(): void {
this.settings && this.settings.afterViewInit && this.settings.afterViewInit(this);
}
ngAfterContentInit(): void {
this.settings && this.settings.afterContentInit && this.settings.afterContentInit(this);
}
ngOnDestroy(): void {
this.settings && this.settings.onDestroy && this.settings.onDestroy(this);
}
ngDoCheck(): void {
this.settings && this.settings.doCheck && this.settings.doCheck(this);
}
ngOnInit(): void {
this.settings && this.settings.onInit && this.settings.onInit(this);
}
ngOnChanges(changes: SimpleChanges): void {
this.settings && this.settings.onChanges && this.settings.onChanges(this, changes);
}
}
Example of usage:
// Template:
<textarea rows="1" [sj-plugin]="widget.data.textareaPlugin"></textarea>
// JS:
init() {
var self = this;
this.textareaPlugin = {
afterViewInit(plugin) {
self.$textarea = self.widget.$(plugin.$element.nativeElement); // now '$textarea' jquery element available in widget
self.refreshTextareaSize(); // here height is updated
self.initAutocomplete(); // here is shooju autocomplete plugin initialized (jquery plugin)
}
}}
It can be also used as workaround to reference elements from Angular template in js code as we not able to define @ViewChild (it had to be defined in WidgetComponent which is shared for all widgets).
// THIS WILL NOT WORK!!!
// template:
<input #myname>
// JS
@ViewChild('myname') input;
// template:
<input #myname [sj-plugin]="widget.data.initElement(myname)">
// JS
initElement(el) {
this.input = el;
}
Making use of URL parameters in a View
Write the following code in view definition in 'global' object. It will parse raw query parms after view id and save them to 'global.rawParams'. Then you can access these params in widget through 'widget.service.getGlobalOptions().rawParams'.
global: {
"rawParams": {},
"onInit": function (rootComponent) {
if(this.extra_params) {
var rawParams = {};
this.extra_params.split('&').forEach(function(part) {
var arg = part.split('=');
rawParams[decodeURIComponent(arg[0])]=decodeURIComponent(arg[1]);
});
this.rawParams = rawParams;
}
}
}
Accessing a widget from a different widget
Use 'widget.service.getWidgetById(<widget_id>).component'.
var searchWidget = widget.service.getWidgetById('search-1').component;
searchWidget.state.searchString = ''; // create state field for 'search-1' widget
widget.service.setState(); // push state and make new SJSearch request
Charting an expression that refers to the Global Query
Use <CURRENT_QUERY>
in the expression. That will be replaced by the current global query (including additional filters because of multi-select widgets, etc.).
<CURRENT_OPERATORS>
is also available.
Expression example:
={{<CURRENT_QUERY>}}*10<CURRENT_OPERATORS>@A:w
Note that <CURRENT_QUERY> may have quotes in it, so if using quotes around, make sure to use """, like so:
=F.function(r"""<CURRENT_QUERY>""")
Custom File Upload
{
"definition": {
"init": function (widget) {
widget.data = {
widget : widget,
fileUpload: function(e) {
var self = this;
var files = e.target.files;
if (files.length > 0) {
this.uploading = true;
self.widget.s.sjclient.filesUpload(files, function(uploadedFiles) {
if (uploadedFiles) {
// TODO: handle new file
}
e.target.value = null;
self.uploading = false;
}, function() {
self.uploading = false;
}, {
progress: function() {
}
});
}
}
};
},
"styles": "",
"template": "<input type=\"file\" (change)='w.data.fileUpload($event)' />"
},
"id": "custom-1",
"type": "custom",
"defaultState": {
"widgetName": "Custom 1"
}
}
Upload file to callable
var formdata = new FormData();
formdata.append('file', file);
// you can add some other fields there
return self.widget.service.sjclient.request('<URL>', 'POST', {
// query string params (seems not needed in your case)
}, formdata, success, error, {
uploadOnProgress: function() { /* progress callback */ },
serialize: false
});
Use of facets in views
A practical example of applying Facets is in creating graphical views. It offers features beyond the traditional scatter plot, allowing for the inclusion of more information and a more specific structure, according to the needs. Below is an example of Facets and its return structure:
def get_facet(query, facet):
facet_result = sj.raw.get('/series', params={
'facets': facet,
'query':query,
'max_facet_values':10000,
'per_page':0
})['facets'][facet]
return facet_result
The facets below is simple, we just take the amount of energy flow, but you can filter by data, add quantities of a field among other things.
query = r'sid:jodigas\moniquem'
facet = r'source_obj.flow'
get_facet(query, facet)
In the given structure, "count" represents the number of existing terms, "missing" represents the count of terms that were not included (we don't have a value here but when I have a value it will represent the sum of elements). The reason missing could be due to empty values or non-conformance with the numerical format.
{'terms': [{'term': 'IMPLNG', 'count': 182},
{'term': 'EXPLNG', 'count': 176},
{'term': 'INDPROD', 'count': 158},
{'term': 'TOTDEMO', 'count': 152},
{'term': 'MAINTOT', 'count': 146},
{'term': 'TOTIMPSB', 'count': 146},
{'term': 'TOTDEMC', 'count': 145},
{'term': 'TOTEXPSB', 'count': 141},
{'term': 'STOCKCH', 'count': 139},
{'term': 'IMPPIP', 'count': 136},
{'term': 'EXPPIP', 'count': 126},
{'term': 'STATDIFF', 'count': 126},
{'term': 'CLOSTLV', 'count': 114},
{'term': 'CONVER', 'count': 12},
{'term': 'OSOURCES', 'count': 12}],
'other': 0,
'total': 1911,
'missing': 0}
The example below is a view that contains a graph created using Facets:
For more training on facets: Facets