Why use an object when denormalising data? - firebase

In the recent blog post on denormalising data, it suggests logging all of a user's comments beneath each user like so:
comments: {
comment1: true,
comment2: true
}
Why is this not a list like so:
comments: [
"comment1",
"comment2",
]
What are the advantages? Is there any difference at all? While I'm at it, how would you go about generating unique references for these comments for a distributed app? I was imagining that with a list I'd just push them onto the end and let the array take care of the index.

Firebase only ever stores objects. The JS client converts arrays into objects using the index as a key. So, for instance if you store the following array using set:
comments: [
"comment1",
"comment2"
]
In Forge (the graphical debugger), it will show up as:
comments:
0: comment1
1: comment2
Given this, storing the ID of the comment directly as a key has the advantage that you can refer to it directly in the security rules, for example, with an expression like:
root.child('comments').hasChild($comment)
In order to generate unique references for these comments, please use push (https://www.firebase.com/docs/managing-lists.html):
var commentsRef = new Firebase("https://<example>.firebaseio.com/comments");
var id = commentsRef.push({content: "Hello world!", author: "Alice"});
console.log(id); // Unique identifier for the comment just added.

Related

Firestore rule to only add/remove one item of array

To optimize usage, I have a Firestore collection with only one document, consisting in a single field, which is an array of strings.
This is what the data looks like in the collection. Just one document with one field, which is an array:
On the client side, the app is simply retrieving the entire status document, picking one at random, and then sending the entire array back minus the one it picked
var all = await metaRef.doc("status").get();
List tokens=all['all'];
var r=new Random();
int numar=r.nextInt(tokens.length);
var ales=tokens[numar];
tokens.removeAt(numar);
metaRef.doc("status").set({"all":tokens});
Then it tries to do some stuff with the string, which may fail or succeed. If it succeeds, then no more writing to the database, but if it fails it fetches that array again, adds the string back and pushes it:
var all = await metaRef.doc("status").get();
List tokens=all['all'];
List<String> toate=(tokens.map((element) => element as String).toList());
toate.add(ales.toString());
metaRef.doc("status").set({"all":toate});
You can use the methods associated with the Set object.
Here is an example to check that only 1 item was removed:
allow update: if checkremoveonlyoneitem()
function checkremoveonlyoneitem() {
let set = resource.data.array.toSet();
let setafter = request.resource.data.array.toSet();
return set.size() == setafter.size() + 1
&& set.intersection(setafter).size() == 1;
}
Then you can check that only one item was added. And you should also add additional checks in case the array does not exist on your doc.
If you are not sure about how the app performs the task i.e., successfully or not, then I guess it is nice idea to implement this logic in the client code. You can just make a simple conditional block which deletes the field from the document if the operation succeeds, either due to offline condition or any other issue. You can find the following sample from the following document regarding how to do it. Like this, with just one write you can delete the field which the user picks without updating the whole document.
city_ref = db.collection(u'cities').document(u'BJ')
city_ref.update({
u'capital': firestore.DELETE_FIELD
})snippets.py

firebase - pushing an object to an array angularFire2

I have a db in which I want to store data like this:
Books:
0:{pushed Object}
1:{pushed object} ...
As if it is an arrays so I can iterate over it and do the stuff I need to do. The problem is that I am storing data as pic related shows:
pic related
It is storing the object with the push_ID firebase assigns to an object but not with the index of an array. This is how I am pushing the object to my database:
let book_data = {
id: bookId,
name: bookData.name,
author: bookData.author,
genre: bookData.genre,
publishDate: bookData.publishDate
};
this.fb.list(`my-lists/${bookList.name}/books/`).push(bookData);
What am I doing wrong?
If needed, I'm coding this for an ionic project.
If you use push() method, Firebase assign it a unique ID without you doing anything else. so if you want a custom key that you defined, you can use set() to save data to a specified reference, also it can be replacing any existing data at that path.
let book_data = {
id: bookId,
name: bookData.name,
author: bookData.author,
genre: bookData.genre,
publishDate: bookData.publishDate
};
let yourkey = 0;
this.fb.list(`my-lists/${bookList.name}/books/${yourkey}`).set(bookData);
When you set new object next time, you have to change the key again and save data

Normalizr and recursive nested structure

I'm using normalizr to flatten a structure like the following one:
{
"fields":[{
"id":"29",
"text": "something"
}, {
"id":"16",
"text": "something"
"fields":[{
"id":"17",
"text": "something"
}]
}, {
"id":"18",
"text": "something"
}
}
My structure has an array of fields and a field may also have nested fields. There's only one level of nesting allowed.
What I'm trying to do is this:
const block = new schema.Entity('fields')
const blockList = new schema.Array(block)
block.define({
fields: blockList
})
const normalizedData = normalize(originalData, blockList)
After running this snippet, normalizedData has a results property and it only has the first level of field ids, even though entities has all the fields normalized, including the nested ones.
I'd like to have in the results array all the ids, including the nested ones. What am I missing?
I'd like to have in the results array all the ids, including the nested ones. What am I missing?
This is how Normalizr works. The returned result is always in the same format of the input data. You won't be able to get what you are asking for from Normalizr.
If, however, you're just looking for a list of blocks, pull that from the entities:
const blockIds = Object.keys(normalizedData.entities.blocks);
You should consider using a normalized form for your data structure in redux.
This is advisable if your application need growth in complexity.
There is an interesting article on redux docs.
http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html
A normalized form take some ideas from db counterpart and Normalizr works in that way, so your request does not really match with how Normalizr works.
Please consider #Paul Armstrong answer for a work around if you really need get blocks in that way.

How to return a subdocument from a document based on parameters in Minimongo?

Short question
Is there a way to filter documents client side?
Long question
So I have user accounts in a current Meteor JS project that store subdocuments, such as emails and comics.
_id: "jGZgBRqPRGFakcQRS",
comics:[
{_id: "z4fq6QWKfQiX4G5gb",
appearanceName: "Paradigm's Los",
coverart: "sampleurl",
heroName: "deadpool",
publisher: "marvel"
volNo: "-1"},
{_id: "kvQLtT5nMdqhsxBRp",
appearanceName: "Test",
coverart: "sampleurl2",
heroName: "deadpool",
publisher: "marvel",
volNo: "1"}],
emails: [{address: "email#email.com",
verified: false}],
username: "test"
I also have a different collection named "Volumes", that passes the _id, appearanceName, coverArt, heroName, publisher, and volNo to the comics array on click. I currently have an {{#each}} displaying the documents from Volume. I want to display client side a certain link if a certain comic exists in the "comics" array AND if the _id of the item in the comics array matches the Volumes _id and a different link if it doesn't meet both requirements. Since aggregation doesn't work, I'm completely stuck. Thanks ahead of time.
I didn't find easy way how to filter data in subdocuments, so I always create separate collection. Then you need to join collections, you can read how to do it in this article: http://www.meteor.hromnik.com/blog/joins-in-meteorjs-and-mongodb
If you want to just find data in subdocument you can do it:
Meteor.users.findOne({
'comics._id': yourComicsId
});
It will find user document with given comics id.

pagination in alfresco

I am working on an application which lists and searches document from alfresco. The issue is the alfresco can return upto 5000 records per query. but I don't want my application to list down all documents instead if I can some how implement pagination in alfresco, so that alfresco only return X result per page. I am using Alfresco 4 enterprise edition.
Any help or suggestion please.
UPDATE (Example)
I have written a web script which executes the query and returns all the documents satisfies the condition. Lets say, there are 5000 entries found. I want to modify my web script in a way that the web script returns 100 documents for 1st page, next 100 for second page and so on...
It'll be something like usage of Limit BY and OFFSET keywords. something like this
There are two ways to query on the SearchService (excluding the selectNodes/selectProperties calls). One way is to specify all your arguments directly to the query method. This has the advantage of being concise, but the disadvantage is that you don't get all the options.
Alternately, you can query with a SearchParameters object. This lets you do everything the simple query does, and more. Included in that more are setLimit, setSkipCount and setMaxItems, which will allow you to do your paging.
If your query used to be something like:
searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "lucene", myQuery);
You'd instead do something like:
SearchParameters sp = new SearchParameters();
sp.addStore(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE);
sp.setLanguage("lucene");
sp.setQuery(myQuery);
sp.setMaxItems(100);
sp.setSkipCount(900);
searchService.query(sp);
Assuming you have written your webscript in Javascript you can use the search.query() function and add the page property to the search definition as shown below:
var sort1 = {
column: "#{http://www.alfresco.org/model/content/1.0}modified",
ascending: false
};
var sort2 = {
column: "#{http://www.alfresco.org/model/content/1.0}created",
ascending: false
};
var paging = {
maxItems: 100,
skipCount: 0
};
var def = {
query: "cm:name:test*",
store: "workspace://SpacesStore",
language: "fts-alfresco",
sort: [sort1, sort2],
page: paging
};
var results = search.query(def);
You can find more information here: http://wiki.alfresco.com/wiki/4.0_JavaScript_API#Search_API

Resources