DEV Community

Cover image for Queries for Observables: Crazy & Simple!
Kostia Palchyk
Kostia Palchyk

Posted on

Queries for Observables: Crazy & Simple!

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 (3)

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.