Skip to content

fix(connections): apply active search filters on incoming messages #1939

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 10, 2025

Conversation

muzaffarmhd
Copy link
Contributor

@muzaffarmhd muzaffarmhd commented Jun 1, 2025

PR Checklist

If you have any questions, you can refer to the Contributing Guide

What is the current behavior?

Incoming messages bypass active search filters unless Enter is pressed again.

Issue Number

Fixes #1900

What is the new behavior?

  • Incoming messages are filtered based on active topic/payload filters
  • batchUpdateMsgs() updated to apply searchMessage() before rendering

Does this PR introduce a breaking change?

  • Yes
  • No

Specific Instructions

  • Test filtering under high message rates
  • Verify async changes for race/UI issues
  • Confirm backward compatibility and message buffering

Other information

  • None

@ysfscream ysfscream requested review from Copilot, ysfscream and Red-Asuka and removed request for ysfscream and Copilot June 3, 2025 16:48
@ysfscream ysfscream requested review from ysfscream and Copilot June 3, 2025 16:49
@ysfscream ysfscream added enhancement New feature or request fix Fix bug or issues desktop MQTTX Desktop labels Jun 3, 2025
@ysfscream ysfscream moved this to TO DO in MQTTX Jun 3, 2025
@ysfscream ysfscream added this to the v1.12.0 milestone Jun 3, 2025
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR ensures that incoming MQTT messages are immediately filtered by active topic/payload search parameters before being rendered.

  • Made batchUpdateMsgs and renderMessage asynchronous to await search filtering
  • Inserted searchMessage checks in message batching based on searchParams
  • Updated RxJS subscriptions to await the new async rendering flow
Comments suppressed due to low confidence (1)

src/views/connections/ConnectionsDetail.vue:1461

  • Add unit tests for batchUpdateMsgs covering scenarios with and without searchParams to ensure filtering behaves as expected under various message batches.
if (this.searchParams.topic !== '' || this.searchParams.payload !== '') {

@muzaffarmhd muzaffarmhd force-pushed the fix/filterincoming branch from a170aad to a5f9538 Compare June 3, 2025 18:04
@muzaffarmhd muzaffarmhd requested a review from Copilot June 3, 2025 18:05
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR ensures incoming messages are immediately filtered by active topic/payload search parameters without needing to re-press Enter.

  • Converted batchUpdateMsgs to async and integrated searchMessage filtering.
  • Updated renderMessage to await batchUpdateMsgs and added error handling.
  • Changed RxJS subscriptions to use async callbacks for rendering and logging failures.
Comments suppressed due to low confidence (3)

src/views/connections/ConnectionsDetail.vue:1455

  • Cloning the entire message list on each update can become a bottleneck under high message rates. Consider using an incremental append/remove approach or a fixed-size dequeue to avoid deep cloning the array.
let _messages = _.cloneDeep(this.recordMsgs.list)

src/views/connections/ConnectionsDetail.vue:1457

  • The new branch that applies searchMessage filtering when searchParams is set isn't covered by existing tests. Add unit tests for both empty and non-empty searchParams to verify correct inclusion/exclusion of messages.
const filteredMsgs = incomingMsgs.filter((msg) => {

src/views/connections/ConnectionsDetail.vue:1588

  • [nitpick] The error log is generic and may lack context. Include identifiers like connection ID or message details to improve troubleshooting.
this.$log.error(`Error rendering message: ${error}`)

Comment on lines 1579 to 1581
messageSubject$.subscribe(async (message) => {
if (!message) return
this.printMessageLog(id, message)
if (isBufferEnabled) {
messageBuffer$.next(message)
} else {
this.renderMessage(id, message)
try {
await this.renderMessage(id, message)
} catch (error) {
this.$log.error(`Error rendering message: ${error}`)
}
}
})
Copy link
Preview

Copilot AI Jun 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using an async callback in subscribe may lead to overlapping render calls if messages arrive faster than processing completes. Consider using RxJS operators like concatMap or exhaustMap to serialize async rendering and maintain order.

Copilot uses AI. Check for mistakes.

@muzaffarmhd muzaffarmhd force-pushed the fix/filterincoming branch from a5f9538 to 90d25b7 Compare June 3, 2025 18:18
@muzaffarmhd muzaffarmhd requested a review from Copilot June 3, 2025 18:19
Copilot

This comment was marked as outdated.

@ysfscream ysfscream moved this from TO DO to In Progress in MQTTX Jun 6, 2025
@Red-Asuka
Copy link
Member

@muzaffarmhd Thank you for your contribution! I noticed an optimization opportunity: since matchMultipleSearch is currently implemented as an async function, it causes async contagion effects that lead to some unnecessary modifications in the related code (such as using operators like concatMap).

After reviewing the code, I found that the matchMultipleSearch function internally performs purely synchronous operations (just simple array filtering and string matching) and can be completely converted to a synchronous function. This would maintain its synchronous nature while reducing code complexity and avoiding unnecessary async handling.

I suggest refactoring this function to a synchronous implementation, which would simplify the entire call chain and improve code readability and maintainability. Here's a reference implementation:

type MatchMultipleSearch = (data: any[], params: SearchParams) => any[] | null

const matchMultipleSearch: MatchMultipleSearch = (data, params) => {
  const paramsKeys = Object.keys(params)
  try {
    const filterData = data.filter(($) => {
      return paramsKeys.every((oneKey) => {
        if ($[oneKey]) {
          const key: string = $[oneKey].toLowerCase().replace(/\s+/g, '')
          const value: string = params[oneKey]
            .toLocaleLowerCase()
            .replace(/\s+/g, '')
            .replace(/[~#^$@%&!+*]/gi, (val) => `\\${val}`)
          return key.match(value)
        } else {
          return null
        }
      })
    })
    return filterData
  } catch (error) {
    throw error
  }
}

@muzaffarmhd
Copy link
Contributor Author

Thanks for your review, well as you mention, it's just filtering and string matching, and the promise overhead isn't needed, for this. I'll make the necessary changes and squash the next commit into this one.

@muzaffarmhd muzaffarmhd force-pushed the fix/filterincoming branch from 90d25b7 to 7f2c1e3 Compare June 6, 2025 10:32
@muzaffarmhd
Copy link
Contributor Author

@Red-Asuka I have made the necessary changes. The matchMultipleSearch function is now synchronous, and the related call chain has been updated accordingly.

@Red-Asuka Red-Asuka requested a review from Copilot June 7, 2025 00:18
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR ensures incoming messages are filtered by active search parameters immediately upon receipt.

  • Converts searchMessage to synchronous for direct filtering.
  • Updates batchUpdateMsgs to apply searchMessage when any search parameters are set.
  • Refactors matchMultipleSearch to remove its Promise wrapper and return results directly.

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src/views/connections/ConnectionsDetail.vue Synchronous searchMessage and updated message buffering logic
src/utils/matchMultipleSearch.ts Removed Promise wrapper from matchMultipleSearch
Comments suppressed due to low confidence (1)

src/views/connections/ConnectionsDetail.vue:1289

  • [nitpick] You can simplify this to return Boolean(res?.length); for clarity and brevity.
return res && res.length ? true : false

private async searchMessage(oneMessage: MessageModel): Promise<boolean> {
const res = await matchMultipleSearch([oneMessage], this.searchParams)
private searchMessage(oneMessage: MessageModel): boolean {
const res = matchMultipleSearch([oneMessage], this.searchParams)
return res && res.length ? true : false
Copy link
Preview

Copilot AI Jun 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since matchMultipleSearch can return null, res.length may throw a runtime error. Consider guarding with res?.length or ensuring the function never returns null.

Suggested change
return res && res.length ? true : false
return res?.length ? true : false

Copilot uses AI. Check for mistakes.

@@ -2,30 +2,29 @@ interface SearchParams {
[propName: string]: string
}

type MatchMultipleSearch = (data: any[], params: SearchParams) => Promise<any[] | null>
type MatchMultipleSearch = (data: any[], params: SearchParams) => any[] | null
Copy link
Preview

Copilot AI Jun 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return type includes null, but the implementation always returns an array or throws. Update the signature to any[] for consistency.

Suggested change
type MatchMultipleSearch = (data: any[], params: SearchParams) => any[] | null
type MatchMultipleSearch = (data: any[], params: SearchParams) => any[]

Copilot uses AI. Check for mistakes.

return null
}
})
const matchMultipleSearch: MatchMultipleSearch = (data, params) => {
Copy link
Preview

Copilot AI Jun 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The try/catch only rethrows the error. Consider removing it or handling errors at the call site to simplify the function.

Copilot uses AI. Check for mistakes.

@ysfscream
Copy link
Member

@muzaffarmhd Thank you for your contribution.

@ysfscream ysfscream merged commit e89a837 into emqx:main Jun 10, 2025
4 checks passed
@github-project-automation github-project-automation bot moved this from In Progress to Done in MQTTX Jun 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
desktop MQTTX Desktop enhancement New feature or request fix Fix bug or issues
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

[Bug] Does not filter when incoming messages
3 participants