I have a stream - I want to compare number of events in the current window with the previous window.
It can be done by keeping the number of events in the window in globalState and doing something link :
class Foo [I,O] extends ProcessWindowFunction[I,O, String, TimeWindow] {
override def process(key: String, context: Context, elements: Iterable[I], out: Collector[O]): Unit = {
val state = context.globalState.getState(windowStateDescriptor)
if (state.value != null) {
if(state.value > elements.size) {
// do some out.collect
} else {
state.update(elements.size)
}
}
}
}
however I am trying to avoid keeping the persistent state. is there a better more idiomatic way to achieve that ?
Related
I use KeyedCoProcessFunction function to enrich main datastream with data comes from another stream
Code:
class AssetDataEnrichment extends KeyedCoProcessFunction[String, PacketData, AssetCommandState, AssetData] with LazyLogging {
case class AssetStateDoc(assetId: Option[String])
private var associatedDevices: ValueState[AssetStateDoc] = _
override def open(parameters: Configuration): Unit = {
val associatedDevicesDescriptor =
new ValueStateDescriptor[AssetStateDoc]("associatedDevices", classOf[AssetStateDoc])
associatedDevices = getRuntimeContext.getState[AssetStateDoc](associatedDevicesDescriptor)
}
override def processElement1(
packet: PacketData,
ctx: KeyedCoProcessFunction[String, PacketData, AssetCommandState, AssetData]#Context,
out: Collector[AssetData]): Unit = {
val tmpState = associatedDevices.value
val state = if (tmpState == null) AssetStateDoc(None) else tmpState
state.assetId match {
case Some(assetId) =>
logger.debug(s"There are state for ${packet.tag.externalId} = $assetId")
out.collect(AssetData(assetId, packet.tag.externalId.get, packet.toString))
case None => logger.debug(s"No state for a packet ${packet.tag.externalId}")
case _ => logger.debug("Smth went wrong")
}
}
override def processElement2(
value: AssetCommandState,
ctx: KeyedCoProcessFunction[String, PacketData, AssetCommandState, AssetData]#Context,
out: Collector[AssetData]): Unit = {
value.command match {
case CREATE =>
logger.debug(s"Got command to CREATE state for tag: ${value.id} with value: ${value.assetId}")
logger.debug(s"current state is ${associatedDevices.value()}")
associatedDevices.update(AssetStateDoc(Some(value.assetId)))
logger.debug(s"new state is ${associatedDevices.value()}")
case _ =>
logger.error("Got unknown AssetCommandState command")
}
}
}
processElement2() works good, it's accept data and update a state.
but in a processElement1() I am always hitting case None => logger.debug(s"No state for a packet ${packet.tag.externalId}")
although I expect that there will be a value that was set in processElement2 function
as an example I used this guide - https://nightlies.apache.org/flink/flink-docs-master/docs/dev/datastream/fault-tolerance/state/
processElement1 and processElement2 do share state, but keep in mind that this is key-partitioned state. This means that a value set in processElement2 when processing a given value v2 will only be seen in processElement1 when it is called later with a value v1 having the same key as v2.
Also keep in mind that you have no control over the race condition between the two streams coming into processElement1 and processElement2.
The RidesAndFares exercise from the official Apache Flink training is all about learning to work with this part of the API. https://nightlies.apache.org/flink/flink-docs-stable/docs/learn-flink/etl/ is the home for the corresponding tutorial.
I have an object as follows which comes through #Input.
#Input() data;
//**
{
"class_a":["John","Harr y"],
"class_b":["Joseph","Phlip","David"],
"class_c":[]
}
**//
I need to detect the changes if data added or removed in class_a or class_b but im only getting change detection if values of objects are string.
Since the keys in my object are dynamic i couldn't iterate the object and create Iterable differs.
Is there anyway to detect changes of array inside the object.
My Implementation:
constructor(private differs: KeyValueDiffers) {
this.keyValueDiffer = differs.find({}).create();
}
ngDoCheck() {
let changes = this.keyValueDiffer.diff(this.data[this.component.factedBindKey]);
if (changes) {
console.log('Changes detected');
}
}
you can test like this
constructor(private cd: ChangeDetectorRef) {
}
ngOnChanges() {
let actualData =this.data
this.mymethod(actualData);
}
and call this line where you want to access that actual data like this
mymethod(data){
this.cd.detach();
//write main logic
}
I've implemented my own method to handle click event at chart label (React ChartJS 2 library) and it's for filtering data. I have a little problem becouse I'm storing state of filters for specific user so at first, when I'm initializing view with that chart I have to get array with actual filters. The problem is that even when I'm getting this array, I can't use it in my method becouse it's always undefined. What is important - every time I'm adding new filter, i'm also sending array of filters to store. I'm pretty new in React so Your advices will be really welcome.
This is part of my code (mayby componentDidMount is not good choice?):
constructor(props) {
super(props);
this.state = {
itemsArray: []
};
}
componentDidMount(): void {
// Overview doughnut chart filtering
AppStore.instance.select(selectQuickQueryParams).pipe(takeUntil(this.componentDestroyed$)).subscribe((quickQueryParam: QueryParamListState) => {
this.setState({itemsArray: quickQueryParam.entities['Connection:NOT IN:connectionType:QuickFiltersComponent'] ? quickQueryParam.entities['Connection:NOT IN:connectionType:QuickFiltersComponent'].value : []})
});
const originalDoughnutLegendBehavior = Chart.defaults.doughnut.legend.onClick;
Chart.defaults.doughnut.legend.onClick = function (e, legendItem) {
if (!legendItem.hidden) {
if (!this.state.itemsArray.includes(legendItem.text)) {
this.state.itemsArray.push(legendItem.text);
}
const query = new QueryParam(EQueryEntity.Connection, 'connectionType', this.itemsArray, EQueryOperator.NotIn, 'QuickFiltersComponent');
AppStore.instance.dispatch(new AddQuickQueryParamsAction([query]));
AppStore.instance.dispatch(new GetUpdateQuickFilters(true));
} else {
if (this.state.itemsArray.length > 1) {
for (let i = this.state.itemsArray.length; i >= 0; i--) {
if (this.state.itemsArray[i] === legendItem.text) {
this.state.itemsArray.splice(i, 1);
break;
}
}
const query = new QueryParam(EQueryEntity.Connection, 'connectionType', this.itemsArray, EQueryOperator.NotIn, 'QuickFiltersComponent');
AppStore.instance.dispatch(new AddQuickQueryParamsAction([query]));
AppStore.instance.dispatch(new GetUpdateQuickFilters(true));
} else {
AppStore.instance.dispatch(new ClearQuickQueryParamsAction());
}
}
originalDoughnutLegendBehavior.call(this, e, legendItem);
};
}
you can use componentDidMount method only to fetch the list and then in componentDidUpdate method you can update that list with the filter and it will re-render your component with the latest data.
I created a custom blot for links that requires to be able to set rel and target manually. However when loading content that has those attributes, quill strips them. I'm not sure why.
I created a codepen to illustrate the issue.
This is my custom blot:
const Inline = Quill.import('blots/inline')
class CustomLink extends Inline {
static create(options) {
const node = super.create()
node.setAttribute('href', options.url)
if (options.target) { node.setAttribute('target', '_blank') }
if (options.follow === 'nofollow') { node.setAttribute('rel', 'nofollow') }
return node
}
static formats(node) {
return node.getAttribute('href')
}
}
CustomLink.blotName = 'custom_link'
CustomLink.tagName = 'A'
Quill.register({'formats/custom_link': CustomLink})
Do I have to tell Quill to allow certain atttributes?
Upon initialization from existing HTML, Quill will try to construct the data model from it, which is the symmetry between create(), value() for leaf blots, and formats() for inline blots. Given how create() is implemented, you would need formats() to be something like this:
static formats(node) {
let ret = {
url: node.getAttribute('href'),
};
if (node.getAttribute('target') == '_blank') {
ret.target = true;
}
if (node.getAttribute('rel') == 'nofollow') {
ret.follow = 'nofollow';
}
return ret;
}
Working fork with this change: https://codepen.io/quill/pen/xPxGgw
I would recommend overwriting the default link as well though instead of creating another one, unless there's some reason you need both types.
How to use Ext.ComponentQuery.query with nested attributes in Sencha Touch?
e.g
var myHardtoGetObj = topLevelView.down('someview[config.categoryCfg.id=1]')[0];
This gets me "uncaught error"
given :
Ext.define('SomeView', {
xtype : 'someview',
config : {
categoryCfg : {
id : 5,
name : 'someName'
}
}
});
Is this possible?
Thanks.
The canonical way of doing things like that is adding a custom pseudo class matcher:
Ext.ComponentQuery.pseudos.hasCategoryId = function(components, selector) {
var result = [],
c, i, len;
for (i = 0, len = components.length; i < len; i++) {
c = components[i];
if (c.config.categoryCfg && c.config.categoryCfg.id == selector) {
result.push(c);
}
}
return result;
}
Then you can use this pseudo class both globally with Ext.ComponentQuery.query, and locally with methods like query, down, etc.:
var allMatched, someComponent;
allMatched = Ext.ComponentQuery.query(':hasCategoryId(1)');
someComponent = myPanel.down(':hasCategoryId(42)');
See more ways to skin the cat in ComponentQuery doc.
This really is an interesting question. There doesn't seem to be an absolutely straightforward solution, however there is a rather quick workaround. You can modify your view code to:
Ext.define('SomeView', {
xtype : 'someview',
config : {
categoryCfg : {
id : 5,
name : 'someName'
}
},
hasCategoryId: function (id) {
return this.getCategoryCfg().id == id;
}
});
Then you can make a query like this:
Ext.ComponentQuery.query('someview{hasCategoryId(1)}');
or
topLevelView.down('someview{hasCategoryId(1)}');
Note: The syntax of the selector is xtype{memberMethod()} without a space in between. This way both selectors must match (the same way as .class1.class2 in CSS). Also the selectors must be in this order, because the result set is filtered by each selector in order and if some of the components don't have the hasCategoryId method it will break with just '{hasCategoryId(1)}'
Although not exactly answering the question but you can do a little work around to get it to work.
you can add update method to your nestedConfig like so
Ext.define('myCoolClass', {
config : {
nestedConfig : {
nestedId : 5
},
nestedId : null
},
updateNestedConfig: function (nestedConfig) {
if (nestedConfig.nestedId) {
this.setNestedId(nestedConfig.nestedId);
}
}
});
By doing that you now have access to normal component query attribute
Ext.ComponentQuery.query('[nestedId=555]')
As an example. If you take a look at Sencha source code they use this quite a lot like in NavigationView and TabPanels