DEV Community

loading...
Cover image for Queries for Observables: Crazy & Simple!

Queries for Observables: Crazy & Simple!

kosich profile image Kostia Palchyk 惻3 min read

In the previous post we explored API to select events from streams using RegExp-like syntax, e.g.:

const D = fromEvent(element, 'mousedown');
const M = fromEvent(document, 'mousemove');
const U = fromEvent(document, 'mouseup');

exec(
  'DM*U'         // <- regular expression
  , { D, M, U }  // <- streams that will be used
)
  .pipe(ā€¦)
  .subscribe(console.log);

In this post we'll review it's functional API counterpart:

query(D, some(M), U) // select some Ms between Ds and Us
  .pipe(ā€¦)
  .subscribe(console.log)

But first, we'll recap the thought process. If you're already familiar with the idea ā€” hop here

tl;dr: package @ https://github.com/erql/rx-rql šŸ“¦

šŸ’” Recap the idea

Imagine we need to implement a drag-n-drop behavior.

For that, we have three event streams: mousemove$, mousedown$, mouseup$. So we want to capture mousemove$ events after mousedown$ emitted and before mouseup$.

Let's draw a marble diagram of the event streams we have:

mousedown$  --o------------
mousemove$  -o-o-o-o-o-o-o-
mouseup$    ------------o--
Every event stream is represented with separate line.
o stands for event on the stream. - is a separator to represent passing time

For better readability let's substitute all the os to respective letters of the streams:

mousedown$  --d------------
mousemove$  -m-m-m-m-m-m-m-
mouseup$    ------------u--
'd' will now stand for mouse-down event, 'm' for mouse-move, 'u' for mouse-up

Now that we have distinct event names, we can simplify our diagram to a single line of events:

events$     -mdm-m-m-m-mum-

Let's remove the time - signs as well, we don't them:

events$      mdmmmmmum

Okay, to rephrase our task in terms of the new diagram: we need to capture the m events between d and u emissions.

šŸ¤”

Hmm...

"we need ms between d and u"...

Sounds familiar...

Ah! If that was a string, we could easily do it with a regular expression:

/dm*u/.exec('mdmmmum')

Would give us the needed dmmmu without trailing mouse-move m events...

Right?

If only we had a library to select events from streams with regexes...

šŸš€ Solution

query(D, some(M), U)
  .pipe(ā€¦)
  .subscribe(console.log)

Rx-RQL šŸ“¦ package provides following API to make such selections:

  • query(ā€¦) ā€” root of your selection
  • A ā€” select 1 emission from the stream
  • some(A) ā€” select 0 to āˆž emissions from A
  • maybe(A) ā€” select 0 or 1 emission from A
  • many(n,m)(A) ā€” select from n to m emissions from A
  • mute(A) ā€” select emission from A & mute it

And you can group them as you like:

  • some(A, some(B), mute(C)) ā€” select as many emissions from: select as many Bs as possible between emissions from A and muted C

Here's how to create a simple drag-n-drop behavior using this package:

And here's a Mr. Potato-Head DnD šŸ„” ā€” a more sophisticated example based on this amazing article by @dailydevtips1 ! Thx, Chris šŸ™

šŸ‘‹ Outro

Thank you for reading this article! Stay reactive and have a nice day šŸ™‚

If you enjoyed reading ā€” please, indicate that with ā¤ļø šŸ¦„ šŸ“˜ buttons

And in case you're not yet following me here and on twitter ā€” then you've probably missed my recent experiments: Rx + Proxy, Rx Autorun, React + Rx

Now I'd love to hear your thoughts! šŸ‘‚

Discussion

pic
Editor guide
Collapse
yellow1912 profile image
yellow1912

Look very cool. I will try it out.

Collapse
kosich profile image
Kostia Palchyk Author

Thanks! šŸ˜Š

Be cautious: although it's well covered with tests, it's still very experimental!

If community would find this library useful, I'm thinking it could be extended with:

  • time-based queries, like "listen to A for 5 sec"
  • pipe-able queries, like query(A, many(B).pipe(ā€¦), C) that would be applied to particular group output

Anyhow, please ping me back here or on github if you have additional thoughts or ideas. Thx!

Collapse
yellow1912 profile image
yellow1912

I think it would be interesting to create predefined example to solve some common scenarios. Things like Drag and Drop, Hotkeys etc. I can see many possibilities here.