Firebase Structure Database and Queries - ios

I'm developing an employee time management app in swift, I would be happy to help you with the structure of the data and how to extract some queries.
The user database is like this:
https://i.stack.imgur.com/hfSXM.jpg
{"Database":
{"users"
{"3iHTIn1MicMdPbgEV6nnMy5ijHq1":
{ "company" : "My Company",
"email" : "edel#gmail.com",
"name" : "Tom",
"type" : "Employee"
}
}
}
}
The user will clock in and out when start and finish job.
Is better to open new child in the project for the dates and the hours or to add to the user child with the date? (please see the queries that I want to pull before answer)
Help with the queries:
Pull all the dates and the hours for this month for user.
Pull all the user that have same company name and work now (the field clock in are full and the field clock out are empty).
Pull list all the company names that have for all users and delete duplicate (is better to make another child in the project with only company’s names?)
Thanks!!

A few thoughts. I would probably denormalize the data because you have several ways you want to query it. I'm using some pseudo code to keep the answer short(er)
Pull all the dates and the hours for this month for user.
the challenge here is querying by two child nodes; one of them being a certain user and the other being a range of dates. The solution is a compound query of uid and YYMM (year month)
here's the structure:
jobs
job_0
start: 170518090135
end: 170518170515
uid_yymm: uid_0_1705
uid: uid_0
company_id: company_0
comp_status: company_0_true
job_1
start: 1705190835
end: 17051910430
uid_yymm: uid_0_1705
uid: uid_1
company_id: company_0
comp_status: company_0_false
queryOrdered(byChild: "uid_yymm").queryEqual(to value: "uid_0_1705")
This will query all of the jobs for uid_0 with the year being 2017 and month being 05.
Pull all the user that have same company name and work now (the field
clock in are full and the field clock out are empty).
Searching for no data can make things more complex with NoSQL so we'll just avoid that completely. Again, a compound query will do the job with the above structure on the comp_status node.
queryOrdered(byChild: "comp_status").queryEqual(to value: "company_0_true")
this query will return all of the users that work for company_0 that are (true) on the clock. when they go off the clock, as in uid_1's case, set to company_0_false.
Pull list all the company names that have for all users and delete
duplicate (is better to make another child in the project with only
company’s names?)
This is a little vague but I think the answer is yes, have a separate company names node but keep a reference to the company in the users node.
Database
users
3iHTIn1MicMdPbgEV6nnMy5ijHq1
company: "company_0"
email: "edel#gmail.com"
name: "Tom"
type: "Employee"
6889js9i99s9ksij8asd88as8d8
company: "company_1"
email: "bill#gmail.com"
name: "bill"
type: "Employee"
companies
company_0
comp_name: "Electric Company"
company_1
comp_name: "Bad Company"

Related

Fetching array equal to any array element ordered by field - Swift - Firestore

I need to make a Firestore query in a collection that:
is ordered by field "timestamp"
the field "uid" is equal to any of the elements of a strings array (more than 10)
I need to paginate the query so that I'm able to perform later more queries starting from a precise timestamp
The code should look like this:
Firestore.firestore()
.where(field: "uid", in: arrayOfUids)
.order(by: "timestamp", descending: false)
.limit(to: 5)
or in case of starting from a particular timestamp
Firestore.firestore()
.where(field: "uid", in: arrayOfUids)
.startAt("timestmap", lastTimestampFetched)
.order(by: "timestamp", descending: false)
.limit(to: 5)
How can I achieve this result?
In this topic was suggested to perform multiple calls filtering with up to 10 elements in the array in each call (the maximum number of array elements for Firestore to be able to compare) but I didn't have the constraint of ordering by timestamp.
EDIT:
I cannot order the query result client-side, it wouldn't be helpful for my use case.
Let's say that we have 30 uids to query for:
First, we have to divide these uids into groups of 10.
So we'll have 3 groups of 10 uids each (the max array lenght to for querying with Firestore); for each of this groups we'll do a separate query which looks like this:
Firestore.firestore()
.where(field: "uid", in: arrayOfTenUids)
.startAt("timestmap", lastTimestampFetched)
.order(by: "timestamp", descending: false)
.limit(to: 5)
For each query, we save the last document timestamp in order to be able to perform successive queries with pagination.
Here is the problem, we are going to have multiple last timestamps, since we are performing multiple queries, which timestamp are we going to save?
1st option: the last timestamp of all last timestamps
2nd option: the first timestamp of all last timestamps
1st case -> in successive queries we are going to fetch some documents that have been previously fetched
2nd case -> in successive queries we are going to miss some documents that have been previously fetched (because the last timestamp of one query could be after the last timestamp of another one)
This topic is really hard to explain but I did my best.
Based on the comments let me try to create the current Firestore structure and re-state the objective, then suggest a solution. I am ignoring the timestamp for the moment:
Posts (collection)
post_0
uid: uid_4
post_1:
uid: uid_1
post_2:
uid: uid_3
post_3:
uid: uid_2
and then
Matches (collection)
uid_0
uid_1
uid_2
uid_3
uid_5
uid_4
The objective is for the current user, say uid_0, to read their node within the Matches collection which would be this
uid_0
uid_1
uid_2
uid_3
and then get the posts from the Post nodes where the uid field matches the uid's in that list. The result should be post_1, post_2 and post_3.
The issue is the users node, uid_0, could have dozens of child uid's and since Firestore can only match up to 10 at a time using the 'in' function
.where(field: "uid", in: arrayOfUids)
it would take multiple reads - and then there's the issue of the timestamp and sorting along with paginating.
My suggestion is to simplify; if the above stated goal is correct, instead of storing those different uid's with each post, just store the uid of the current user, like this
Posts (collection)
post_0
uid: uid_2
post_1:
uid: uid_0
post_2:
uid: uid_0
post_3:
uid: uid_0
Then query the Posts node for uid = uid_0. The result will be exactly the same and much simplier to implement and maintain and then the timestamp/pagination issue is no longer and issue.
Obviously this is a simple example; I would imagine you want to store multiple uid's within each post, so whichever user is logged in can retrieve their selection of posts. Do to that, use array contains to retrieve the posts that match for this user.
Posts (collection)
post_0
uid_array:
uid_2
uid_9
uid_21

Order by nodes that received more relationships in a certain period of time

Is it possible to order by nodes that received more relationships in a certain period of time?
For example, I have User and Movie, and a User can LIKE a Movie. The LIKE relationship has a property called date, which is the moment the user liked the product.
What I want is: the Products that received more LIKE in the last 2 days.
How can I do it? :)
I'm assuming that you are storing dates as strings like "2017-09-02 00:00:00". So in this case I believe you can try something like:
MATCH (:User)-[like:LIKE]->(movie:Movie)
WHERE like.date > "date from 2 days ago"
RETURN movie, count(like) as numberOfLikes
ORDER BY numberOfLikes DESC

graphing dorm movements in neo4j

I'm really struggling getting my head around neo4j and was hoping someone might be able to help point me in the right direction with the below.
Basically, I have a list of what can be referred to as events; the event can be said to describe a patient entering and leaving a room.
Each event has a unique identifier; it also has an identifier for the student in question along with start and end times (e.g. the student entered the room at 12:00 and left at 12:05) and an identifier for the room.
The event and data might look along the lines of the below, columns separated by a pipe delimiter
ID|SID|ROOM|ENTERS|LEAVES
1|1|BLUE|1/01/2015 11:00|4/01/2015 10:19
2|2|GREEN|1/01/2015 12:11|1/01/2015 12:11
3|2|YELLOW|1/01/2015 12:11|1/01/2015 12:20
4|2|BLUE|1/01/2015 12:20|5/01/2015 10:48
5|3|GREEN|1/01/2015 18:41|1/01/2015 18:41
6|3|YELLOW|1/01/2015 18:41|1/01/2015 21:00
7|3|BLUE|1/01/2015 21:00|9/01/2015 9:30
8|4|BLUE|1/01/2015 19:30|3/01/2015 11:00
9|5|GREEN|2/01/2015 19:08|2/01/2015 19:08
10|5|ORANGE|2/01/2015 19:08|3/01/2015 2:43
11|5|PURPLE|3/01/2015 2:43|4/01/2015 16:44
12|6|GREEN|3/01/2015 11:52|3/01/2015 11:52
13|6|YELLOW|3/01/2015 11:52|3/01/2015 17:45
14|6|RED|3/01/2015 17:45|7/01/2015 10:00
Questions that might be asked could be:
what rooms have student x visited and in what order
what does the movement of students between rooms look like - to which room does students go to when they leave room y
That sounds simple enough but I'm tying myself into knots.
I started off creating unique constraints for both student and room
create constraint on (student: Student) assert student.id is unique
I then did the same for room.
I then loaded student as
using periodic commit 1000 load csv with headers from 'file://c:/event.csv' as line merge (s:Student {id: line.SID});
I also did the same for room and visits.
I have absolutely no idea how to create the relationships though to be able to answer the above questions though. Each event lists the time the student enters and leaves the room but not the room the student went to. Starting with the extract, should the extract be changed so that it contains the room the student left for? If someone could help talk through how I need to think of the relationships that needs to be created, that would be very much appreciated.
Cheers
As the popular saying goes, there is more than one way to skin an Ouphe - or thwart a mage. One way you could do it (which makes for the simplest modeling imo) is as follows :
CREATE CONSTRAINT ON (s:Student) ASSERT s.studentID IS UNIQUE;
CREATE CONSTRAINT ON (r:Room) ASSERT r.roomID IS UNIQUE;
USING PERIODIC COMMIT
LOAD CSV WITH HEADERS FROM "file:///dorm.csv" as line fieldterminator '|'
MERGE (s:Student {studentID: line.SID})
MERGE (r:Room {roomID: line.ROOM})
CREATE (s)-[:VISIT {starttime: apoc.date.parse(line.ENTERS,'s',"dd/MM/yyyy HH:mm"), endtime: apoc.date.parse(line.LEAVES,'s',"dd/MM/yyyy HH:mm")}]->(r);
# What rooms has student x visited and in what order
MATCH (s:Student {studentID: "2"})-[v:VISIT]->(r:Room)
RETURN r.roomID,v.starttime ORDER BY v.starttime;
# What does the movement of students between rooms look like - to which room does students go to when they leave room y
MATCH (s:Student)-[v:VISIT]->(r:Room {roomID: "GREEN"})
WITH s, v
MATCH (s)-[v2:VISIT]->(r2:Room)
WHERE v2.starttime > v.endtime
RETURN s.studentID, r2.roomID, v2.starttime ORDER BY s.studentID, v2.starttime;
So actually you would only have Student and Room as nodes and a student VISITing a room would make up the relationship (with the enter/leave times as properties of that relationship). Now, as you can see that might not be ideal for your second query (although it does work). One alternative is to have a Visit node and chain it (as timeline events) to both Students and Rooms. There's plenty of examples around on how to do that.
Hope this helps,
Tom

Creating an id number for each new node

Each time a new member joins my application, a "member" node is create. I wanted to attach a 8 digit unique ID # to each "member" node in order to keep track of things easier. Is it possible to generate a random 8 digit number or have a number that starts at 00000001 and go up from there
// Member profile
CREATE member: MemberProfile {
first_name: '',
last_name: '',
id_number: '',
}
I would like id_number to auto generate.
Creating a user ID is perhaps best done outside of neo4j, but one way to handle it would be to make a single node that would hold your current id value, then update it as part of each CREATE query. This would look something like this.
First create your MasterID node:
CREATE (:MasterID {id_number : 1})
Then, when you create a new MemberProfile node:
MATCH (l:MasterID)
CREATE (m:MemberProfile {first_name: '...', last_name : '...', id_number : l.id_number})
SET l.id_number = l.id_number + 1
Hope this helps.
You could try using this GraphAware Module - it transparently generates a UUID for each new node (or optionally only for nodes with certain labels). You could try forking it and change UUID to your 8-digit ID, if you wish.
Disclaimer: I'm one of the authors.

Is there a benefit to creating a very generic data model for a Rails 3 Project?

A Product can have lots of things said about it, I'll call them Properties. It can have a brief description. But that description can be in multiple languages. Likewise, it can have multiple Prices, some which are specific to Customers. Given the following data:
Product:
identifier: 123-ABC
Price:
value: $1.25
currency: USD
customer: Wal-Mart
Price:
value: $1.96
currency: USD
Description:
short: "A Widget"
language: EN
Description:
marketing: "Made from Space Age Polymers."
language: EN
Does it make sense to use STI here, and make a generic set of models:
Product has_many Properties
Property has_many Attributes
Price < Property
Description < Property
Package < Property
Is this way too broad of a generalization in the data model? Should I just stick with regular models and their associated tables?
No.
Seriously
No.
at least not in a SQL database.
If you want to use Cassandra or a NoSQL database, that's exactly how everything is stored.
Read my answer here
and
Read this
Think of a SQL Table with First Name, Last Name, and Birth date
Find all the LNAME = Page older than 30.
in one table it's
SELECT FNAME, LNAME, (SYSDATE - BDATE)/365 age
FROM people
WHERE LNAME = 'Page' and BDate < SYSDATE - 30*365
Now try it in an EAV
Post your answer.

Resources