فهرست منبع

continue to improve view performance

Tienson Qin 22 ساعت پیش
والد
کامیت
ff56c143ab

+ 41 - 15
deps/db/src/logseq/db/common/initial_data.cljs

@@ -160,35 +160,61 @@
         (with-raw-title entity)
         (assoc :db/id (:db/id entity)))))
 
+(defn hidden-ref-pred
+  "Build a predicate that determines if a ref block should be hidden for `id`.
+   Reuses cached class/tag context so callers can check many refs efficiently."
+  [db id]
+  (let [entity (d/entity db id)
+        entity-ident (:db/ident entity)
+        class-ids (when (entity-util/class? entity)
+                    (let [children (db-class/get-structured-children db id)]
+                      (set (conj children id))))]
+    (fn [ref-block]
+      (or
+       (= (:db/id ref-block) id)
+       (= id (:db/id (:block/page ref-block)))
+       (= id (:db/id (:logseq.property/view-for ref-block)))
+       (entity-util/hidden? (:block/page ref-block))
+       (entity-util/hidden? ref-block)
+       (and class-ids
+            (some class-ids (map :db/id (:block/tags ref-block))))
+       (and entity-ident
+            (some? (get ref-block entity-ident)))))))
+
 (defn hidden-ref?
   "Whether ref-block (for block with the `id`) should be hidden."
   [db ref-block id]
-  (let [entity (d/entity db id)]
-    (or
-     (= (:db/id ref-block) id)
-     (= id (:db/id (:block/page ref-block)))
-     (= id (:db/id (:logseq.property/view-for ref-block)))
-     (entity-util/hidden? (:block/page ref-block))
-     (entity-util/hidden? ref-block)
-     (and (entity-util/class? entity)
-          (let [children (db-class/get-structured-children db id)
-                class-ids (set (conj children id))]
-            (some class-ids (map :db/id (:block/tags ref-block)))))
-     (some? (get ref-block (:db/ident entity))))))
+  ((hidden-ref-pred db id) ref-block))
 
 (defn get-block-refs
   [db id]
   (let [with-alias (->> (get-block-alias db id)
                         (cons id)
-                        distinct)]
+                        distinct)
+        hidden-ref?* (hidden-ref-pred db id)]
     (some->> with-alias
              (map #(d/entity db %))
              (mapcat :block/_refs)
-             (remove (fn [ref-block] (hidden-ref? db ref-block id))))))
+             (remove hidden-ref?*))))
 
 (defn get-block-refs-count
   [db id]
-  (count (get-block-refs db id)))
+  (let [with-alias (->> (get-block-alias db id)
+                        (cons id)
+                        distinct)
+        hidden-ref?* (hidden-ref-pred db id)
+        entity-by-id (memoize (fn [eid] (d/entity db eid)))]
+    (reduce
+     (fn [total alias-id]
+       (+ total
+          (reduce (fn [n datom]
+                    (if (hidden-ref?* (entity-by-id (:e datom)))
+                      n
+                      (inc n)))
+                  0
+                  (d/datoms db :avet :block/refs alias-id))))
+     0
+     with-alias)))
 
 (defn ^:large-vars/cleanup-todo get-block-and-children
   [db id-or-page-name {:keys [children? properties include-collapsed-children?]

+ 3 - 2
deps/db/src/logseq/db/common/reference.cljs

@@ -50,7 +50,8 @@
   [db id ref-blocks children-ids]
   (when (seq ref-blocks)
     (let [children (->> children-ids
-                        (map (fn [id] (d/entity db id))))]
+                        (map (fn [id] (d/entity db id))))
+          hidden-ref?* (common-initial-data/hidden-ref-pred db id)]
       (->> (concat (mapcat #(get-path-refs db %) ref-blocks)
                    (mapcat :block/refs (concat ref-blocks children)))
            frequencies
@@ -58,7 +59,7 @@
                    (when (and (ldb/page? ref)
                               (not= (:db/id ref) id)
                               (not= :block/tags (:db/ident ref))
-                              (not (common-initial-data/hidden-ref? db ref id)))
+                              (not (hidden-ref?* ref)))
                      [(:block/title ref) size])))
            (sort-by second #(> %1 %2))))))
 

+ 156 - 116
deps/db/src/logseq/db/common/view.cljs

@@ -203,11 +203,11 @@
                      (if entity?
                        (let [property (d/entity db property-ident)]
                          (if (match-property-value-as-entity? (first value') property)
-                           (boolean (seq (set/intersection (set (map :block/uuid value')) match)))
+                           (boolean (some match (map :block/uuid value')))
                            (boolean (seq (set/intersection (set (map db-property/property-value-content value'))
                                                            (set (map (comp db-property/property-value-content #(d/entity db [:block/uuid %]))
                                                                      match)))))))
-                       (boolean (seq (set/intersection (set value') match)))))
+                       (boolean (some match value'))))
 
                    :is-not
                    (cond
@@ -223,11 +223,11 @@
                      (if entity?
                        (let [property (d/entity db property-ident)]
                          (if (match-property-value-as-entity? (first value') property)
-                           (empty? (set/intersection (set (map :block/uuid value')) match))
+                           (not (some match (map :block/uuid value')))
                            (empty? (set/intersection (set (map db-property/property-value-content value'))
                                                      (set (map (comp db-property/property-value-content #(d/entity db [:block/uuid %]))
                                                                match))))))
-                       (empty? (set/intersection (set value') match))))
+                       (not (some match value'))))
 
                    :text-contains
                    (some (fn [v]
@@ -284,6 +284,44 @@
              result))))
       (:filters filters)))))
 
+(defn- ->filter-match-id
+  [db v]
+  (cond
+    (nil? v) nil
+    (number? v) v
+    (uuid? v) (some-> (d/entity db [:block/uuid v]) :db/id)
+    (de/entity? v) (:db/id v)
+    (and (map? v) (contains? v :db/id)) (:db/id v)
+    :else nil))
+
+(defn- build-fast-filter-pred
+  "Build a faster matcher for common filter shapes while preserving semantics.
+   Currently optimized for a single ref property filter with :is/:is-not."
+  [db filters input]
+  (when (and (string/blank? input)
+             (map? filters)
+             (not (:or? filters)))
+    (let [clauses (:filters filters)]
+      (when (= 1 (count clauses))
+        (let [[property-ident operator match] (first clauses)
+              property (d/entity db property-ident)
+              ref-property? (= :db.type/ref (:db/valueType property))]
+          (when (and ref-property?
+                     (#{:is :is-not} operator)
+                     (set? match)
+                     (seq match)
+                     (not (contains? match :empty)))
+            (let [match-ids (set (keep #(->filter-match-id db %) match))]
+              (when (seq match-ids)
+                (fn [row]
+                  (let [v (get row property-ident)
+                        value-col (cond
+                                    (set? v) v
+                                    (nil? v) nil
+                                    :else #{v})
+                        hit? (boolean (some match-ids (keep #(->filter-match-id db %) value-col)))]
+                    (if (= operator :is) hit? (not hit?))))))))))))
+
 (defn- get-exclude-page-ids
   [db]
   (let [property-tag-id (:db/id (d/entity db :logseq.class/Property))]
@@ -484,121 +522,123 @@
                :as opts}]
   ;; TODO: create a view for journals maybe?
   (cond
-    journals?
-    (let [ids (->> (ldb/get-latest-journals db)
-                   (mapv :db/id))]
-      {:count (count ids)
-       :data ids})
-    :else
-    (let [view (d/entity db view-id)
-          group-by-property (:logseq.property.view/group-by-property view)
-          list-view? (= :logseq.property.view/type.list (:db/ident (:logseq.property.view/type view)))
-          group-by-property-ident (or (:db/ident group-by-property) group-by-property-ident)
-          group-by-closed-values? (some? (:property/closed-values group-by-property))
-          ref-property? (= (:db/valueType group-by-property) :db.type/ref)
-          filters (or (:logseq.property.table/filters view) filters)
-          feat-type (or view-feature-type (:logseq.property.view/feature-type view))
-          query? (= feat-type :query-result)
-          query-entity-ids (when (seq query-entity-ids) (set query-entity-ids))
-          sorting (let [sorting* (:logseq.property.table/sorting view)]
-                    (if (or (= sorting* :logseq.property/empty-placeholder) (empty? sorting*))
-                      (or sorting [{:id :block/updated-at :asc? false}])
-                      sorting*))
-          fast-all-pages-ids (when (and (= feat-type :all-pages)
-                                        (not query?)
-                                        (nil? group-by-property-ident)
-                                        (empty? filters)
-                                        (string/blank? input))
-                               (get-all-page-ids-fast db sorting))]
-      (if fast-all-pages-ids
-        {:count (count fast-all-pages-ids)
-         :data fast-all-pages-ids}
-        (let [entities-result (if query?
-                                (keep (fn [id]
-                                        (let [e (d/entity db id)]
-                                          (when-not (= :logseq.property/query (:db/ident (:logseq.property/created-from-property e)))
-                                            e)))
-                                      query-entity-ids)
-                                (get-view-entities db view-id opts))
-              entities (if (= feat-type :linked-references)
-                         (:ref-blocks entities-result)
-                         entities-result)
-              filtered-entities (if (or (seq filters) (not (string/blank? input)))
-                                  (into [] (filter (fn [row] (row-matched? db row filters input))) entities)
-                                  entities)
-              group-by-page? (= group-by-property-ident :block/page)
-              readable-property-value-or-ent
-              (fn readable-property-value-or-ent [ent]
-                (let [pvalue (get ent group-by-property-ident)]
-                  (if (de/entity? pvalue)
-                    (if (match-property-value-as-entity? pvalue group-by-property)
-                      pvalue
-                      (db-property/property-value-content pvalue))
-                    pvalue)))
-              result (if group-by-property-ident
-                       (let [groups-sort-by-property-ident (or (:db/ident (:logseq.property.view/sort-groups-by-property view))
-                                                               :block/journal-day)
-                             desc? (:logseq.property.view/sort-groups-desc? view)
-                             result (->> filtered-entities
-                                         (group-by readable-property-value-or-ent)
-                                         (seq))
-                             keyfn (fn [groups-sort-by-property-ident]
-                                     (fn [[by-value _]]
-                                       (cond
-                                         group-by-page?
-                                         (let [v (get by-value groups-sort-by-property-ident)]
-                                           (if (and (= groups-sort-by-property-ident :block/journal-day) (not desc?)
-                                                    (nil? (:block/journal-day by-value)))
+     journals?
+     (let [ids (->> (ldb/get-latest-journals db)
+                    (mapv :db/id))]
+       {:count (count ids)
+        :data ids})
+     :else
+     (let [view (d/entity db view-id)
+           group-by-property (:logseq.property.view/group-by-property view)
+           list-view? (= :logseq.property.view/type.list (:db/ident (:logseq.property.view/type view)))
+           group-by-property-ident (or (:db/ident group-by-property) group-by-property-ident)
+           group-by-closed-values? (some? (:property/closed-values group-by-property))
+           ref-property? (= (:db/valueType group-by-property) :db.type/ref)
+           filters (or (:logseq.property.table/filters view) filters)
+           feat-type (or view-feature-type (:logseq.property.view/feature-type view))
+           query? (= feat-type :query-result)
+           query-entity-ids (when (seq query-entity-ids) (set query-entity-ids))
+           sorting (let [sorting* (:logseq.property.table/sorting view)]
+                     (if (or (= sorting* :logseq.property/empty-placeholder) (empty? sorting*))
+                       (or sorting [{:id :block/updated-at :asc? false}])
+                       sorting*))
+           fast-all-pages-ids (when (and (= feat-type :all-pages)
+                                         (not query?)
+                                         (nil? group-by-property-ident)
+                                         (empty? filters)
+                                         (string/blank? input))
+                                (get-all-page-ids-fast db sorting))]
+       (if fast-all-pages-ids
+         {:count (count fast-all-pages-ids)
+          :data fast-all-pages-ids}
+         (let [entities-result (if query?
+                                 (keep (fn [id]
+                                         (let [e (d/entity db id)]
+                                           (when-not (= :logseq.property/query (:db/ident (:logseq.property/created-from-property e)))
+                                             e)))
+                                       query-entity-ids)
+                                 (get-view-entities db view-id opts))
+               entities (if (= feat-type :linked-references)
+                          (:ref-blocks entities-result)
+                          entities-result)
+               filtered-entities (if (or (seq filters) (not (string/blank? input)))
+                                   (let [filter-pred (or (build-fast-filter-pred db filters input)
+                                                         (fn [row] (row-matched? db row filters input)))]
+                                     (into [] (filter filter-pred) entities))
+                                   entities)
+               group-by-page? (= group-by-property-ident :block/page)
+               readable-property-value-or-ent
+               (fn readable-property-value-or-ent [ent]
+                 (let [pvalue (get ent group-by-property-ident)]
+                   (if (de/entity? pvalue)
+                     (if (match-property-value-as-entity? pvalue group-by-property)
+                       pvalue
+                       (db-property/property-value-content pvalue))
+                     pvalue)))
+               result (if group-by-property-ident
+                        (let [groups-sort-by-property-ident (or (:db/ident (:logseq.property.view/sort-groups-by-property view))
+                                                                :block/journal-day)
+                              desc? (:logseq.property.view/sort-groups-desc? view)
+                              result (->> filtered-entities
+                                          (group-by readable-property-value-or-ent)
+                                          (seq))
+                              keyfn (fn [groups-sort-by-property-ident]
+                                      (fn [[by-value _]]
+                                        (cond
+                                          group-by-page?
+                                          (let [v (get by-value groups-sort-by-property-ident)]
+                                            (if (and (= groups-sort-by-property-ident :block/journal-day) (not desc?)
+                                                     (nil? (:block/journal-day by-value)))
                                             ;; Use MAX_SAFE_INTEGER so non-journal pages (without :block/journal-day) are sorted
                                             ;; after all journal pages when sorting by journal date.
-                                             js/Number.MAX_SAFE_INTEGER
-                                             v))
-                                         group-by-closed-values?
-                                         (:block/order by-value)
-                                         ref-property?
-                                         (db-property/property-value-content by-value)
-                                         :else
-                                         by-value)))]
-                         (sort (common-util/by-sorting
-                                (cond->
-                                 [{:get-value (keyfn groups-sort-by-property-ident)
-                                   :asc? (not desc?)}]
-                                  (not= groups-sort-by-property-ident :block/title)
-                                  (conj {:get-value (keyfn :block/title)
-                                         :asc? (not desc?)})))
-                               result))
-                       (sort-entities db sorting filtered-entities))
-              data' (if group-by-property-ident
-                      (map
-                       (fn [[by-value entities]]
-                         (let [by-value' (if (de/entity? by-value)
-                                           (select-keys by-value [:db/id :db/ident :block/uuid :block/title :block/name :logseq.property/value :logseq.property/icon :block/tags])
-                                           by-value)
-                               pages? (not (some :block/page entities))
-                               group (if (and list-view? (not pages?))
-                                       (let [parent-groups (->> entities
-                                                                (group-by :block/parent)
-                                                                (sort-by (fn [[parent _]] (:block/order parent))))]
-                                         (map
-                                          (fn [[_parent blocks]]
-                                            [(:block/uuid (first blocks))
-                                             (map (fn [b]
-                                                    {:db/id (:db/id b)
-                                                     :block/parent (:block/uuid (:block/parent b))})
-                                                  (ldb/sort-by-order blocks))])
-                                          parent-groups))
-                                       (->> (sort-entities db sorting entities)
-                                            (map :db/id)))]
-                           [by-value' group]))
-                       result)
-                      (map :db/id result))
-              dedupe-data? (or (= feat-type :property-objects) query?)]
-          (cond->
+                                              js/Number.MAX_SAFE_INTEGER
+                                              v))
+                                          group-by-closed-values?
+                                          (:block/order by-value)
+                                          ref-property?
+                                          (db-property/property-value-content by-value)
+                                          :else
+                                          by-value)))]
+                          (sort (common-util/by-sorting
+                                 (cond->
+                                  [{:get-value (keyfn groups-sort-by-property-ident)
+                                    :asc? (not desc?)}]
+                                   (not= groups-sort-by-property-ident :block/title)
+                                   (conj {:get-value (keyfn :block/title)
+                                          :asc? (not desc?)})))
+                                result))
+                        (sort-entities db sorting filtered-entities))
+               data' (if group-by-property-ident
+                       (map
+                        (fn [[by-value entities]]
+                          (let [by-value' (if (de/entity? by-value)
+                                            (select-keys by-value [:db/id :db/ident :block/uuid :block/title :block/name :logseq.property/value :logseq.property/icon :block/tags])
+                                            by-value)
+                                pages? (not (some :block/page entities))
+                                group (if (and list-view? (not pages?))
+                                        (let [parent-groups (->> entities
+                                                                 (group-by :block/parent)
+                                                                 (sort-by (fn [[parent _]] (:block/order parent))))]
+                                          (map
+                                           (fn [[_parent blocks]]
+                                             [(:block/uuid (first blocks))
+                                              (map (fn [b]
+                                                     {:db/id (:db/id b)
+                                                      :block/parent (:block/uuid (:block/parent b))})
+                                                   (ldb/sort-by-order blocks))])
+                                           parent-groups))
+                                        (->> (sort-entities db sorting entities)
+                                             (map :db/id)))]
+                            [by-value' group]))
+                        result)
+                       (map :db/id result))
+               dedupe-data? (or (= feat-type :property-objects) query?)]
+           (cond->
             {:count (count filtered-entities)
              :data (if dedupe-data?
                      (distinct data')
                      data')}
-            (= feat-type :linked-references)
-            (merge (select-keys entities-result [:ref-pages-count :ref-matched-children-ids]))
-            query?
-            (assoc :properties (get-query-properties query entities-result))))))))
+             (= feat-type :linked-references)
+             (merge (select-keys entities-result [:ref-pages-count :ref-matched-children-ids]))
+             query?
+             (assoc :properties (get-query-properties query entities-result))))))))

+ 49 - 0
deps/db/test/logseq/db/common/initial_data_refs_test.cljs

@@ -0,0 +1,49 @@
+(ns logseq.db.common.initial-data-refs-test
+  (:require [cljs.test :refer [deftest is]]
+            [datascript.core :as d]
+            [logseq.db.common.initial-data :as common-initial-data]
+            [logseq.db.test.helper :as db-test]))
+
+(deftest get-block-refs-count-matches-get-block-refs-for-class-page-test
+  (let [conn (db-test/create-conn-with-blocks
+              {:classes {:Topic {:block/title "Topic"}}
+               :pages-and-blocks
+               [{:page {:block/title "Ref A"}}
+                {:page {:block/title "Ref B"}}]})
+        topic-id (:db/id (d/entity @conn :user.class/Topic))
+        ref-a-id (d/q '[:find ?e .
+                        :in $ ?title
+                        :where [?e :block/title ?title]]
+                      @conn
+                      "Ref A")
+        ref-b-id (d/q '[:find ?e .
+                        :in $ ?title
+                        :where [?e :block/title ?title]]
+                      @conn
+                      "Ref B")
+        _ (d/transact! conn [[:db/add ref-a-id :block/refs topic-id]
+                             [:db/add ref-b-id :block/refs topic-id]
+                             [:db/add ref-b-id :logseq.property/hide? true]])
+        refs (common-initial-data/get-block-refs @conn topic-id)
+        count* (common-initial-data/get-block-refs-count @conn topic-id)]
+    (is (= (count refs) count*))
+    (is (= 1 count*))))
+
+(deftest get-block-refs-count-page-without-db-ident-test
+  (let [conn (db-test/create-conn-with-blocks
+              {:pages-and-blocks
+               [{:page {:block/title "Foo"}}
+                {:page {:block/title "Bar"}}]})
+        foo-id (d/q '[:find ?e .
+                      :in $ ?title
+                      :where [?e :block/title ?title]]
+                    @conn
+                    "Foo")
+        bar-id (d/q '[:find ?e .
+                      :in $ ?title
+                      :where [?e :block/title ?title]]
+                    @conn
+                    "Bar")
+        _ (d/transact! conn [[:db/add bar-id :block/refs foo-id]])
+        count* (common-initial-data/get-block-refs-count @conn foo-id)]
+    (is (= 1 count*))))

+ 93 - 9
deps/db/test/logseq/db/common/view_test.cljs

@@ -4,12 +4,15 @@
             [logseq.db.common.view :as db-view]
             [logseq.db.test.helper :as db-test]))
 
-(defn- all-pages-view-id [conn]
-  (let [tx (d/transact! conn [{:db/id -100
-                               :block/title "All pages test view"
-                               :block/uuid #uuid "00000000-0000-0000-0000-000000000100"
-                               :logseq.property.view/feature-type :all-pages
-                               :logseq.property.view/type :logseq.property.view/type.table}])]
+(defn- create-view-id
+  [conn feature-type & {:keys [view-for-id]}]
+  (let [tx (d/transact! conn [(cond-> {:db/id -100
+                                       :block/title "Test view"
+                                       :block/uuid #uuid "00000000-0000-0000-0000-000000000100"
+                                       :logseq.property.view/feature-type feature-type
+                                       :logseq.property.view/type :logseq.property.view/type.table}
+                                view-for-id
+                                (assoc :logseq.property/view-for view-for-id))])]
     (get-in tx [:tempids -100])))
 
 (deftest get-view-data-all-pages-sorts-and-filters-hidden-test
@@ -19,7 +22,7 @@
                 {:page {:block/title "Beta" :block/updated-at 20}}
                 {:page {:block/title "Hidden" :block/updated-at 30 :logseq.property/hide? true}}
                 {:page {:block/title "Deleted" :block/updated-at 40 :logseq.property/deleted-at 1}}]})
-        view-id (all-pages-view-id conn)
+        view-id (create-view-id conn :all-pages)
         result (db-view/get-view-data @conn view-id {:view-feature-type :all-pages
                                                      :sorting [{:id :block/updated-at :asc? false}]})
         ids (:data result)
@@ -33,7 +36,7 @@
                [{:page {:block/title "gamma" :block/updated-at 1}}
                 {:page {:block/title "alpha" :block/updated-at 2}}
                 {:page {:block/title "beta" :block/updated-at 3}}]})
-        view-id (all-pages-view-id conn)
+        view-id (create-view-id conn :all-pages)
         result (db-view/get-view-data @conn view-id {:view-feature-type :all-pages
                                                      :sorting [{:id :block/title :asc? true}]})
         ids (:data result)
@@ -58,10 +61,91 @@
                            "Without timestamp")
         without-ts-value (:block/updated-at (d/entity @conn without-ts-id))
         _ (d/transact! conn [[:db/retract without-ts-id :block/updated-at without-ts-value]])
-        view-id (all-pages-view-id conn)
+        view-id (create-view-id conn :class-objects :view-for-id class-id)
         result (db-view/get-view-data @conn view-id {:view-feature-type :class-objects
                                                      :view-for-id class-id
                                                      :sorting [{:id :block/updated-at :asc? false}]})
         titles (map (fn [id] (:block/title (d/entity @conn id))) (:data result))]
     (is (= 2 (:count result)))
     (is (= #{"With timestamp" "Without timestamp"} (set titles)))))
+
+(deftest get-view-data-class-objects-simple-is-filter-test
+  (let [conn (db-test/create-conn-with-blocks
+              {:classes {:Topic {:block/title "Topic"}}
+               :pages-and-blocks
+               [{:page {:block/title "A" :build/tags [:Topic]}}
+                {:page {:block/title "B" :build/tags [:Topic]}}
+                {:page {:block/title "C" :build/tags [:Topic]}}]})
+        class-id (:db/id (d/entity @conn :user.class/Topic))
+        view-id (create-view-id conn :class-objects :view-for-id class-id)
+        result (db-view/get-view-data @conn view-id {:view-feature-type :class-objects
+                                                     :view-for-id class-id
+                                                     :filters {:or? false
+                                                               :filters [[:block/title :is #{"B"}]]}})
+        titles (map (fn [id] (:block/title (d/entity @conn id))) (:data result))]
+    (is (= 1 (:count result)))
+    (is (= ["B"] titles))))
+
+(deftest get-view-data-linked-references-page-view-does-not-crash-on-missing-db-ident-test
+  (let [conn (db-test/create-conn-with-blocks
+              {:pages-and-blocks
+               [{:page {:block/title "Foo"}}
+                {:page {:block/title "Bar"}}]})
+        foo-id (d/q '[:find ?e .
+                      :in $ ?title
+                      :where [?e :block/title ?title]]
+                    @conn
+                    "Foo")
+        bar-id (d/q '[:find ?e .
+                      :in $ ?title
+                      :where [?e :block/title ?title]]
+                    @conn
+                    "Bar")
+        _ (d/transact! conn [[:db/add bar-id :block/refs foo-id]])
+        view-id (create-view-id conn :linked-references :view-for-id foo-id)
+        result (db-view/get-view-data @conn view-id {:view-feature-type :linked-references
+                                                     :view-for-id foo-id})]
+    (is (number? (:count result)))
+    (is (contains? (set (:data result)) bar-id))))
+
+(deftest get-view-data-class-objects-ref-filter-fast-path-test
+  (let [conn (db-test/create-conn-with-blocks
+              {:classes {:Topic {:block/title "Topic"}}
+               :pages-and-blocks
+               [{:page {:block/title "Page A"}
+                        :blocks [{:block/title "Obj A"
+                                  :build/tags [:Topic]}]}
+                {:page {:block/title "Page B"}
+                 :blocks [{:block/title "Obj B"
+                           :build/tags [:Topic]}]}]})
+        obj-a-id (d/q '[:find ?e .
+                         :in $ ?title
+                         :where [?e :block/title ?title]]
+                       @conn
+                       "Obj A")
+        obj-b-id (d/q '[:find ?e .
+                         :in $ ?title
+                         :where [?e :block/title ?title]]
+                       @conn
+                       "Obj B")
+        page-a-uuid (:block/uuid (d/entity @conn (d/q '[:find ?e .
+                                                        :in $ ?title
+                                                        :where [?e :block/title ?title]]
+                                                      @conn
+                                                      "Page A")))
+        class-id (:db/id (d/entity @conn :user.class/Topic))
+        view-id (create-view-id conn :class-objects :view-for-id class-id)
+        is-result (db-view/get-view-data @conn view-id {:view-feature-type :class-objects
+                                                        :view-for-id class-id
+                                                        :filters {:or? false
+                                                                  :filters [[:block/page :is #{page-a-uuid}]]}})
+        is-titles (map (fn [id] (:block/title (d/entity @conn id))) (:data is-result))
+        is-not-result (db-view/get-view-data @conn view-id {:view-feature-type :class-objects
+                                                            :view-for-id class-id
+                                                            :filters {:or? false
+                                                                      :filters [[:block/page :is-not #{page-a-uuid}]]}})
+        is-not-titles (set (map (fn [id] (:block/title (d/entity @conn id))) (:data is-not-result)))]
+    (is (= #{"Obj A"} (set is-titles)))
+    (is (= #{"Obj B"} is-not-titles))
+    (is (= #{obj-a-id} (set (:data is-result))))
+    (is (= #{obj-b-id} (set (:data is-not-result))))))