Meteor/MongoDB Reference specific subdocument from another document - mongodb

I'm working on prototyping a note-taking application in Meteor; functional requirements include:
users have access to shared notes
notes contain distinct sections
each user needs to be able to add notations to notes/sections
notations can be preserved over time (e.g. add to existing notations without updating or deleting previously created notation)
notations should be private between users
Given the above, each document has a data key that contains the array of subdocuments - each section of the note. Something like this:
{
"_id" : ObjectId("someObjectID"),
"owner" : "Q5mpJZnAtFN5EMWT9",
"createdAt" : "2018-01-05T22:56:03.257Z",
"updatedAt" : "2018-01-06T12:07:03.123Z",
"parent" : null,
"title" : "Note Title",
"data" : [
{
"date" : "2018-01-05T22:56:03.257Z",
"title" : "Section 1 Title",
"text" : "Section content goes here..."
},
{
"date" : "2018-01-05T22:56:03.257Z",
"title" : "Section 2 Title",
"text" : "Section content goes here..."
}
]
}
For the main notes documents, the data array stores the sections as subdocuments; for user notations, the data array stores their personal notations as subdocuments. My thinking is to use the parent key to distinguish between shared notes and user notations:
parent : null for "top level", shared notes
something like parent : "yG8xrh6KiZXv7e8MD" to point back to the "top level" note or subdocument for user notations. (Hopefully this makes sense).
Two questions. First and foremost - is this a valid design?
If it IS a valid design, how do I then reference a specific subdocument? For example, in the above document, if a user wants to add a notation to Section 2 only? Can I add an _id to the subdocument and then use that value for the parent key in the notation document?

This not the complete solution, but just an example:
I would do it something like this. I'd modify your document a bit, adding notations field in every section:
{
"_id" : ObjectId("someObjectID"),
"owner" : "Q5mpJZnAtFN5EMWT9",
"createdAt" : "2018-01-05T22:56:03.257Z",
"updatedAt" : "2018-01-06T12:07:03.123Z",
"parent" : null,
"title" : "Note Title",
"data" : [
{
"date" : "2018-01-05T22:56:03.257Z",
"title" : "Section 1 Title",
"text" : "Section content goes here...",
"notations": [
{
_id: "some id",
version:1
userId: "fsajksffhj",
date: "2018-01-05T22:56:06",
note: "some note about this sectioon"
},
{
_id: "some id2",
version:1,
userId: "fsajksffhj",
date: "2018-01-05T22:56:06",
note: "some note about this sectioon"
},
{
_id: "some id1",
version:1,
userId: "fsajksffhj",
date: "2018-02-06T00:56:06",
note: "edited the first notation"
}
]
},
{
"date" : "2018-01-05T22:56:03.257Z",
"title" : "Section 2 Title",
"text" : "Section content goes here..."
}
]
}
notations should be private between users
This is harder part. I'd use Meteor Methods to do this. Another way could be to use MongoDB's aggregation functionality with match, unwind, re-match, group and create document again. You are using reactivity if using either of these.
Meteor.methods({
'notes.singleNote: function(noteId, notationsUserId) {
check(noteId, String);
check(notationsUserId);
let note = Notes.findOne(noteId);
// remove other users' notations
note.data = note.data.map(function(data) {
if (data.notations) {
data.notations = data.notations.filter(function(d) {
return d.userId === notationsUserId;
});
}
return data
});
});
return note;
}
});

Related

MongoDB: Is it possible to index(unique) subarrays from documents in an isolated way?

I recently encountered an issue, and I'd like to solve it. If anyone would give any suggestion I'll be grateful.
I have documents that represent "users" and each document has a subarray that is responsible to save some codes, they can be many for each user. The matter is, each user cannot have duplicate codes in its specific array, but at the same time, in this case, each document should be isolated, for example, being possible to have two or more identical codes but since they are from different documents(users).
In short, the subarray("codes") cannot have individually duplicated codes(code), but that shouldn't interfere with other documents
I could do that in the application part, but I think doing that guarantee directly on DB, it's safer.
Is it possible to create indexes for this specific situation?
Example of two documents representing their respective users:
{ // Document of user 1
"_id" : "1", //user 1 and its codes
"codes" : [
{
"code" : "1111",
"description" : "code 1",
},
{
"code" : "2222",
"description" : "code 2",
},
{
"code" : "3333",
"description" : "code 3",
}
]
},
{ // Document of user 2
"_id" : "2", //user 2 and its codes
"codes" : [
{
"code" : "1111",
"description" : "code 1",
},
{
"code" : "4444",
"description" : "code 2",
},
{
"code" : "2222",
"description" : "code 3",
}
]
}
Thank you!
Use https://docs.mongodb.com/manual/reference/operator/update/addToSet/ to maintain uniqueness of code subdocuments. You will need to ensure that you always specify code fields in the same order (e.g. code, description).

Adding multiple key/values

In my database.collection i.e. db.blog.posts I am trying to add a key and value that itself has multiple keys and values.
Current collection:
db.blog.posts.findOne()
"title":"blog posts"
I tried using $set, $push but nothing seems to work.
This also didn't work when I tried adding single collection:
db.blog.posts.updateOne({"title":"blog posts"}, {"$set":{"comments":[{"comment":"good post", "author":"john","votes":0}]}})
Nor insertOne instead of updateOne and I even tried with:
var myEmployee=[
{"comment":"good post", "author":"john", "votes":0},
{"comment":"i thought it was too short", "author":"claire","votes":3},
{"comment":"free watches", "author":"claire","votes":-1},
];
db.blog.posts.insert(myEmployee)
This is what I want:
"title" : "A blog post",
"comments" : [
{
"name" : "joe",
"email" : "joe#example.com",
"content" : "nice post."
},
{
"name" : "bob",
"email" : "bob#example.com",
"content" : "good post."
}
]
The updateOne command you have should have created an array for comments with a single entry. If you wanted multiple entries, you can just add multiple objects to the array in the update. The $set operator will change the value of the key to what you set as the second parameter.
db['blog.posts'].updateOne({"title":"blog posts"}, {
"$set": {
"comments":[
{
"name" : "joe",
"email" : "joe#example.com",
"content" : "nice post."
},
{
"name" : "bob",
"email" : "bob#example.com",
"content" : "good post."
}
]
}
})
If you want to add additional items to the comments, this can be done with $push. The $push operator adds to the array.
db['blog.posts'].updateOne({"title":"blog posts"}, {
"$push": {
"comments": {
"comment": "good post",
"author": "john",
"votes": 0
}
}
})
Docs for $set
Docs for $push
NB the examples above are for a collection named 'blog.posts' rather than a database named 'blog' and a collection names 'posts'. Ideally, brackets should be used for the property accessor where the collection name is not a valid JavaScript identifier although the dot notation in the question still works.

Perform a search on main collection field and array of objects simultaneously

I have my document structure as below:
{
"codeId" : 8.7628945723895E13, // long numeric value stored in scientific notation by Mongodb
"problemName" : "Hardware Problem",
"problemErrorCode" : "97695686856",
"status" : "active",
"problemDescription" : "ghdsojgnhsdjgh sdojghsdjoghdghd i0dhgjodshgddsgsdsdfghsdfg",
"subProblems" : [
{
"codeId" : 8.76289457238896E14,
"problemName" : "Some problem",
"problemErrorCode" : "57790389503490249640",
"problemDescription" : "This is edited",
"status" : "active",
"_id" : ObjectId("589476eeae39b20b1c15535b")
},
...
]
}
I have a search field which should search by codeId which basically serves as parentCodeID in search fields as shown below
Now, along with parentIdCode I want to search for codeId, problemCode, problemName and problemDescription as well.
How do I query the submodules with a regex search and at same time tag some parent field with "$or" clause etc. to achieve this ?
You can try something like this.
query = {
'$or': [{
"codeId":somevalue
}, {
"subProblems.codeId": {
"$regex": searchValue,
"$options": "i"
}
}, {
//rest of sub modules fields
}]
};

How to query and update nested arrays

I am building a course system. Each course has multiple sections, each section has multiple steps. My datastructure is as follows:
{
"_id" : "Mtz4DMTwMMKWTWbzE",
"slug" : "how-to-be-awesome",
"title" : "How to be awesome",
"description" : "In 4 easy lessons.",
"createdAt" : ISODate("2014-08-25T13:33:24.675Z"),
"sections" : [
{
"title" : "Be cool",
"description" : "Title says it all really",
"steps" : [
{
"title" : "Wear sunglasses",
"description" : "Always works."
},
{
"title" : "Be funny",
"description" : "Make an occasional joke. But no lame ones."
}
]
}
]
}
This worked while adding steps;
Course._collection.update( { _id: course._id, sections: section }, {
"$push": {
"sections.$.steps": step
}
})
But I can't figure out how to update a step. I tried to give the steps an ID and do it like that, but it's not working, apparently because it's two arrays deep, and you can't have two positionals ($) in a query. I tried something like this:
Course._collection.update( { _id: course._id, 'sections.steps._id': step._id }, {
"$set": {
"sections.steps.$.title": "test updated title"
}
})
But this gave the following error:
can't append to array using string field name: steps
Is there a way to do this? Or is my schema design off?
Thanks!

Filtering Mongo items by multiple fields and subfields

I have the following items in my collection:
> db.test.find().pretty()
{ "_id" : ObjectId("532c471a90bc7707609a3d4f"), "name" : "Alice" }
{
"_id" : ObjectId("532c472490bc7707609a3d50"),
"name" : "Bob",
"partner_type1" : {
"status" : "rejected"
}
}
{
"_id" : ObjectId("532c473e90bc7707609a3d51"),
"name" : "Carol",
"partner_type2" : {
"status" : "accepted"
}
}
{
"_id" : ObjectId("532c475790bc7707609a3d52"),
"name" : "Dave",
"partner_type1" : {
"status" : "pending"
}
}
There are two partner types: partner_type1 and partner_type2. A user cannot be accepted partner in the both of types. But he can be a rejected partner in partner_type1 but accepted in the another, for example.
How can I build Mongo query that fetches the users that can become partners?
When your user can only be accepted in one partner-type, you should turn it around: Have a field accepted_as:"partner_type1" or accepted_as:"partner_type2". For people who aren't accepted yet, either have no such field or set it to null.
In both cases, your query to get any non-accepted will then be:
{
data.accepted_as: null
}
(null matches both non-existing fields as well as fields explicitly set to null)
For me the logical schema would be this:
"partner : {
"type": 1,
"status" : "rejected"
}
At least that keeps the paths consistent between documents.
So if you want to stay away from using mapReduce type methods to find out "which field" it is on, and otherwise use plain queries and the aggregation pipeline, then don't vary field paths on documents. If you alter the "data" then that is the most consistent form.