(modelling in mongo) create a micro log inside the entity? - mongodb

Scenario:
I got a entity called "devices", the devices are changing its "assignation", but not many time in its cycle life.
{
device_id: "1234",
...,
assignation: {
status: "assigned",
ts: ...
}
}
Is good idea maintain a log inside object assignation (easy to implement).
{
device_id: "1234",
...,
assignation: {
status: "registered",
ts: 2020-11-04-..,
log: [
["registered", 2020-11-04-..],
["assigned", 2020-10-01-..],
["unassigned", 2020-01-01-..],
]
}
}
Or its better to keep this in another collection? Another collection makes not atomic operation in my insertions (importation process also).
Any other way to do this?

Related

Matching a master collection to a transactional status collection

My issue is rather specific so I'll try to explain my setup first.
I have a collection called clients, which is a master list of all clients. The model for it is:
{
id: String,
organizationId: Number,
networkId: String,
deviceSerial: String,
}
(irrelevant properties removed)
I also have a collection called clienttransactions, which is a list of when clients have gone online or offline. So each time a client comes online, it adds a record saying it came online (online: true), and vice-versa for when a client goes offline (online: false). The model for that looks like this:
{
clientId: String,
deviceSerial: String,
networkId: String,
organizationId: Number,
ts: Number,
online: Boolean
}
ts is a unix timestamp in seconds. Also if you're wondering why I need all those foreign keys on each record, it's because of the way the API where I get this data from works. So just ignore that.
issue:
Given a deviceSerial, networkId, and organizationId, I want to find all clients that were online at any point between a given time frame (given a start time and end time in epoch seconds).
Possible edge case: There could be times when a client came online before the given start time, and stayed online until after the given end time. In this case, there will be no transaction record within the time frame, but the client should still be seen as online.
Accounting for this case is what I'm having the most trouble with, since I can't simply just search for online transactions between the time frame. If there are no transactions for a client in the time frame, then I need to search outside the time frame to see if the last transaction made before the start time for that client was an online one.
I'm not super well-versed on the aggregation pipeline yet, so this is as far as I got:
const startTime = 1550601742;
const endTime = 1550599341;
ClientTransaction.aggregation([
{
$match: {
organizationId: 600381,
networkId: 'N_651896046061895525',
deviceSerial: 'Q2MN-3CUN-6GQM',
ts: {$lt: endTime}
}
},
{
$group: {
_id: '$clientId',
lastStatus: {
$max: '$ts'
},
online: {
$last: '$online'
}
}
}
]);
I think I'm halfway there with this. It finds all transactions for unique clients before the end time, but stops before process of checking if the client was actually online during the time frame.
You are looking for all clients whose latest activity is an online activity before start time or has online/offline activity between start and end time.
So something like should work
ClientTransaction.aggregation([
{ $match: {
organizationId: 600381,
networkId: 'N_651896046061895525',
deviceSerial: 'Q2MN-3CUN-6GQM',
ts: {$lte: endTime}
}
},
{ $sort:{"clentId":1, "ts":-1 } },
{ $group: {
_id: '$clientId',
latest: {
$first: '$$ROOT'
}
}},
{ $match:{
$or:[
{"latest.online":true,"latest.ts":{$lt:startTime}},
{"latest.ts":{$gte:startTime, $lte:endTime}}
]
}}
]);

Best way to store and organize data in MongoDB

I have a users in MongoDB and each user has an interface allowing them to set their current state of hunger being a combination of "hungry", "not hungry", "famished", "starving", or "full"
Each user can enter a multiple options for any period of time. For example, one use case would be "in the morning, record how my hunger is" and the user can put "not hungry" and "full". They can record how their hunger is at any time in the day, and as many times as they want.
Should I store the data as single entries, and then group the data by a date in MongoDB later on when I need to show it in a UI? Or should I store the data as an array of the options the user selected along with a date?
It depends on your future queries, and you may want to do both. Disk space is cheaper than processing, and it's always best to double your disk space than double your queries.
If you're only going to map by date then you'll want to group all users/states by date. If you're only going to map by user then you'll want to group all dates/states by user. If you're going to query by both, you should just make two Collections to minimize processing. Definitely use an array for the hunger state in either case.
Example structure for date grouping:
{ date: '1494288000',
time-of-day: [
{ am: [
{ user: asdfas, hunger-state: [hungry, full] },
{ user: juhags, hunger-state: [full] }
],
pm: [
{ user: asdfas, hunger-state: [hungry, full] },
{ user: juhags, hunger-state: [full] }
]}]}
It depends on how you are going to access it. If you want to report on a user's last known state, then the array might be better:
{
user_id: '5358e4249611f4a65e3068ab',
timestamp: '2017-05-08T17:30:00.000Z',
hunger: ['HUNGRY','FAMISHED'],
}
The timestamps of multiple records might not align perfectly if you are passing in the output from new Date() (note the second record is 99 ms later):
{
user_id: '5358e4249611f4a65e3068ab',
timestamp: '2017-05-08T17:30:00.000Z',
hunger: 'HUNGRY',
}
{
user_id: '5358e4249611f4a65e3068ab',
timestamp: '2017-05-08T17:30:00.099Z',
hunger: ['FAMISHED',
}
You should probably look at your data model though and try to get a more deterministic state model. Maybe:
{
user_id: '5358e4249611f4a65e3068ab',
timestamp: '2017-05-08T17:30:00.000Z',
isHungry: true,
hunger: 'FAMISHED',
}

In terms of application running time, is putting array elements on the root of UserDefaults preferred than putting them in container key?

I have an array of chat threads, which each also has array of chat messages.
Thread 1: "Hello world."
Participant: Alex, Billy, Cherry
Alex = "Hello"
Billy = "World"
Cherry = "Hahaha"
Thread 2: personal chat
Participants: Alex, Daniel
Alex = "Gday mate!"
Daniel = "Hey, how is it going?"
Alex = "Cool, lots of fun!"
I want to save this in UserDefaults. All of these will be saved in form of dictionaries and arrays.
Now imagine structure like this with thousands of threads and millions of messages. Then imagine I have to save it on the UserDefaults. Which way is better for me in saving these information?
Method 1: Save everything under one key "threads". In this method, I have to load everything and save everything each time I add/change/delete even one message or thread.
{
"threads": [
{
"id":90, "title":"Hello World", "participants":[7,12,54],
"messages": [
{ "id":827, "sender":7, "text":"Hello" },
{ "id":828, "sender":12, "text":"World" },
{ "id":836, "sender":7, "text":"Hahaha" }
...
]
},
{
"id":92, "title":"", "participants":[7,9],
"messages": [
{ "id":850, "sender":7, "text":"Gday mate!" },
{ "id":855, "sender":12, "text":"Hey, how is it going?" },
{ "id":861, "sender":7, "text":"Cool, lot's of fun!" }
...
]
}
...
]
}
Method 2: Break down the threads into elements, and save them one by one on the root. This way I'll only operating with one key at a time. Still have the original "threads" key though, to know what thread entries do I have. But that means the key on the the root level is going to be massive (the phone needs to load all the root key each time I tried to access UserDefaults?), and I'm not only going to have just chats to save.
{
"threads": [ 90, 92, ... ],
"thread-90": {
"id":90, "title":"Hello World", "participants":[7,12,54],
"messages": [
{ "id":827, "sender":7, "text":"Hello" },
{ "id":828, "sender":12, "text":"World" },
{ "id":836, "sender":7, "text":"Hahaha" }
...
]
},
"thread-92": {
"id":92, "title":"", "participants":[7,9],
"messages": [
{ "id":850, "sender":7, "text":"Gday mate!" },
{ "id":855, "sender":12, "text":"Hey, how is it going?" },
{ "id":861, "sender":7, "text":"Cool, lot's of fun!" }
...
]
}
...
}
Which one is better in the long run in terms of access speed, application performance, and CPU load, if all of these will be accessed quite frequent? I don't know how significant the loading time is if it's from UserDefaults.

Can you update a single document based on the current state consistently?

Imagine I have a document structure like this.
{
_id: ObjectId('internalId'),
externalId: 'externalId',
history: [
{
effective: ISODate('2000-02-01T00:00:00.000Z'),
property: 'new value'
},
{
effective: ISODate('2000-01-01T00:00:00.000Z'),
property: 'value'
}
]
}
Each time this document is read, all of the properties are merged together in historical order into a final state, possibly stopping at a specific point in time.
To add a new history item, I would need to perform something like this.
{
$push: {
history: {
property: 'even newer value',
effective: new Date()
}
},
$setOnInsert: {
externalId: externalId
}
}
I would like to find a way to make sure that an update that does not modify the actual merged history state is never stored. However it seems like this would require a separate read operation, and thus an (external) pessimistic lock to be held, while it was determined if a revision could proceed.
This feels like an incorrect design. Help!

Where to put version __v in redux state?

I have a model that is scattered all around the application. I have a redux state tree:
{
page: {
modelPart1: ...,
... : {
modelPart2: ...
}
}
I need to keep a reference to mongoDb __v in my state too. Where is the best place to place it?
I was thinking about a separate branch model_metadata that would keep the metadata about docs (_id, __v, ...).
{
model_metadata: { <------------------------ HERE
model: {
_id: id,
__v: 2
}
}
page: {
modelPart1: ...,
... : {
modelPart2: ...
}
}
Is it a valid approach or would you recommend a different one?
Every reducer only can access its own part of state, so when you do
combineReducers({
one,
another
});
and access state in one, it is equivalent to doing store.getState().one, and the same for another. So, you need to split the data in page property of state into two parts: actual data and metadata. Just like the object you retrieve from Mongo.
The point in having metadata and actual data being processed by the same reducer is that every time a reducer function is performed, you have everything you need about your object in state argument of that function. Splitting the data into two different reducers would make things way more complicated.
So, the new data representation in page would look like
{
model_metadata: { <------------------------ HERE
model: {
_id: id,
__v: 2
}
}
page: {
modelPart1: ...,
... : {
modelPart2: ...
}
}
while connecting to page would look like
connect(state => ({
page: state.page
})(...)