2013-12-15 104 views
2

在我的客戶端用戶界面我有一個窗體與不同的搜索標準,我想反應更新結果列表。搜索查詢被轉化成一個經典的minimongo選擇,保存在一個Session變量,然後我有觀察家做事的結果:流星minimongo動態光標

// Think of a AirBnb-like application 
// The session variable `search-query` is updated via a form 
// example: Session.set('search-query', {price: {$lt: 100}}); 

Offers = new Meteor.Collection('offers'); 
Session.setDefault('search-query', {}); 
resultsCursor = Offers.find(Session.get('search-query')); 

// I want to add and remove pins on a map 
resultCursor.observe({ 
    added: Map.addPin, 
    removed: Map.removePin 
}); 

Deps.autorun(function() { 
    // I want to modify the cursor selector and keep the observers 
    // so that I would only have the diff between the old search and 
    // the new one 
    // This `modifySelector` method doesn't exist 
    resultsCursor.modifySelector(Session.get('search-query')); 
}); 

我怎麼能實現光標對象上這個modifySelector方法?

基本上我覺得這個方法需要更新遊標的編譯版本,即selector_f屬性,然後重新運行觀察者(不會丟失先前結果的緩存)。還是有更好的解決方案?


編輯:你們有些人誤解了我想要做的事。讓我舉一個完整的例子:

Offers = new Meteor.Collection('offers'); 

if (Meteor.isServer && Offers.find().count() === 0) { 
    for (var i = 1; i < 4; i++) { 
    // Inserting documents {price: 1}, {price: 2} and {price: 3} 
    Offers.insert({price:i}) 
    } 
} 

if (Meteor.isClient) { 
    Session.setDefault('search-query', {price:1}); 
    resultsCursor = Offers.find(Session.get('search-query')); 

    resultsCursor.observe({ 
    added: function (doc) { 
     // First, this added observer is fired once with the document 
     // matching the default query {price: 1} 
     console.log('added:', doc); 
    } 
    }); 

    setTimeout(function() { 
    console.log('new search query'); 
    // Then one second later, I'd like to have my "added observer" fired 
    // twice with docs {price: 2} and {price: 3}. 
    Session.set('search-query', {}); 
    }, 1000); 
} 
+0

而不是'resultCursor.observe',你有沒有試過'resultCursor.observeChanges'?這隻會跟蹤結果集中的變化,這似乎是你想要的。雖然我不確定如果遊標本身(即遊標的查詢)發生變化,它是否會起作用。請參閱http://docs.meteor.com/#observe_changes –

+0

'observe' vs'observeChanges'與我的問題無關,即關於更改*遊標查詢*的問題。 – mquandalle

+0

我認爲你不需要'Deps.autorun'塊。如果你用'Offers.find(Session.get('search-query'))''創建一個遊標,我會認爲每次Session變量發生變化時,該遊標都會自動更新,因爲Session是被動的。然後改變'resultCursor.observe'的位置爲'resultCursor.observeChanges',並剪切最後7行。那樣有用嗎? –

回答

1

這並不能解決問題的方式,你似乎想要,但我認爲結果仍然是相同的。如果這是您明確不需要的解決方案,請告訴我,我可以刪除答案。我只是不想在評論中放置代碼。

Offers = new Meteor.Collection('offers'); 
Session.setDefault('search-query', {}); 
Template.map.pins = function() { 
    return Offers.find(Session.get('search-query')); 
} 

Template.map.placepins = function(pins) { 
    // use d3 or whatever to clear the map and then place all pins on the map 
} 

假設你的模板是這樣的:

<template name="map"> 
    {{placepins pins}} 
</template> 
+0

謝謝你的回答。 'placepins'函數將會得到新的結果,而不是舊的和新的結果之間的差異。所以這不是我要找的。但請不要刪除您的答案,因爲您認爲這是用某些代碼評論的更好方法。 – mquandalle

1

一種解決方法是手動diff的舊的和新的光標:

# Every time the query change, do a diff to add, move and remove pins on the screen 
# Assuming that the pins order are always the same, this use a single loop of complexity 
# o(n) rather than the naive loop in loop of complexity o(n^2) 
Deps.autorun => 
    old_pins = @pins 
    new_pins = [] 
    position = 0 
    old_pin = undefined # This variable needs to be in the Deps.autorun scope 

    # This is a simple algo to implement a kind of "reactive cursor" 
    # Sorting is done on the server, it's important to keep the order 
    collection.find(Session.get('search-query'), sort: [['mark', 'desc']]).forEach (product) => 
    if not old_pin? 
     old_pin = old_pins.shift() 

    while old_pin?.mark > product.mark 
     @removePin(old_pin) 
     old_pin = old_pins.shift() 

    if old_pin?._id == product._id 
     @movePin(old_pin, position++) 
     new_pins.push(old_pin) 
     old_pin = old_pins.shift() 

    else 
     newPin = @render(product, position++) 
     new_pins.push(newPin) 

    # Finish the job 
    if old_pin? 
    @removePin(old_pin) 
    for old_pin in old_pins 
    @removePin(old_pin) 

    @pins = new_pins 

但它是一個有點哈克和效率不高。此外,diff邏輯已經在minimongo中實現,所以最好重用它。

1

也許一個可以接受的解決方案是跟蹤本地集合中的舊針腳?就像這樣:

Session.setDefault('search-query', {}); 

var Offers = new Meteor.Collection('offers'); 
var OldOffers = new Meteor.Collection(null); 

var addNewPin = function(offer) { 
    // Add a pin only if it's a new offer, and then mark it as an old offer 
    if (!OldOffers.findOne({_id: offer._id})) { 
    Map.addPin(offer); 
    OldOffers.insert(offer); 
    } 
}; 

var removePinsExcept = function(ids) { 
    // Clean out the pins that no longer exist in the updated query, 
    // and remove them from the OldOffers collection 
    OldOffers.find({_id: {$nin: ids}}).forEach(function(offer) { 
    Map.removePin(offer); 
    OldOffers.remove({_id: offer._id}); 
    }); 
}; 

Deps.autorun(function() { 
    var offers = Offers.find(Session.get('search-query')); 

    removePinsExcept(offers.map(function(offer) { 
    return offer._id; 
    })); 

    offers.observe({ 
    added: addNewPin, 
    removed: Map.removePin 
    }); 
}); 

我不知道這比你的數組答案快多少,但我認爲它更可讀。你需要考慮的是,查詢改變的結果是否比刪除所有的引腳和每次重繪都快得多。我懷疑這可能是一個過早優化的例子。您希望用戶更改搜索查詢的頻率如何,以便舊查詢和新查詢的結果之間存在大量重疊?

+0

其實我的主要目標不是優化,而是動畫。所以把舊的結果放在一個集合中是一個有效的解決方案,就像我的數組排序一樣。但是我仍然想知道是否可以保留相同的集合並只修改遊標查詢。 – mquandalle

0

我在我自己的愛好Meteor項目中遇到同樣的問題。

filter會話var其中選擇器正在存儲。觸發任何複選框或按鈕更改filter和所有UI退回。

該解決方案有一些缺點和主要 - 你不能與其他用戶共享應用程序狀態。

所以我意識到更好的方式是在URL中存儲應用程序狀態。

可能是你的情況會更好嗎?

點擊按鈕現在基於它更改URL和UI呈現。我意識到它與FlowRouter

幫助閱讀:Keeping App State on the URL