Breezejs - pattern for querying local cache
Asked Answered
H

1

6

I have a class called Item that is filtered by PeriodId. There are many periods - but we only need to look at one at a time. I want to present the user with an initial data load (say where PeriodId==1). I then want the user to be able to query/filter on other periods.

If the user selects PeriodId==2, I want the entityManager to query the local cache, and if the data is there, return that data. If it's not in the local cache, I want it to query the server, and add the set where PeriodId==2 to the cache. If the user then clicks on PeriodId==1, that data should already be in the cache and not round-trip to the server.

Using the code below, I am hitting the server every time I select a period (even if I just toggle back and forth). Is there a pattern that solves this... I do NOT know the primary keys here.

var getItems = function (myObservable, periodId, forceRemote) {

    var pId = periodId ? periodId : 1;

    var query = entityQuery.from('Item')
        .orderBy(orderBy.items)
        .where("PeriodId", "==", pId);

    if (!forceRemote) {
        var r = getLocal(query);
        if (r) {
            if (r.length > 3) {
                myObservable(r);
                return Q.resolve();
            }
        }
    }

    return manager.executeQuery(query)
        .then(querySucceeded)
        .fail(queryFailed);

    function querySucceeded(data) {
        if (myObservable) {
            myObservable(data.results);
        }
    }
};

function getLocal(query) {
    try {
        return manager.executeQueryLocally(query);
    } catch(e) {
        // set was not found in local cache, return null - forcing trip to server
        return null;
    }
}

UPDATE:

Incorporating Jay's suggestion, (I had to rename 'param' to 'pId', 'q' to 'query', reset the queryParamCache if forceRemote==true, and I had to prepend the FetchStrategy with 'breeze.' ):

var queryParamCache = {};

var getItems = function(myObservable, periodId, forceRemote) {

    if (forceRemote) {
        queryParamCache = {};
    }

    var pId = periodId ? periodId : 1;

    var query = entityQuery.from('Item')
        .orderBy(orderBy.items)
        .where('PeriodId', '==', pId);

    var isInCache = queryParamCache[pId];

    if (isInCache && !forceRemote) {
        query = query.using(breeze.FetchStrategy.FromLocalCache);
    } else {
        queryParamCache[pId] = true;
        query = query.using(breeze.FetchStrategy.FromServer);
    }

    return manager.executeQuery(query)
        .then(querySucceeded)
        .fail(queryFailed);

    function querySucceeded(data) {
        rosterObservable(data.results);
    }
};

function queryFailed(error) {
    var msg = 'Error retreiving data. ' + error.message;
    logError(msg, error);
    throw error;
}
Helicograph answered 3/4, 2013 at 16:42 Comment(0)
G
6

A simpler idea might just be to cache the fact as to whether you have performed the query or not. And instead of using the executeQueryLocally method, in this case it is easier to use the ability to specify a FetchStrategy. Note that when using FetchStrategy.FromLocalCache you will still get a promise returned, but that promise will actually execute immediately. The nice part is that you don't have to treat remote vs local queries differently.

var queryParamCache = {};
var getItemsPromise = function (periodId, forceRemote) {

    var pId = periodId ? periodId : 1;

    var query = entityQuery.from('Item')
        .orderBy(orderBy.items)
        .where("PeriodId", "==", pId);


    var isInCache = queryParamCache[pId];
    if (isInCache && !forceRemote) {
        q = q.using(FetchStrategy.FromLocalCache);
    } else {
        queryParamCache[pId] = true;
        q = q.using(FetchStrategy.FromServer);
    }
    return  manager.executeQuery(q);
}

The other feature of this approach is that it also deals with the case where the "remote" query returns no records. So, unlike testing for whether the local query actually returns data, which will be false both when you haven't yet run the query and when there is no data satisfying the query, this approach simply keeps track of whether you have ever executed the query, regardless of its results.

Gilleod answered 3/4, 2013 at 17:50 Comment(1)
Brilliant, Jay! A few tweaks and it worked... I'll update my question with the changes I made. Thanks!Helicograph

© 2022 - 2024 — McMap. All rights reserved.