techfort / LokiJS

javascript embeddable / in-memory database
http:/techfort.github.io/LokiJS
MIT License
6.71k stars 478 forks source link

How to remove from collection? #544

Closed Eldhelion closed 7 years ago

Eldhelion commented 7 years ago

due to local storage limitations, I installed lokijs, the migration was fine, and I'm able to create collections, add and update data.

so far, so good.

but when the user click on a button I need to send some stored data to my server, and then remove that from de collection

I'm creating the db as follow:

var idbAdapter = new LokiIndexedAdapter();
var pa = new loki.LokiPartitioningAdapter(idbAdapter, { paging: true });
            var db = new loki('survey_form',
                {
                    adapter: pa,
                    autosave: true,
                    autosaveInterval: 60000
                }
            );
            this.db = db;
            db.loadDatabase({}, function(result) {
                console.log('done');
                if (db.getCollection('quizes') == null) {
                    var q = db.addCollection('quizes', {
                        unique: ['id'],
                        indices: ['id'],
                        autoupdate: true
                    });
                }
                if (db.getCollection('quizAnswer') == null) {
                    var x = db.addCollection('quizAnswer', {
                        unique: ['id'],
                        indices: ['id'],
                        autoupdate: true
                    });
                }
                db.saveDatabase();
            });

I have tried to remove data in mutiples ways, but none worked case 1: passing object found to remove method

$.ajax({
                    url: url,
                    type: 'POST',
                    processData: false,
                    contentType: 'application/json',
                    data: JSON.stringify(json)
                }).success(function(data){
                    var quizesAnswer =  page.db.getCollection('quizAnswer');
                    var resultset = quizesAnswer.chain().find({id: v.id}).data();

                    if (resultset) {
                        quizesAnswer.remove(resultset);
                    }
                    page.db.saveDatabase();

                    page.respostasLocais.remove(v.id);
                    page.refresh();
                });

case 2: calling remove on the find chain

.success(function(data){
                    var quizesAnswer =  page.db.getCollection('quizAnswer');

                    quizesAnswer.chain().find({id: v.id}).remove();

                    page.db.saveDatabase();

                    page.respostasLocais.remove(v.id);

                    page.refresh();
                });

case 3: calling remove passing the id of the object to remove

 var quizesAnswer =  page.db.getCollection('quizAnswer');

                    quizesAnswer.chain().remove({id: v.id});

                    page.db.saveDatabase();

                    page.respostasLocais.remove(v.id);

                    page.refresh();

In all cases, lokijs seems find the object, but is unable to remove it loki1

so, wtf I'm doing wrong?

obeliskos commented 7 years ago

indexedDb is asynchronous so you need to wait for its callback before doing refresh... something like :

page.db.saveDatabase(function(err) {
  page.refresh();
});

also, unless you have a collection that can grow to be over 30 or megs I would recommend using paging : false

Eldhelion commented 7 years ago

I'm using lokijs just because my db will grow past 30 mb, and to be fair, maybe lokijs will not be enough, as my app will only upload its data when user do so. but I'll solve this later.

I changed my code, adding the callback to the saveDatabase (thank for the tip), but

back to the problem the db is loaded, I have not pasted here, but my ajax call is inside a $.each, and the array came indirectly from lokijs (I'm using lokijs to create a collection of models from Backbone.js).

so to resume, db is loaded collections are loaded.

I'm able to find my object on the collection through:

resultset = quizesAnswer.chain().find({id: v.id})

but none of the below method worked to remove said object from the collection, and the error is show in the image on my first post case 1:

var resultset = quizesAnswer.chain().find({id: v.id}).data();

if (resultset) {
quizesAnswer.remove(resultset);
}

or

if (resultset) {
                        quizesAnswer.remove(resultset[0]);
                    }

case 2

quizesAnswer.chain().find({id: v.id}).remove();

case 3

quizesAnswer.chain().remove({id: v.id});

printscreen of the object found with .find() b2989f396712495d96408795f2ca274d

obeliskos commented 7 years ago

all your examples involve a 'quizesAnswer' collection but your screenshot does the remove on a 'page.respostasLocais' object... why are you not using the same collection object reference? Then there is the page.db reference used for saving... lotta variety to get confused.

Can this be replicated without saving? Can you inspect or log collection document counts before and after remove to verify its removed in memory?

Do you need to reload page after every object insert or removal?

Also, I would probably use either of these :

quizesAnswer.chain().find({id: v.id}).remove();
quizesAnswer.findAndRemove({id: v.id});
var obj = quizesAnswer.find({id: v.id});
quizesAnswer.remove(obj);
Eldhelion commented 7 years ago

@obeliskos, ok, I was able to solve my problem while composing my response to your inquiries, I stumbled upon an odd thing, And fixing that solved it all for me. @obeliskos, thank you very much.

basically because of a oversight, I as inserting a backbone model on the lokijs, as show below:

loki4

so far, so good,

but when lokis loaded the collection later, it came as a normal object, but at this point lokijs was somewhat broken. as show below:

loki5

Well, for me, this issue can be closed, but given the fact that beside being 'broken' the find was working and the 'remove' not.

sugest that the code need to be revised to have a more coherent behavior

anyway, here follows my original response

all your examples involve a 'quizesAnswer' collection but your screenshot does the remove on a 'page.respostasLocais' object... why are you not using the same collection object reference?

I'm removing from both page.respostasLocais and quizesAnswer, quizesAnswer is a lokijs collection page.respostasLocais is a backbone.js collection

don't know if you had saw the backbone documentation, basically backbone is a framework like angular, wich give me : router, view, collection and models to work with.

Then there is the page.db reference used for saving... lotta variety to get confused.

I this case, we are inside a view. given the current scope aka inside a $.each, the 'this' refere to the current object on the loop, reason why I'm using 'page' to reference the View. So my View have the db, and load it on its creation, all view functions just use it.

So, page.respostasLocais is a backbone collection of backbone models, wich because of this, have functions to do lots of this, like a render that coupled with a template, know how display it self.

Can this be replicated without saving? Can you inspect or log collection document counts before and after remove to verify its removed in memory?

yes, the error is throws when the execution reach the remove function, and so my app stop before the saveDatabase all of this variantes throw said error

quizesAnswer.chain().find({id: v.id}).remove();

quizesAnswer.findAndRemove({id: v.id});

var obj = quizesAnswer.find({id: v.id});
quizesAnswer.remove(obj);

on the other hand, the backbone collection works as intended:

loki2

Do you need to reload page after every object insert or removal?

Don't know from where you came with 'insert' given that I'm trying to do a remove, but yes, kinda, given that I'm removing objects from the collection, in the view, I give a feedback to the user, my page.reflesh does just that, update number on the user screen

Also, I would probably use either of these :

point is, none is working, when I reach the line with a remove operation, my app crash, with the error as show in the image on my first post

"Object is not a document in the collection",

despite the fact that I just retrieved said object with a find function.

And here is a console.log of the quizesAnswer loki3

Kanaye commented 7 years ago

Hm I took a quick look. The (loki) collection throws that error because that object doesn't have a $loki property. I also can't see a meta object in your screenshot above. Loki adds these properties in collection.insert(). After a short search over backbones source I saw they are implementing their own toJSON() method on models. I guess that's were the $loki and meta properties got removed when loki serialized the db. I'm not familiar with backbone so I can't tell if there is a way to make backbone also return these properties in toJSON()

obeliskos commented 7 years ago

Yea @Kanaye is right, the screenshot with the proto being object has no $loki or meta properties on the document in the collection data array. Your screenshot with proto derived from Backbone.Model looks correct. What is confusing me is that the objects in both screenshots look nothing like each other... they have entirely different properties.

I'm not familiar with backbone but it sounds like it is creating almost a 'proxy' object and you are serializing the proxy. Do any of the properties in the screenshot with proto of 'Backbone.Model" have meaning to you or are they used internally to backbone? We will serialize the object reference you insert, and if those object references have a bunch of getter/setter methods, those won't get persisted.

It could be that you need to :

In some situations, we allow you to define the prototype of the objects we inflate from by overriding the proto (see spec/generic/typed.spec.js) but I'm not certain you are even saving correctly.

So your page.refresh does not reload page? If so that's good and maybe we can try using loki memory adapter to examine what actually gets saved and make sure it has all your data (rather than just backbone metadata). Maybe start with something roughly like this example.

If you can replicate this problem without even saving, then inspect the lokijs db collection data immediately after inserting an object to see if it has attached $loki and meta... otherwise you could try to call JSON.stringify(yourobject) and make sure it serializes ok (rather than just getter setter functions)

Eldhelion commented 7 years ago

@obeliskos , @Kanaye

first and foremost, just to make sure you guys saw in my last response, I was able to solve the problem of crash when trying to remove from lokijs

I never intended to put backbone.model directly on lokijs. As @Kanaye have pointed backbone.model have a toJson() method but I had forgoten to use that..

Where I was doing the insert, I just changed from answers.insert(current); to answers.insert(JSON.parse(JSON.stringify(current)));

so I hope not had wasted your time, trying to solve a solved puzzle.

======================

Yea @Kanaye is right, the screenshot with the proto being object has no $loki or meta properties on the document in the collection data array.

Yes, I saw that, that's what I was said

but when lokis loaded the collection later, it came as a normal object, but at this point lokijs was somewhat broken. as show below:

note also that, idIndex: [0] -> undefined and MaxId: 0

Your screenshot with proto derived from Backbone.Model looks correct.

Yes, the object show is fine, and the properties shown are from backbone model. My own properties are encapsulated in the 'attributes' object

In the second imagem (where $loki and meta are missing) the properties show are my own (aka, the ones inside the 'atributes' property of backbone model)

well, It was pretty trick to do a in memoryAdapter db after inserts, before save l1

memory adapter l2

sandbox.db.0.value:

"{"id":"585d5be248177e1808b46d27","formName":"Clone - MARCIO-NOVO-9","slug":"mnmgfgarcio-novo-9","questions":[],"creationDate":{"date":"2016-12-23 15:16:18.000000","timezone_type":3,"timezone":"America/Sao_Paulo"}}$<
{"id":"585d278e48177eeb7bb46d22","formName":"MARCIO-NOVO-9","slug":"marcio-novodsfsdfsdf-9","questions":[],"creationDate":{"date":"2016-12-23 11:33:02.000000","timezone_type":3,"timezone":"America/Sao_Paulo"}}$<
{"id":"5863be6f48177e1d6ac84cd9","formName":"Clone - Clone - MARCIO-NOVO-9","slug":"marcio-novo-9","questions":[],"creationDate":{"date":"2016-12-28 11:30:23.000000","timezone_type":3,"timezone":"America/Sao_Paulo"}}$<
{"id":"585d2af948177eef7bb46d24","formName":"Teste Principal","slug":"marcio-teste-dasdasd3-clone","questions":[],"creationDate":{"date":"2016-12-23 11:47:37.000000","timezone_type":3,"timezone":"America/Sao_Paulo"}}$<
{"id":"585bfb2448177e75246a8ba3","formName":"Teste Marcio","slug":"marcio-teste-3-clone","questions":[],"creationDate":{"date":"2016-12-22 14:11:16.000000","timezone_type":3,"timezone":"America/Sao_Paulo"}}$<
"

db after load l3

after seeing this logs. I think that the behavior show, is a combination of loki's trying to serialize the object for storage, and the toJson() method of the backbone.model returning only the properties inside the 'atributes' property

once again, thank for the help