I've created an IObservable<> combination using CombineLatest():
IObservable<IList<List<TileItem>>> combination = Observable.CombineLatest(combinations);
I need:
1. to do something `doSomethingOnFirst()` when first element is reached.
2. to do something `doSomethingOnComplete()` when subscription has completed.
Up to now, I've been able to do something when combination is completed. Nevertheless, I don't quite figure out what do I need to do in order to do something when subscription starts...
combination
.Subscribe(
_ => { },
_ => { },
() => this.doSomethingOnCompleted()
);
Any ideas?
It's probably best to use two subscriptions to accomplish this. When subscribing twice to an observable, it's also best to share a subscription to the source, which can be accomplished with .Publish().RefCount().
IEnumerable<IObservable<List<TileItem>>> combinations = null /* using null so I can compile /;
IObservable<IList<List<TileItem>>> combination = Observable.CombineLatest(combinations)
.Publish()
.RefCount();
combination.Take(1)
.Subscribe(_ => doSomethingOnFirst());
combination.Subscribe(_ => { }, _ => {}, () => doSomethingOnCompleted);
You can also accomplish the functionality by filtering the observable by index:
IObservable<IList<List<TileItem>>> combination = Observable.CombineLatest(combinations)
.Where((item, i) => i == 0)
combination.Subscribe(_ => doSomethingOnFirst(), _ => {}, () => doSomethingOnCompleted);
I would avoid .Publish().RefCount() (as this combination can prevent multiple future subscriptions after the initial subscription completes) and just do it this way:
combination
.Select((x, n) => new { x, n })
.Subscribe(xn =>
{
if (xn.n == 0)
{
doSomethingOnFirst();
}
}, () => doSomethingOnCompleted());
Related
I have a separate function where I am returning useQueries. Inside the function, I have a query key and query function for each query. I want to have access to the query key outside of the function. I couldn't find any documentation/resource online regarding this one. Please help!
In the example code provided below, I will need access to the queryKey at the place where I am calling the result.
const result = () => {
return useQueries(
users.map(user => ({
queryKey :[`INeedThisValue`]
queryFn:()=> client.getUserData().toPromise()
}
I'm afraid there is no such thing built-in, but you can implement it by yourself, something like that:
const result = () => {
return useQueries(
users.map((user) => ({
queryKey: [`INeedThisValue`],
queryFn: () =>
client
.getUserData()
.toPromise()
.then((data) => {
return {
data,
queryKey: [`INeedThisValue`]
};
})
}))
);
};
And then get it like data[0].queryKey from your query result.
I've queries like:
useQuery(['myquery',{test:1}], fetchFn)
useQuery(['myquery',{test:2}], fetchFn)
useQuery(['myquery',{test:3}], fetchFn)
I would like to observe the data of all those queries with myquery without knowing the rest of the items of queryKey.
In documentation, as I understood it is possible to observe multiple queries but my matching condition seems not covered.
const observer = new QueriesObserver(queryClient, [
{ queryKey: ['post', 1], queryFn: fetchPost },
{ queryKey: ['post', 2], queryFn: fetchPost },
])
const unsubscribe = observer.subscribe(result => {
console.log(result)
unsubscribe()
})
I could only find similar usage for useIsFetching but it only gives a number of matching queries:
// How many queries matching the posts prefix are fetching?
const isFetchingPosts = useIsFetching(['posts'])
But I want to access the result of the queries, specifically the last updated one.
This is the best thing i can come up with using queryClient :
const Component = () => {
// match all queries with:
const keyPrefix = "courseSection_list";
// since it is loading state, it will trigger twice for each returning result
const matchingQueriesUpdated = useIsFetching([keyPrefix]);
const data = useMemo(() => {
const lastUpdatedMatchingQuery = queryClient.queryCache.queries
.filter((q) => q.queryKey[0] === keyPrefix)
.sort((a, b) => b.state.dataUpdatedAt - a.state.dataUpdatedAt)[0] // sorting puts the last updated one to the 1st index;
return lastUpdatedMatchingQuery.state.data;
}, [matchingQueriesUpdated]);
return <div> bla bla </div>
}
Extra render can be prevented by catching dataUpdatedAt value at 0 for loading state. But i rather keep my code more simple for now.
I have a Cloud Functions transaction that uses FieldValue.increment() to update a nested map, but it isn't running atomically, thus the value updates aren't accurate (running the transaction in quick succession results in an incorrect count).
The function is fired via:
export const updateCategoryAndSendMessage= functions.firestore.document('events/{any}').onUpdate((event, context) => {
which include the following transaction:
db.runTransaction(tx => {
const categoryCounterRef = db.collection("data").doc("categoryCount")
const intToIncrement = event.after.data().published == true ? 1 : -1;
const location = event.after.data().location;
await tx.get(categoryCounterRef).then(doc => {
for (const key in event.after.data().category) {
event.after.data().category[key].forEach(async (subCategory) => {
const map = { [key]: { [subCategory]: FieldValue.increment(intToIncrement) } };
await tx.set(categoryCounterRef, { [location]: map }, { merge: true })
})
}
},
).then(result => {
console.info('Transaction success!')
})
.catch(err => {
console.error('Transaction failure:', err)
})
}).catch((error) => console.log(error));
Example:
Value of field to increment: 0
Tap on button that performs the function multiple times in quick succession (to switch between true and false for "Published")
Expected value: 0 or 1 (depending on whether reference document value is true or false)
Actual value: -3, 5, -2 etc.
As far as I'm aware, transactions should be performed "first come, first served" to avoid inaccurate data. It seems like the function isn't "queuing up" correctly - for lack of a better word.
I'm a bit stumped, would greatly appreciate any guidance with this.
Oh goodness, I was missing return...
return db.runTransaction(tx => {
I'm trying to specify a second output of logstash in order to save certain aggregated data only. No clue how to achieve it at the moment. Documentation doesn't cover such a case.
At the moment I use a single input and a single output.
Input definition (logstash-udp.conf):
input {
udp {
port => 25000
codec => json
buffer_size => 5000
workers => 2
}
}
filter {
grok {
match => [ "message", "API call happened" ]
}
aggregate {
task_id => "%{example_task}"
code => "
map['api_calls'] ||= 0
map['api_calls'] += 1
map['message'] ||= event.get('message')
event.cancel()
"
timeout => 60
push_previous_map_as_event => true
timeout_code => "event.set('aggregated_calls', event.get('api_calls') > 0)"
timeout_tags => ['_aggregation']
}
}
Output definition (logstash-output.conf):
output {
elasticsearch {
hosts => ["localhost"]
manage_template => false
index => "%{[#metadata][udp]}-%{+YYYY.MM.dd}"
document_type => "%{[#metadata][type]}"
}
}
What I want to achieve now? I need to add a second, different aggregation (different data and conditions) which will save all the not aggregated data to Elasticsearch like now however aggregated data for this aggregation would be saved to Postgres. I'm pretty much stuck at the moment and searching the web for some docs/examples doesn't help.
I'd suggest using multiple pipelines: https://www.elastic.co/guide/en/logstash/current/multiple-pipelines.html
This way you can have one pipeline for aggregation and second one for pure data.
I'm in the process of trying to combine some nested calls with reactivemongo in my play2 application.
I get a list of objects returned from createObjects. I then loop over them, check if the object exist in the collection and if not insert them:
def dostuff() = Action {
implicit request =>
form.bindFromRequest.fold(
errors => BadRequest(views.html.invite(errors)),
form => {
val objectsReadyForSave = createObjects(form.companyId, form.companyName, sms_pattern.findAllIn(form.phoneNumbers).toSet)
Async {
for(object <- objectsReadyForSave) {
collection.find(BSONDocument("cId" -> object.get.cId,"userId" ->
object.userId.get)).cursor.headOption.map { maybeFound =>
maybeFound.map { found =>
Logger.info("Found record, do not insert")
} getOrElse {
collection.insert(object)
}
}
}
Future(Ok(views.html.invite(form)))
}
})
}
I feel that this way is not as good as it can be and feels not "play2" and "reactivemongo".
So my question is: How should I structure my nested calls to get the result I want
and get the information of which objects that have been inserted?
I am not an expert in mongoDB neither in ReactiveMongo but it seems that you are trying to use a NoSQL database in the same way as you would use standard SQL databases. Note that mongoDB is asynchronous which means that operations may be executed in some future, this is why insertion/update operations do not return affected documents. Regarding your questions:
1 To insert the objects if they do not exist and get the information of which objects that have been inserted?
You should probably look at the mongoDB db.collection.update() method and call it with the upsert parameter as true. If you can afford it, this will either update documents if they already exist in database or insert them otherwise. Again, this operation does not return affected documents but you can check how many documents have been affected by accessing the last error. See reactivemongo.api.collections.GenericCollection#update which returns a Future[LastError].
2 For all the objects that are inserted, add them to a list and then return it with the Ok() call.
Once again, inserted/updated documents will not be returned. If you really need to return the complete affected document back, you will need to make another query to retrieve matching documents.
I would probably rewrite your code this way (without error/failure handling):
def dostuff() = Action {
implicit request =>
form.bindFromRequest.fold(
errors => BadRequest(views.html.invite(errors)),
form => {
val objectsReadyForSave = createObjects(form.companyId, form.companyName, sms_pattern.findAllIn(form.phoneNumbers).toSet)
Async {
val operations = for {
data <- objectsReadyForSave
} yield collection.update(BSONDocument("cId" -> data.cId.get, "userId" -> data.userId.get), data, upsert = true)
Future.sequence(operations).map {
lastErrors =>
Ok("Documents probably inserted/updated!")
}
}
}
)
}
See also Scala Futures: http://docs.scala-lang.org/overviews/core/futures.html
This is really useful! ;)
Here's how I'd rewrote it.
def dostuff() = Action { implicit request =>
form.bindFromRequest.fold(
errors => BadRequest(views.html.invite(errors)),
form => {
createObjects(form.companyId,
form.companyName,
sms_pattern.findAllIn(form.phoneNumbers).toSet).map(ƒ)
Ok(views.html.invite(form))
}
)
}
// ...
// In the model
// ...
def ƒ(cId: Option[String], userId: Option[String], logger: Logger) = {
// You need to handle the case where obj.cId or obj.userId are None
collection.find(BSONDocument("cId" -> obj.cId.get, "userId" -> obj.userId.get))
.cursor
.headOption
.map { maybeFound =>
maybeFound map { _ =>
logger.info("Record found, do not insert")
} getOrElse {
collection.insert(obj)
}
}
}
There may be some syntax errors, but the idea is there.