[{"data":1,"prerenderedAt":1729},["ShallowReactive",2],{"docs:/docs/concepts/filters":3},{"id":4,"title":5,"body":6,"description":1722,"extension":1723,"meta":1724,"navigation":109,"path":1725,"seo":1726,"stem":1727,"__hash__":1728},"docs/docs/concepts/filters.md","Topic Filters",{"type":7,"value":8,"toc":1710},"minimark",[9,13,17,22,30,42,46,49,52,62,66,73,561,565,572,831,835,842,1045,1061,1065,1071,1255,1259,1262,1534,1538,1604,1608,1676,1680,1706],[10,11,5],"h1",{"id":12},"topic-filters",[14,15,16],"p",{},"Filters let you narrow message delivery to specific entities within a topic. Instead of receiving all updates, subscribe with filters and only get the messages you care about.",[18,19,21],"h2",{"id":20},"why-filters","Why Filters?",[14,23,24,25,29],{},"Without filters, subscribing to a topic delivers ",[26,27,28],"strong",{},"every"," message published to it. For a dashboard showing 3 bookings out of thousands, this means receiving updates for all bookings and discarding most of them client-side.",[14,31,32,33,37,38,41],{},"Filters solve this at the infrastructure level. Subscribe to ",[34,35,36],"code",{},"bookings"," with filters ",[34,39,40],{},"[\"booking_1\", \"booking_2\"]"," and only updates for those two bookings are delivered. When the user navigates to different bookings, swap filters dynamically, no resubscribe needed.",[18,43,45],{"id":44},"how-it-works","How It Works",[14,47,48],{},"Each filter value becomes a separate routing key at the infrastructure level, so filtering happens before messages reach your client, not by inspecting message payloads. This means zero wasted bandwidth.",[14,50,51],{},"A subscription without filters is a wildcard: it receives every message on the topic, filtered or not. A subscription with filters receives only messages published with one of its filter values. A message published without a filter reaches only the wildcard subscribers.",[53,54,60],"pre",{"className":55,"code":57,"language":58,"meta":59},[56],"language-text","No filters (wildcard, receives everything on the topic, including filtered publishes):\nsubscribe(\"bookings\")\n\nWith filters, receives only publishes carrying one of these values:\nsubscribe(\"bookings\", { filters: [\"booking_1\"] })\n\nPublish with a filter: reaches subscribers filtering on \"booking_1\" AND every wildcard subscriber\npublish(\"bookings\", data, { filter: \"booking_1\" })\n\nPublish without a filter: reaches wildcard subscribers only\npublish(\"bookings\", data)\n","text","",[34,61,57],{"__ignoreMap":59},[18,63,65],{"id":64},"subscribe-with-filters","Subscribe with Filters",[14,67,68,69,72],{},"Pass a ",[34,70,71],{},"filters"," array when subscribing to a topic:",[74,75,76,327,421],"code-tabs",{},[53,77,82],{"className":78,"code":79,"filename":80,"language":81,"meta":59,"style":59},"language-typescript shiki shiki-themes github-light github-dark","import { NoLag } from '@nolag/js-sdk'\n\nconst client = NoLag('your_access_token')\nawait client.connect()\n\nconst room = client.setApp('dashboard').setRoom('ops')\n\n// Subscribe with filters: only receive updates for these bookings\nroom.subscribe('bookings', {\n  filters: ['booking_1', 'booking_2']\n})\n\n// Listen for messages (only matching filters arrive)\nroom.on('bookings', (data, meta) => {\n  console.log('Booking update:', data)\n  console.log('Filter:', meta.filter)  // e.g. 'booking_1'\n})\n","TypeScript","typescript",[34,83,84,104,111,137,152,157,190,195,202,219,237,243,248,254,287,304,322],{"__ignoreMap":59},[85,86,89,93,97,100],"span",{"class":87,"line":88},"line",1,[85,90,92],{"class":91},"szBVR","import",[85,94,96],{"class":95},"sVt8B"," { NoLag } ",[85,98,99],{"class":91},"from",[85,101,103],{"class":102},"sZZnC"," '@nolag/js-sdk'\n",[85,105,107],{"class":87,"line":106},2,[85,108,110],{"emptyLinePlaceholder":109},true,"\n",[85,112,114,117,121,124,128,131,134],{"class":87,"line":113},3,[85,115,116],{"class":91},"const",[85,118,120],{"class":119},"sj4cs"," client",[85,122,123],{"class":91}," =",[85,125,127],{"class":126},"sScJk"," NoLag",[85,129,130],{"class":95},"(",[85,132,133],{"class":102},"'your_access_token'",[85,135,136],{"class":95},")\n",[85,138,140,143,146,149],{"class":87,"line":139},4,[85,141,142],{"class":91},"await",[85,144,145],{"class":95}," client.",[85,147,148],{"class":126},"connect",[85,150,151],{"class":95},"()\n",[85,153,155],{"class":87,"line":154},5,[85,156,110],{"emptyLinePlaceholder":109},[85,158,160,162,165,167,169,172,174,177,180,183,185,188],{"class":87,"line":159},6,[85,161,116],{"class":91},[85,163,164],{"class":119}," room",[85,166,123],{"class":91},[85,168,145],{"class":95},[85,170,171],{"class":126},"setApp",[85,173,130],{"class":95},[85,175,176],{"class":102},"'dashboard'",[85,178,179],{"class":95},").",[85,181,182],{"class":126},"setRoom",[85,184,130],{"class":95},[85,186,187],{"class":102},"'ops'",[85,189,136],{"class":95},[85,191,193],{"class":87,"line":192},7,[85,194,110],{"emptyLinePlaceholder":109},[85,196,198],{"class":87,"line":197},8,[85,199,201],{"class":200},"sJ8bj","// Subscribe with filters: only receive updates for these bookings\n",[85,203,205,208,211,213,216],{"class":87,"line":204},9,[85,206,207],{"class":95},"room.",[85,209,210],{"class":126},"subscribe",[85,212,130],{"class":95},[85,214,215],{"class":102},"'bookings'",[85,217,218],{"class":95},", {\n",[85,220,222,225,228,231,234],{"class":87,"line":221},10,[85,223,224],{"class":95},"  filters: [",[85,226,227],{"class":102},"'booking_1'",[85,229,230],{"class":95},", ",[85,232,233],{"class":102},"'booking_2'",[85,235,236],{"class":95},"]\n",[85,238,240],{"class":87,"line":239},11,[85,241,242],{"class":95},"})\n",[85,244,246],{"class":87,"line":245},12,[85,247,110],{"emptyLinePlaceholder":109},[85,249,251],{"class":87,"line":250},13,[85,252,253],{"class":200},"// Listen for messages (only matching filters arrive)\n",[85,255,257,259,262,264,266,269,273,275,278,281,284],{"class":87,"line":256},14,[85,258,207],{"class":95},[85,260,261],{"class":126},"on",[85,263,130],{"class":95},[85,265,215],{"class":102},[85,267,268],{"class":95},", (",[85,270,272],{"class":271},"s4XuR","data",[85,274,230],{"class":95},[85,276,277],{"class":271},"meta",[85,279,280],{"class":95},") ",[85,282,283],{"class":91},"=>",[85,285,286],{"class":95}," {\n",[85,288,290,293,296,298,301],{"class":87,"line":289},15,[85,291,292],{"class":95},"  console.",[85,294,295],{"class":126},"log",[85,297,130],{"class":95},[85,299,300],{"class":102},"'Booking update:'",[85,302,303],{"class":95},", data)\n",[85,305,307,309,311,313,316,319],{"class":87,"line":306},16,[85,308,292],{"class":95},[85,310,295],{"class":126},[85,312,130],{"class":95},[85,314,315],{"class":102},"'Filter:'",[85,317,318],{"class":95},", meta.filter)  ",[85,320,321],{"class":200},"// e.g. 'booking_1'\n",[85,323,325],{"class":87,"line":324},17,[85,326,242],{"class":95},[53,328,333],{"className":329,"code":330,"filename":331,"language":332,"meta":59,"style":59},"language-python shiki shiki-themes github-light github-dark","from nolag import NoLag, SubscribeOptions\n\nclient = NoLag('your_access_token')\nawait client.connect()\n\nroom = client.set_app('dashboard').set_room('ops')\n\n# Subscribe with filters\nawait room.subscribe('bookings', SubscribeOptions(\n    filters=['booking_1', 'booking_2']\n))\n\n# Listen for messages\ndef handle_booking(data, meta):\n    print('Booking update:', data)\n    print('Filter:', meta.filter)\n\nroom.on('bookings', handle_booking)\n","Python","python",[34,334,335,340,344,349,354,358,363,367,372,377,382,387,391,396,401,406,411,415],{"__ignoreMap":59},[85,336,337],{"class":87,"line":88},[85,338,339],{},"from nolag import NoLag, SubscribeOptions\n",[85,341,342],{"class":87,"line":106},[85,343,110],{"emptyLinePlaceholder":109},[85,345,346],{"class":87,"line":113},[85,347,348],{},"client = NoLag('your_access_token')\n",[85,350,351],{"class":87,"line":139},[85,352,353],{},"await client.connect()\n",[85,355,356],{"class":87,"line":154},[85,357,110],{"emptyLinePlaceholder":109},[85,359,360],{"class":87,"line":159},[85,361,362],{},"room = client.set_app('dashboard').set_room('ops')\n",[85,364,365],{"class":87,"line":192},[85,366,110],{"emptyLinePlaceholder":109},[85,368,369],{"class":87,"line":197},[85,370,371],{},"# Subscribe with filters\n",[85,373,374],{"class":87,"line":204},[85,375,376],{},"await room.subscribe('bookings', SubscribeOptions(\n",[85,378,379],{"class":87,"line":221},[85,380,381],{},"    filters=['booking_1', 'booking_2']\n",[85,383,384],{"class":87,"line":239},[85,385,386],{},"))\n",[85,388,389],{"class":87,"line":245},[85,390,110],{"emptyLinePlaceholder":109},[85,392,393],{"class":87,"line":250},[85,394,395],{},"# Listen for messages\n",[85,397,398],{"class":87,"line":256},[85,399,400],{},"def handle_booking(data, meta):\n",[85,402,403],{"class":87,"line":289},[85,404,405],{},"    print('Booking update:', data)\n",[85,407,408],{"class":87,"line":306},[85,409,410],{},"    print('Filter:', meta.filter)\n",[85,412,413],{"class":87,"line":324},[85,414,110],{"emptyLinePlaceholder":109},[85,416,418],{"class":87,"line":417},18,[85,419,420],{},"room.on('bookings', handle_booking)\n",[53,422,427],{"className":423,"code":424,"filename":425,"language":426,"meta":59,"style":59},"language-go shiki shiki-themes github-light github-dark","package main\n\nimport (\n    \"fmt\"\n    \"log\"\n\n    nolag \"github.com/NoLagApp/go-sdk\"\n)\n\nfunc main() {\n    client := nolag.New(\"your_access_token\")\n    if err := client.Connect(); err != nil {\n        log.Fatal(err)\n    }\n\n    room := client.SetApp(\"dashboard\").SetRoom(\"ops\")\n\n    // Subscribe with filters and handler. Filters is []any because an\n    // element may also be a []string AND group (see below).\n    room.Subscribe(\"bookings\", func(data any, meta nolag.MessageMeta) {\n        fmt.Println(\"Booking update:\", data)\n        fmt.Println(\"Filter:\", meta.Filter)\n    }, nolag.SubscribeOptions{\n        Filters: []any{\"booking_1\", \"booking_2\"},\n    })\n}\n","Go","go",[34,428,429,434,438,443,448,453,457,462,466,470,475,480,485,490,495,499,504,508,513,519,525,531,537,543,549,555],{"__ignoreMap":59},[85,430,431],{"class":87,"line":88},[85,432,433],{},"package main\n",[85,435,436],{"class":87,"line":106},[85,437,110],{"emptyLinePlaceholder":109},[85,439,440],{"class":87,"line":113},[85,441,442],{},"import (\n",[85,444,445],{"class":87,"line":139},[85,446,447],{},"    \"fmt\"\n",[85,449,450],{"class":87,"line":154},[85,451,452],{},"    \"log\"\n",[85,454,455],{"class":87,"line":159},[85,456,110],{"emptyLinePlaceholder":109},[85,458,459],{"class":87,"line":192},[85,460,461],{},"    nolag \"github.com/NoLagApp/go-sdk\"\n",[85,463,464],{"class":87,"line":197},[85,465,136],{},[85,467,468],{"class":87,"line":204},[85,469,110],{"emptyLinePlaceholder":109},[85,471,472],{"class":87,"line":221},[85,473,474],{},"func main() {\n",[85,476,477],{"class":87,"line":239},[85,478,479],{},"    client := nolag.New(\"your_access_token\")\n",[85,481,482],{"class":87,"line":245},[85,483,484],{},"    if err := client.Connect(); err != nil {\n",[85,486,487],{"class":87,"line":250},[85,488,489],{},"        log.Fatal(err)\n",[85,491,492],{"class":87,"line":256},[85,493,494],{},"    }\n",[85,496,497],{"class":87,"line":289},[85,498,110],{"emptyLinePlaceholder":109},[85,500,501],{"class":87,"line":306},[85,502,503],{},"    room := client.SetApp(\"dashboard\").SetRoom(\"ops\")\n",[85,505,506],{"class":87,"line":324},[85,507,110],{"emptyLinePlaceholder":109},[85,509,510],{"class":87,"line":417},[85,511,512],{},"    // Subscribe with filters and handler. Filters is []any because an\n",[85,514,516],{"class":87,"line":515},19,[85,517,518],{},"    // element may also be a []string AND group (see below).\n",[85,520,522],{"class":87,"line":521},20,[85,523,524],{},"    room.Subscribe(\"bookings\", func(data any, meta nolag.MessageMeta) {\n",[85,526,528],{"class":87,"line":527},21,[85,529,530],{},"        fmt.Println(\"Booking update:\", data)\n",[85,532,534],{"class":87,"line":533},22,[85,535,536],{},"        fmt.Println(\"Filter:\", meta.Filter)\n",[85,538,540],{"class":87,"line":539},23,[85,541,542],{},"    }, nolag.SubscribeOptions{\n",[85,544,546],{"class":87,"line":545},24,[85,547,548],{},"        Filters: []any{\"booking_1\", \"booking_2\"},\n",[85,550,552],{"class":87,"line":551},25,[85,553,554],{},"    })\n",[85,556,558],{"class":87,"line":557},26,[85,559,560],{},"}\n",[18,562,564],{"id":563},"dynamic-filter-management","Dynamic Filter Management",[14,566,567,568,571],{},"Change filters at any time without unsubscribing and resubscribing. New filters are subscribed ",[26,569,570],{},"before"," old ones are removed to minimize message gaps.",[74,573,574,694,761],{},[53,575,577],{"className":78,"code":576,"filename":80,"language":81,"meta":59,"style":59},"// Replace all filters (full swap)\nroom.setFilters('bookings', ['booking_3', 'booking_4'])\n\n// Add filters to existing set\nroom.addFilters('bookings', ['booking_5'])\n// Now active: ['booking_3', 'booking_4', 'booking_5']\n\n// Remove specific filters\nroom.removeFilters('bookings', ['booking_3'])\n// Now active: ['booking_4', 'booking_5']\n\n// Remove all filters (switch to wildcard, receives everything)\nroom.setFilters('bookings', [])\n",[34,578,579,584,609,613,618,636,641,645,650,667,672,676,681],{"__ignoreMap":59},[85,580,581],{"class":87,"line":88},[85,582,583],{"class":200},"// Replace all filters (full swap)\n",[85,585,586,588,591,593,595,598,601,603,606],{"class":87,"line":106},[85,587,207],{"class":95},[85,589,590],{"class":126},"setFilters",[85,592,130],{"class":95},[85,594,215],{"class":102},[85,596,597],{"class":95},", [",[85,599,600],{"class":102},"'booking_3'",[85,602,230],{"class":95},[85,604,605],{"class":102},"'booking_4'",[85,607,608],{"class":95},"])\n",[85,610,611],{"class":87,"line":113},[85,612,110],{"emptyLinePlaceholder":109},[85,614,615],{"class":87,"line":139},[85,616,617],{"class":200},"// Add filters to existing set\n",[85,619,620,622,625,627,629,631,634],{"class":87,"line":154},[85,621,207],{"class":95},[85,623,624],{"class":126},"addFilters",[85,626,130],{"class":95},[85,628,215],{"class":102},[85,630,597],{"class":95},[85,632,633],{"class":102},"'booking_5'",[85,635,608],{"class":95},[85,637,638],{"class":87,"line":159},[85,639,640],{"class":200},"// Now active: ['booking_3', 'booking_4', 'booking_5']\n",[85,642,643],{"class":87,"line":192},[85,644,110],{"emptyLinePlaceholder":109},[85,646,647],{"class":87,"line":197},[85,648,649],{"class":200},"// Remove specific filters\n",[85,651,652,654,657,659,661,663,665],{"class":87,"line":204},[85,653,207],{"class":95},[85,655,656],{"class":126},"removeFilters",[85,658,130],{"class":95},[85,660,215],{"class":102},[85,662,597],{"class":95},[85,664,600],{"class":102},[85,666,608],{"class":95},[85,668,669],{"class":87,"line":221},[85,670,671],{"class":200},"// Now active: ['booking_4', 'booking_5']\n",[85,673,674],{"class":87,"line":239},[85,675,110],{"emptyLinePlaceholder":109},[85,677,678],{"class":87,"line":245},[85,679,680],{"class":200},"// Remove all filters (switch to wildcard, receives everything)\n",[85,682,683,685,687,689,691],{"class":87,"line":250},[85,684,207],{"class":95},[85,686,590],{"class":126},[85,688,130],{"class":95},[85,690,215],{"class":102},[85,692,693],{"class":95},", [])\n",[53,695,697],{"className":329,"code":696,"filename":331,"language":332,"meta":59,"style":59},"# Replace all filters (full swap)\nawait room.set_filters('bookings', ['booking_3', 'booking_4'])\n\n# Add filters to existing set\nawait room.add_filters('bookings', ['booking_5'])\n# Now active: ['booking_3', 'booking_4', 'booking_5']\n\n# Remove specific filters\nawait room.remove_filters('bookings', ['booking_3'])\n# Now active: ['booking_4', 'booking_5']\n\n# Remove all filters (switch to wildcard)\nawait room.set_filters('bookings', [])\n",[34,698,699,704,709,713,718,723,728,732,737,742,747,751,756],{"__ignoreMap":59},[85,700,701],{"class":87,"line":88},[85,702,703],{},"# Replace all filters (full swap)\n",[85,705,706],{"class":87,"line":106},[85,707,708],{},"await room.set_filters('bookings', ['booking_3', 'booking_4'])\n",[85,710,711],{"class":87,"line":113},[85,712,110],{"emptyLinePlaceholder":109},[85,714,715],{"class":87,"line":139},[85,716,717],{},"# Add filters to existing set\n",[85,719,720],{"class":87,"line":154},[85,721,722],{},"await room.add_filters('bookings', ['booking_5'])\n",[85,724,725],{"class":87,"line":159},[85,726,727],{},"# Now active: ['booking_3', 'booking_4', 'booking_5']\n",[85,729,730],{"class":87,"line":192},[85,731,110],{"emptyLinePlaceholder":109},[85,733,734],{"class":87,"line":197},[85,735,736],{},"# Remove specific filters\n",[85,738,739],{"class":87,"line":204},[85,740,741],{},"await room.remove_filters('bookings', ['booking_3'])\n",[85,743,744],{"class":87,"line":221},[85,745,746],{},"# Now active: ['booking_4', 'booking_5']\n",[85,748,749],{"class":87,"line":239},[85,750,110],{"emptyLinePlaceholder":109},[85,752,753],{"class":87,"line":245},[85,754,755],{},"# Remove all filters (switch to wildcard)\n",[85,757,758],{"class":87,"line":250},[85,759,760],{},"await room.set_filters('bookings', [])\n",[53,762,764],{"className":423,"code":763,"filename":425,"language":426,"meta":59,"style":59},"// Replace all filters (full swap). SetFilters takes []any, AddFilters\n// and RemoveFilters take []string.\nroom.SetFilters(\"bookings\", []any{\"booking_3\", \"booking_4\"})\n\n// Add filters to existing set\nroom.AddFilters(\"bookings\", []string{\"booking_5\"})\n// Now active: [\"booking_3\", \"booking_4\", \"booking_5\"]\n\n// Remove specific filters\nroom.RemoveFilters(\"bookings\", []string{\"booking_3\"})\n// Now active: [\"booking_4\", \"booking_5\"]\n\n// Remove all filters (switch to wildcard)\nroom.SetFilters(\"bookings\", []any{})\n",[34,765,766,771,776,781,785,789,794,799,803,807,812,817,821,826],{"__ignoreMap":59},[85,767,768],{"class":87,"line":88},[85,769,770],{},"// Replace all filters (full swap). SetFilters takes []any, AddFilters\n",[85,772,773],{"class":87,"line":106},[85,774,775],{},"// and RemoveFilters take []string.\n",[85,777,778],{"class":87,"line":113},[85,779,780],{},"room.SetFilters(\"bookings\", []any{\"booking_3\", \"booking_4\"})\n",[85,782,783],{"class":87,"line":139},[85,784,110],{"emptyLinePlaceholder":109},[85,786,787],{"class":87,"line":154},[85,788,617],{},[85,790,791],{"class":87,"line":159},[85,792,793],{},"room.AddFilters(\"bookings\", []string{\"booking_5\"})\n",[85,795,796],{"class":87,"line":192},[85,797,798],{},"// Now active: [\"booking_3\", \"booking_4\", \"booking_5\"]\n",[85,800,801],{"class":87,"line":197},[85,802,110],{"emptyLinePlaceholder":109},[85,804,805],{"class":87,"line":204},[85,806,649],{},[85,808,809],{"class":87,"line":221},[85,810,811],{},"room.RemoveFilters(\"bookings\", []string{\"booking_3\"})\n",[85,813,814],{"class":87,"line":239},[85,815,816],{},"// Now active: [\"booking_4\", \"booking_5\"]\n",[85,818,819],{"class":87,"line":245},[85,820,110],{"emptyLinePlaceholder":109},[85,822,823],{"class":87,"line":250},[85,824,825],{},"// Remove all filters (switch to wildcard)\n",[85,827,828],{"class":87,"line":256},[85,829,830],{},"room.SetFilters(\"bookings\", []any{})\n",[18,832,834],{"id":833},"publishing-with-filters","Publishing with Filters",[14,836,837,838,841],{},"Specify a ",[34,839,840],{},"filter"," when publishing to target specific subscribers:",[74,843,844,934,996],{},[53,845,847],{"className":78,"code":846,"filename":80,"language":81,"meta":59,"style":59},"// Publish to a specific filter\nroom.emit('bookings', { status: 'confirmed' }, {\n  filter: 'booking_1'\n})\n// Reaches subscribers with 'booking_1' in their filters,\n// plus every wildcard (no-filter) subscriber\n\n// Publish without a filter\nroom.emit('bookings', { type: 'system_update' })\n// Reaches wildcard subscribers only\n// Filtered subscribers do NOT receive filterless publishes\n",[34,848,849,854,874,882,886,891,896,900,905,924,929],{"__ignoreMap":59},[85,850,851],{"class":87,"line":88},[85,852,853],{"class":200},"// Publish to a specific filter\n",[85,855,856,858,861,863,865,868,871],{"class":87,"line":106},[85,857,207],{"class":95},[85,859,860],{"class":126},"emit",[85,862,130],{"class":95},[85,864,215],{"class":102},[85,866,867],{"class":95},", { status: ",[85,869,870],{"class":102},"'confirmed'",[85,872,873],{"class":95}," }, {\n",[85,875,876,879],{"class":87,"line":113},[85,877,878],{"class":95},"  filter: ",[85,880,881],{"class":102},"'booking_1'\n",[85,883,884],{"class":87,"line":139},[85,885,242],{"class":95},[85,887,888],{"class":87,"line":154},[85,889,890],{"class":200},"// Reaches subscribers with 'booking_1' in their filters,\n",[85,892,893],{"class":87,"line":159},[85,894,895],{"class":200},"// plus every wildcard (no-filter) subscriber\n",[85,897,898],{"class":87,"line":192},[85,899,110],{"emptyLinePlaceholder":109},[85,901,902],{"class":87,"line":197},[85,903,904],{"class":200},"// Publish without a filter\n",[85,906,907,909,911,913,915,918,921],{"class":87,"line":204},[85,908,207],{"class":95},[85,910,860],{"class":126},[85,912,130],{"class":95},[85,914,215],{"class":102},[85,916,917],{"class":95},", { type: ",[85,919,920],{"class":102},"'system_update'",[85,922,923],{"class":95}," })\n",[85,925,926],{"class":87,"line":221},[85,927,928],{"class":200},"// Reaches wildcard subscribers only\n",[85,930,931],{"class":87,"line":239},[85,932,933],{"class":200},"// Filtered subscribers do NOT receive filterless publishes\n",[53,935,937],{"className":329,"code":936,"filename":331,"language":332,"meta":59,"style":59},"from nolag import EmitOptions\n\n# Publish to a specific filter\nawait room.emit('bookings', {'status': 'confirmed'}, EmitOptions(\n    filter='booking_1'\n))\n# Reaches subscribers with 'booking_1' in their filters,\n# plus every wildcard (no-filter) subscriber\n\n# Publish without a filter\nawait room.emit('bookings', {'type': 'system_update'})\n# Reaches wildcard subscribers only\n",[34,938,939,944,948,953,958,963,967,972,977,981,986,991],{"__ignoreMap":59},[85,940,941],{"class":87,"line":88},[85,942,943],{},"from nolag import EmitOptions\n",[85,945,946],{"class":87,"line":106},[85,947,110],{"emptyLinePlaceholder":109},[85,949,950],{"class":87,"line":113},[85,951,952],{},"# Publish to a specific filter\n",[85,954,955],{"class":87,"line":139},[85,956,957],{},"await room.emit('bookings', {'status': 'confirmed'}, EmitOptions(\n",[85,959,960],{"class":87,"line":154},[85,961,962],{},"    filter='booking_1'\n",[85,964,965],{"class":87,"line":159},[85,966,386],{},[85,968,969],{"class":87,"line":192},[85,970,971],{},"# Reaches subscribers with 'booking_1' in their filters,\n",[85,973,974],{"class":87,"line":197},[85,975,976],{},"# plus every wildcard (no-filter) subscriber\n",[85,978,979],{"class":87,"line":204},[85,980,110],{"emptyLinePlaceholder":109},[85,982,983],{"class":87,"line":221},[85,984,985],{},"# Publish without a filter\n",[85,987,988],{"class":87,"line":239},[85,989,990],{},"await room.emit('bookings', {'type': 'system_update'})\n",[85,992,993],{"class":87,"line":245},[85,994,995],{},"# Reaches wildcard subscribers only\n",[53,997,999],{"className":423,"code":998,"filename":425,"language":426,"meta":59,"style":59},"// Publish to a specific filter\nroom.Emit(\"bookings\", map[string]string{\"status\": \"confirmed\"}, nolag.EmitOptions{\n    Filter: \"booking_1\",\n})\n// Reaches subscribers with \"booking_1\" in their filters,\n// plus every wildcard (no-filter) subscriber\n\n// Publish without a filter\nroom.Emit(\"bookings\", map[string]string{\"type\": \"system_update\"})\n// Reaches wildcard subscribers only\n",[34,1000,1001,1005,1010,1015,1019,1024,1028,1032,1036,1041],{"__ignoreMap":59},[85,1002,1003],{"class":87,"line":88},[85,1004,853],{},[85,1006,1007],{"class":87,"line":106},[85,1008,1009],{},"room.Emit(\"bookings\", map[string]string{\"status\": \"confirmed\"}, nolag.EmitOptions{\n",[85,1011,1012],{"class":87,"line":113},[85,1013,1014],{},"    Filter: \"booking_1\",\n",[85,1016,1017],{"class":87,"line":139},[85,1018,242],{},[85,1020,1021],{"class":87,"line":154},[85,1022,1023],{},"// Reaches subscribers with \"booking_1\" in their filters,\n",[85,1025,1026],{"class":87,"line":159},[85,1027,895],{},[85,1029,1030],{"class":87,"line":192},[85,1031,110],{"emptyLinePlaceholder":109},[85,1033,1034],{"class":87,"line":197},[85,1035,904],{},[85,1037,1038],{"class":87,"line":204},[85,1039,1040],{},"room.Emit(\"bookings\", map[string]string{\"type\": \"system_update\"})\n",[85,1042,1043],{"class":87,"line":221},[85,1044,928],{},[1046,1047,1049],"callout",{"type":1048},"warning",[14,1050,1051,1054,1055,1060],{},[26,1052,1053],{},"Two things to plan for."," Messages published without a filter are only delivered to wildcard (no-filter) subscribers; filtered subscribers do NOT receive them, so use a separate topic for broadcasts if needed. And a wildcard subscriber receives everything, including every filtered publish: filters narrow what a subscriber gets, they are not a privacy boundary. Use rooms and ",[1056,1057,1059],"a",{"href":1058},"/docs/concepts/acl","access control"," for that.",[18,1062,1064],{"id":1063},"and-groups","AND Groups",[14,1066,1067,1068,1070],{},"A filter entry can itself be a list, which matches only messages tagged with all of its values. Publish to an AND group with ",[34,1069,71],{}," (plural); the broker sorts and lowercases the values and joins them into one routing key.",[74,1072,1073,1155,1201],{},[53,1074,1076],{"className":78,"code":1075,"filename":80,"language":81,"meta":59,"style":59},"// (alice AND admin) OR bob\nroom.subscribe('bookings', {\n  filters: [['alice', 'admin'], 'bob']\n})\n\n// Reaches subscribers whose group is exactly {alice, admin}, plus wildcard subscribers\nroom.emit('bookings', { status: 'confirmed' }, { filters: ['admin', 'alice'] })\n",[34,1077,1078,1083,1095,1116,1120,1124,1129],{"__ignoreMap":59},[85,1079,1080],{"class":87,"line":88},[85,1081,1082],{"class":200},"// (alice AND admin) OR bob\n",[85,1084,1085,1087,1089,1091,1093],{"class":87,"line":106},[85,1086,207],{"class":95},[85,1088,210],{"class":126},[85,1090,130],{"class":95},[85,1092,215],{"class":102},[85,1094,218],{"class":95},[85,1096,1097,1100,1103,1105,1108,1111,1114],{"class":87,"line":113},[85,1098,1099],{"class":95},"  filters: [[",[85,1101,1102],{"class":102},"'alice'",[85,1104,230],{"class":95},[85,1106,1107],{"class":102},"'admin'",[85,1109,1110],{"class":95},"], ",[85,1112,1113],{"class":102},"'bob'",[85,1115,236],{"class":95},[85,1117,1118],{"class":87,"line":139},[85,1119,242],{"class":95},[85,1121,1122],{"class":87,"line":154},[85,1123,110],{"emptyLinePlaceholder":109},[85,1125,1126],{"class":87,"line":159},[85,1127,1128],{"class":200},"// Reaches subscribers whose group is exactly {alice, admin}, plus wildcard subscribers\n",[85,1130,1131,1133,1135,1137,1139,1141,1143,1146,1148,1150,1152],{"class":87,"line":192},[85,1132,207],{"class":95},[85,1134,860],{"class":126},[85,1136,130],{"class":95},[85,1138,215],{"class":102},[85,1140,867],{"class":95},[85,1142,870],{"class":102},[85,1144,1145],{"class":95}," }, { filters: [",[85,1147,1107],{"class":102},[85,1149,230],{"class":95},[85,1151,1102],{"class":102},[85,1153,1154],{"class":95},"] })\n",[53,1156,1158],{"className":329,"code":1157,"filename":331,"language":332,"meta":59,"style":59},"from nolag import SubscribeOptions, EmitOptions\n\n# (alice AND admin) OR bob\nawait room.subscribe('bookings', SubscribeOptions(\n    filters=[['alice', 'admin'], 'bob']\n))\n\n# Reaches subscribers whose group is exactly {alice, admin}, plus wildcard subscribers\nawait room.emit('bookings', {'status': 'confirmed'}, EmitOptions(filters=['admin', 'alice']))\n",[34,1159,1160,1165,1169,1174,1178,1183,1187,1191,1196],{"__ignoreMap":59},[85,1161,1162],{"class":87,"line":88},[85,1163,1164],{},"from nolag import SubscribeOptions, EmitOptions\n",[85,1166,1167],{"class":87,"line":106},[85,1168,110],{"emptyLinePlaceholder":109},[85,1170,1171],{"class":87,"line":113},[85,1172,1173],{},"# (alice AND admin) OR bob\n",[85,1175,1176],{"class":87,"line":139},[85,1177,376],{},[85,1179,1180],{"class":87,"line":154},[85,1181,1182],{},"    filters=[['alice', 'admin'], 'bob']\n",[85,1184,1185],{"class":87,"line":159},[85,1186,386],{},[85,1188,1189],{"class":87,"line":192},[85,1190,110],{"emptyLinePlaceholder":109},[85,1192,1193],{"class":87,"line":197},[85,1194,1195],{},"# Reaches subscribers whose group is exactly {alice, admin}, plus wildcard subscribers\n",[85,1197,1198],{"class":87,"line":204},[85,1199,1200],{},"await room.emit('bookings', {'status': 'confirmed'}, EmitOptions(filters=['admin', 'alice']))\n",[53,1202,1204],{"className":423,"code":1203,"filename":425,"language":426,"meta":59,"style":59},"// (alice AND admin) OR bob\nroom.Subscribe(\"bookings\", func(data any, meta nolag.MessageMeta) {\n    fmt.Println(\"Booking update:\", data)\n}, nolag.SubscribeOptions{\n    Filters: []any{[]string{\"alice\", \"admin\"}, \"bob\"},\n})\n\n// Reaches subscribers whose group is exactly {alice, admin}, plus wildcard subscribers\nroom.Emit(\"bookings\", map[string]string{\"status\": \"confirmed\"}, nolag.EmitOptions{\n    Filters: []string{\"admin\", \"alice\"},\n})\n",[34,1205,1206,1210,1215,1220,1225,1230,1234,1238,1242,1246,1251],{"__ignoreMap":59},[85,1207,1208],{"class":87,"line":88},[85,1209,1082],{},[85,1211,1212],{"class":87,"line":106},[85,1213,1214],{},"room.Subscribe(\"bookings\", func(data any, meta nolag.MessageMeta) {\n",[85,1216,1217],{"class":87,"line":113},[85,1218,1219],{},"    fmt.Println(\"Booking update:\", data)\n",[85,1221,1222],{"class":87,"line":139},[85,1223,1224],{},"}, nolag.SubscribeOptions{\n",[85,1226,1227],{"class":87,"line":154},[85,1228,1229],{},"    Filters: []any{[]string{\"alice\", \"admin\"}, \"bob\"},\n",[85,1231,1232],{"class":87,"line":159},[85,1233,242],{},[85,1235,1236],{"class":87,"line":192},[85,1237,110],{"emptyLinePlaceholder":109},[85,1239,1240],{"class":87,"line":197},[85,1241,1128],{},[85,1243,1244],{"class":87,"line":204},[85,1245,1009],{},[85,1247,1248],{"class":87,"line":221},[85,1249,1250],{},"    Filters: []string{\"admin\", \"alice\"},\n",[85,1252,1253],{"class":87,"line":239},[85,1254,242],{},[18,1256,1258],{"id":1257},"example-real-time-dashboard","Example: Real-time Dashboard",[14,1260,1261],{},"The most common use case for filters is dashboards where users view a subset of entities that update in real-time:",[74,1263,1264],{},[53,1265,1267],{"className":78,"code":1266,"filename":80,"language":81,"meta":59,"style":59},"// Real-time dashboard: user views 3 bookings out of thousands\nconst room = client.setApp('dashboard').setRoom('ops')\n\n// Subscribe only to the bookings currently visible\nconst visibleBookingIds = ['booking_42', 'booking_87', 'booking_153']\nroom.subscribe('bookings', {\n  filters: visibleBookingIds\n})\n\nroom.on('bookings', (data, meta) => {\n  updateBookingCard(meta.filter, data)\n})\n\n// User navigates to a different page of bookings\nfunction onPageChange(newBookingIds: string[]) {\n  room.setFilters('bookings', newBookingIds)\n}\n\n// User opens a booking detail view, add it to filters\nfunction onBookingOpen(bookingId: string) {\n  room.addFilters('bookings', [bookingId])\n}\n\n// User closes a booking detail view, remove it\nfunction onBookingClose(bookingId: string) {\n  room.removeFilters('bookings', [bookingId])\n}\n",[34,1268,1269,1274,1300,1304,1309,1336,1348,1353,1357,1361,1385,1393,1397,1401,1406,1428,1442,1446,1450,1455,1474,1487,1491,1495,1500,1517,1529],{"__ignoreMap":59},[85,1270,1271],{"class":87,"line":88},[85,1272,1273],{"class":200},"// Real-time dashboard: user views 3 bookings out of thousands\n",[85,1275,1276,1278,1280,1282,1284,1286,1288,1290,1292,1294,1296,1298],{"class":87,"line":106},[85,1277,116],{"class":91},[85,1279,164],{"class":119},[85,1281,123],{"class":91},[85,1283,145],{"class":95},[85,1285,171],{"class":126},[85,1287,130],{"class":95},[85,1289,176],{"class":102},[85,1291,179],{"class":95},[85,1293,182],{"class":126},[85,1295,130],{"class":95},[85,1297,187],{"class":102},[85,1299,136],{"class":95},[85,1301,1302],{"class":87,"line":113},[85,1303,110],{"emptyLinePlaceholder":109},[85,1305,1306],{"class":87,"line":139},[85,1307,1308],{"class":200},"// Subscribe only to the bookings currently visible\n",[85,1310,1311,1313,1316,1318,1321,1324,1326,1329,1331,1334],{"class":87,"line":154},[85,1312,116],{"class":91},[85,1314,1315],{"class":119}," visibleBookingIds",[85,1317,123],{"class":91},[85,1319,1320],{"class":95}," [",[85,1322,1323],{"class":102},"'booking_42'",[85,1325,230],{"class":95},[85,1327,1328],{"class":102},"'booking_87'",[85,1330,230],{"class":95},[85,1332,1333],{"class":102},"'booking_153'",[85,1335,236],{"class":95},[85,1337,1338,1340,1342,1344,1346],{"class":87,"line":159},[85,1339,207],{"class":95},[85,1341,210],{"class":126},[85,1343,130],{"class":95},[85,1345,215],{"class":102},[85,1347,218],{"class":95},[85,1349,1350],{"class":87,"line":192},[85,1351,1352],{"class":95},"  filters: visibleBookingIds\n",[85,1354,1355],{"class":87,"line":197},[85,1356,242],{"class":95},[85,1358,1359],{"class":87,"line":204},[85,1360,110],{"emptyLinePlaceholder":109},[85,1362,1363,1365,1367,1369,1371,1373,1375,1377,1379,1381,1383],{"class":87,"line":221},[85,1364,207],{"class":95},[85,1366,261],{"class":126},[85,1368,130],{"class":95},[85,1370,215],{"class":102},[85,1372,268],{"class":95},[85,1374,272],{"class":271},[85,1376,230],{"class":95},[85,1378,277],{"class":271},[85,1380,280],{"class":95},[85,1382,283],{"class":91},[85,1384,286],{"class":95},[85,1386,1387,1390],{"class":87,"line":239},[85,1388,1389],{"class":126},"  updateBookingCard",[85,1391,1392],{"class":95},"(meta.filter, data)\n",[85,1394,1395],{"class":87,"line":245},[85,1396,242],{"class":95},[85,1398,1399],{"class":87,"line":250},[85,1400,110],{"emptyLinePlaceholder":109},[85,1402,1403],{"class":87,"line":256},[85,1404,1405],{"class":200},"// User navigates to a different page of bookings\n",[85,1407,1408,1411,1414,1416,1419,1422,1425],{"class":87,"line":289},[85,1409,1410],{"class":91},"function",[85,1412,1413],{"class":126}," onPageChange",[85,1415,130],{"class":95},[85,1417,1418],{"class":271},"newBookingIds",[85,1420,1421],{"class":91},":",[85,1423,1424],{"class":119}," string",[85,1426,1427],{"class":95},"[]) {\n",[85,1429,1430,1433,1435,1437,1439],{"class":87,"line":306},[85,1431,1432],{"class":95},"  room.",[85,1434,590],{"class":126},[85,1436,130],{"class":95},[85,1438,215],{"class":102},[85,1440,1441],{"class":95},", newBookingIds)\n",[85,1443,1444],{"class":87,"line":324},[85,1445,560],{"class":95},[85,1447,1448],{"class":87,"line":417},[85,1449,110],{"emptyLinePlaceholder":109},[85,1451,1452],{"class":87,"line":515},[85,1453,1454],{"class":200},"// User opens a booking detail view, add it to filters\n",[85,1456,1457,1459,1462,1464,1467,1469,1471],{"class":87,"line":521},[85,1458,1410],{"class":91},[85,1460,1461],{"class":126}," onBookingOpen",[85,1463,130],{"class":95},[85,1465,1466],{"class":271},"bookingId",[85,1468,1421],{"class":91},[85,1470,1424],{"class":119},[85,1472,1473],{"class":95},") {\n",[85,1475,1476,1478,1480,1482,1484],{"class":87,"line":527},[85,1477,1432],{"class":95},[85,1479,624],{"class":126},[85,1481,130],{"class":95},[85,1483,215],{"class":102},[85,1485,1486],{"class":95},", [bookingId])\n",[85,1488,1489],{"class":87,"line":533},[85,1490,560],{"class":95},[85,1492,1493],{"class":87,"line":539},[85,1494,110],{"emptyLinePlaceholder":109},[85,1496,1497],{"class":87,"line":545},[85,1498,1499],{"class":200},"// User closes a booking detail view, remove it\n",[85,1501,1502,1504,1507,1509,1511,1513,1515],{"class":87,"line":551},[85,1503,1410],{"class":91},[85,1505,1506],{"class":126}," onBookingClose",[85,1508,130],{"class":95},[85,1510,1466],{"class":271},[85,1512,1421],{"class":91},[85,1514,1424],{"class":119},[85,1516,1473],{"class":95},[85,1518,1519,1521,1523,1525,1527],{"class":87,"line":557},[85,1520,1432],{"class":95},[85,1522,656],{"class":126},[85,1524,130],{"class":95},[85,1526,215],{"class":102},[85,1528,1486],{"class":95},[85,1530,1532],{"class":87,"line":1531},27,[85,1533,560],{"class":95},[18,1535,1537],{"id":1536},"filter-rules","Filter Rules",[1539,1540,1541,1551,1580,1583,1590,1597],"ul",{},[1542,1543,1544,1547,1548],"li",{},[26,1545,1546],{},"Max 100 filters"," per topic per connection; more is refused with ",[34,1549,1550],{},"too_many_filters (max 100)",[1542,1552,1553,1554,1557,1558,230,1561,230,1564,1567,1568,1571,1572,1575,1576,1579],{},"Filter values must ",[26,1555,1556],{},"not"," be empty or contain ",[34,1559,1560],{},"/",[34,1562,1563],{},"#",[34,1565,1566],{},"+"," or ",[34,1569,1570],{},"|","; the broker refuses the subscribe with ",[34,1573,1574],{},"invalid_filter_chars (/, #, +, | not allowed)"," on the ",[34,1577,1578],{},"error"," event",[1542,1581,1582],{},"An AND group needs at least two values",[1542,1584,1585,1586,1589],{},"Filters are ",[26,1587,1588],{},"persisted",", on reconnect, your filters are automatically restored",[1542,1591,1592,1593,1596],{},"Filters work with ",[26,1594,1595],{},"load balancing",", each filter topic uses the shared subscription group",[1542,1598,1599,1600,1603],{},"Setting filters to an empty array switches back to ",[26,1601,1602],{},"wildcard mode"," (receives everything)",[18,1605,1607],{"id":1606},"filters-vs-wildcards","Filters vs Wildcards",[1609,1610,1611,1624],"table",{},[1612,1613,1614],"thead",{},[1615,1616,1617,1621],"tr",{},[1618,1619,1620],"th",{},"Scenario",[1618,1622,1623],{},"Approach",[1625,1626,1627,1636,1644,1658,1666],"tbody",{},[1615,1628,1629,1633],{},[1630,1631,1632],"td",{},"Receive all messages on a topic, filtered or not",[1630,1634,1635],{},"Subscribe without filters (wildcard)",[1615,1637,1638,1641],{},[1630,1639,1640],{},"Receive updates for specific entities",[1630,1642,1643],{},"Subscribe with filters",[1615,1645,1646,1649],{},[1630,1647,1648],{},"Change which entities to track",[1630,1650,1651,1653,1654,1653,1656],{},[34,1652,590],{}," / ",[34,1655,624],{},[34,1657,656],{},[1615,1659,1660,1663],{},[1630,1661,1662],{},"Broadcast to every subscriber including filtered ones",[1630,1664,1665],{},"Use a separate topic (filtered subscribers won't receive filterless publishes)",[1615,1667,1668,1671],{},[1630,1669,1670],{},"Hide one entity's messages from other subscribers",[1630,1672,1673,1674],{},"Filters cannot do this; use a room per entity and ",[1056,1675,1059],{"href":1058},[18,1677,1679],{"id":1678},"next-steps","Next Steps",[1539,1681,1682,1688,1694,1700],{},[1542,1683,1684],{},[1056,1685,1687],{"href":1686},"/docs/concepts/topics","Topics & Pub/Sub",[1542,1689,1690],{},[1056,1691,1693],{"href":1692},"/docs/concepts/rooms","Rooms",[1542,1695,1696],{},[1056,1697,1699],{"href":1698},"/docs/sdks/javascript","JavaScript SDK Reference",[1542,1701,1702],{},[1056,1703,1705],{"href":1704},"/docs/guides/dashboards","Live Dashboards Guide",[1707,1708,1709],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":59,"searchDepth":106,"depth":106,"links":1711},[1712,1713,1714,1715,1716,1717,1718,1719,1720,1721],{"id":20,"depth":106,"text":21},{"id":44,"depth":106,"text":45},{"id":64,"depth":106,"text":65},{"id":563,"depth":106,"text":564},{"id":833,"depth":106,"text":834},{"id":1063,"depth":106,"text":1064},{"id":1257,"depth":106,"text":1258},{"id":1536,"depth":106,"text":1537},{"id":1606,"depth":106,"text":1607},{"id":1678,"depth":106,"text":1679},"Learn how to use topic filters to narrow message delivery to specific entities. Subscribe with filters, manage them dynamically, and publish to filtered subscribers.","md",{},"/docs/concepts/filters",{"title":5,"description":1722},"docs/concepts/filters","9vlR4_EipQ3D1G-X9elY6OS6bzzVEONuoZZxl1Ws1xc",1789869419136]