Move subdocument to another document - mongodb

I'm modelling a mongodb database that will have categories and articles.
The most common use will be listing articles through the categories, so I plan to make the articles as subdocuments of categories:
//db.categories.find()
[{
"id": 01,
"name": "Some category",
"articles: [{
"article_id": 01,
"title": "",
"content": ""
}]
}]
Sometimes, I might be moving articles from one category to another.
In a relational database, I should just update a foreign key, but in the above case, how can I move an article to another category?
Some details:
Preferable, the internal ObjectId should remain the same. But it's not mandatory.
Article might also contain subdocuments, if so, they must come along when moving.

You need first to get the article, then remove (pull) the article from its current category and then add (push) it into its new category, like this:
first get the article (article_id:01) from the category with id:01 ...
then pull the article with article_id:01 from the category with id:01
db.categories.update(
{"id" : 01, "articles.article_id" : 01}, // query
{"$pull" : {"articles" : {"article_id" : 01}}} // pull
)
and then push that same article (article_id:01) into its new category with id:02,
db.categories.update(
{"id" : 02}, // query
{"$push" : {"articles" : { // push
"article_id": 01,
"title": "",
"content": ""
}
}
}
)
if you want the array in the new category to be sorted after the push you have to use $each and $sort (see here).
For more info on $pull check here and for more info on $push check here.

Related

Index and sort on nested variable field MongoDB

I have a document that looks like this :
{
"_id": "chatID"
"presence": {
"userID1": 1647627240464,
"userID2": 1647227540464
},
}
I need to query for each userID the chats where he is present and order by the timestamp in the presence map.
I am aware that this is probably not the best way to do, before i had 1 element per user meaning duplicating the chatIDs, but it's a pain to update them all because it would look like :
{
"_id": "userID1chatID",
"at": 1647627240464,
"ids": "30EYwO01_Nyq7dMqe_O3vfL3AH",
"members": ["userID1", "userID2", "userID3"],
"owner": "userID1",
"present": true,
"uid": "chatID",
"url": "databaseURL"
}
This would allow me to find the chats where userID1 is present: true and order by at DESCENDING.
The problem with this is that i need to update the at attribute for all the documents (one per user) for this same chat room.
How can i do this same query while maintaining a single document with present as a map ?
Problem : the index would be on a variable : userID1, userID2, etc...
like : present.userID1 and seems to not be convenient for use when userID1 can be removed from the present map if the user leaves the chat.
Please let me know if this is unclear, thanks in advance.

MongoDB Schema optimization for contacts

I am storing my contacts in mongodb like this but main drawback of this schema is I am not able to store 40k-50k contacts in one document due to limit of 16mb.
I want to change my schema now. So can anyone please suggest me best way to redesign this.
Here is my sample doucument
{
"_id" : ObjectId("5c53653451154c6da4623a77"),
"contacts" : [{
name:"",
email:"",
group:[5c53653451154c6da4623a79]
}],
"groups" : [{
_id: ObjectId("5c53653451154c6da4623a79"),
group_name:"test"
}],
}
According to you document sample, contacts belongs to a group.
In that scenario, there are different ways to end up with a better schema:
1- Document embedding:
You will have an array of contacts inside each group document.
collection groups:
{
"_id": ObjectId("5c53653451154c6da4623a79"),
"group_name":"test",
"contacts": [
{
"name":"something",
"email":"something",
},
{
"name":"something else",
"email":"something else",
}
]
}
2- Document referencing:
You will have two collections - contacts and groups - and store a group reference inside each contact.
collection contacts:
{
"_id" : ObjectId("5c53653451154c6da4623a77"),
"name":"something",
"email":"something",
"groups":["5c53653451154c6da4623a79"]
},
{
"_id" : ObjectId("5c536s7df9sd7f987d9s7d98"),
"name":"something else",
"email":"something else",
"groups":["5c53653451154c6da4623a79"]
}
collection groups:
{
"_id": ObjectId("5c53653451154c6da4623a79"),
"group_name":"test"
}
Why are we referencing group inside contact and not the contrary? Because we probably will have more contacts than groups. This way we have smaller documents with smaller "reference arrays".
The path you will follow depends a lot on how many contacts you have per group. If this number is small, I would take the Document Embedding approach, for the sake of simplicity and access easiness. If you have a large number of contacts per group, I would use Document Reference, to have smaller documents.

Not sure how to save in MongoDB

I am trying to push to a sub-collection for a MongoDB and I am not sure how to go about it.
So, my post collection structure:
{
"_id": "58a3189b67476b420e465f8b",
"postID": "123456",
"comments": [
{
"subComments": [],
"comment": "This is a test",
"commentID": 1
}
]
}
So, what I want to do is find the above document using findOneAndUpdate and then select this particular comment (commentID == 1), and then push into the subComments of it.
I can see how you can findOneAndUpdate by it's postID and add comments by pushing in Mongoose but how do I find the post, and then the comment by it's commentID and then push into it's subComment?
Thanks in advance guys! Very stuck on this last piece of the puzzle.
It's similar to other find queries.
You can try this:
Post.findOneAndUpdate({
"postID" : givenPostID,
"comments.commentID" : givenCommentID
},{
$push : {
"comment.$.subComments" : givenSubComment
}
},function(err,result){...});
comments.commentID: givenCommentID will find the comment element with commentID == givenCommentID.
comment.$.subComments will push givenSubComment to the subComments array of the matched element of the comments array (matched based on commentID).
For more information Please read MongoDB $(update) operator documentation.

Should I use selector or views in Cloudant?

I'm having confusion about whether to use selector or views, or both, when try to get a result from the following scenario:
I need to do a wildsearch for a book and return the result of the books plus the price and the details of the store branch name.
So I tried using selector to do wildsearch using regex
"selector": {
"_id": {
"$gt": null
},
"type":"product",
"product_name": {
"$regex":"(?i)"+search
}
},
"fields": [
"_id",
"_rev",
"product_name"
]
I am able to get the result. The idea after getting the result is to use all the _id's from the result set and query to views to get more details like price and store branch name on other documents, which I feel is kind of odd and I'm not certain is that the correct way to do it.
Below is just the idea once I get the result of _id's and insert it as a "productId" variable.
var input = {
method : 'GET',
returnedContentType : 'json',
path : 'test/_design/app/_view/find_price'+"?keys=[\""+productId+"\"]",
};
return WL.Server.invokeHttp(input);
so I'm asking for input from an expert regarding this.
Another question is how to get the store_branch_name? Can it be done in a single view where we can get the product detail, prices and store branch name? Or do I need to have several views to achieve this?
expected result
product_name (from book document) : Book 1
branch_name (from branch array in Store document) : store 1 branch one
price ( from relationship document) : 79.9
References:
Book
"_id": "book1",
"_rev": "1...b",
"product_name": "Book 1",
"type": "book"
"_id": "book2",
"_rev": "1...b",
"product_name": "Book 2 etc",
"type": "book"
relationship
"_id": "c...5",
"_rev": "3...",
"type": "relationship",
"product_id": "book1",
"store_branch_id": "Store1_branch1",
"price": "79.9"
Store
{
"_id": "store1",
"_rev": "1...2",
"store_name": "Store 1 Name",
"type": "stores",
"branch": [
{
"branch_id": "store1_branch1",
"branch_name": "store 1 branch one",
"address": {
"street": "some address",
"postalcode": "33490",
"type": "addresses"
},
"geolocation": {
"coordinates": [
42.34493,
-71.093232
],
"type": "point"
},
"type": "storebranch"
},
{
"branch_id": "store1_branch2",
"branch_name":
**details ommit...**
}
]
}
In Cloudant Query, you can specify two different kinds of indexes, and it's important to know the differences between the two.
For the first part of your question, if you're using Cloudant Query's $regex operator for wildcard searches like that, you might be better off creating a Cloudant Query index of type "text" instead of type "json". It's in the Cloudant docs, but see the intro blog post for details: https://cloudant.com/blog/cloudant-query-grows-up-to-handle-ad-hoc-queries/ There's a more advanced post on this that covers the tradeoffs between the two types of indexes https://cloudant.com/blog/mango-json-vs-text-indexes/
It's harder to address the second part of your question without understanding how your application interacts with your data, but there are a couple pieces of advice.
1) Consider denormalizing some of this information so you're not doing the JOINs to begin with.
2) Inject more logic into your document keys, and use the traditional MapReduce View indexing system to emit a compound key (an array), that you can use to emulate a JOIN by taking advantage of the CouchDB/Cloudant index sorting rules.
That second one's a mouthful, but check out this example on YouTube: https://youtu.be/0al1KnCKjlA?t=23m39s
Here's a preview (example map function) of what I'm talking about:
'map' : function(doc)
{
if (doc.type==="user") {
emit( [doc._id], null );
}
else if (doc.type==="edge:follower") {
emit( [doc.user, doc.follows], {"_id":doc.follows} );
}
}
The resulting secondary index here would take advantage of the rules outlined in http://wiki.apache.org/couchdb/View_collation -- that strings sort before arrays, and arrays sort before objects. You could then issue range queries to emulate the results you'd get with a JOIN.
I think that's as much detail that's appropriate for here. Hope it helps!

Update Subdocument in Mongodb that stores id(s)

I am developing an application and we have decided to implement database with Mongodb, so I'm truly new to it. In database there is a collection for each company and we need to stores the ID(s) of each category of products of the company in a subdocument of company collection, Let's say we need to insert the following Object into collection:
{
name : "comapnyX"
address : {
"street" : "main street",
"ZipCode" : "12345"
},
categories : [
{ "name" : "category1" },
{ name" : "category2" }
]
}
An then if later on we decide to update the categories, we need add one more category to this subdocument, how do I have to create that update?
please also let me know if this is not a good practice from Datamodeling point of view in Mongodb
You can use the update method with the $push operator to add a new category:
db.collection.update( <query>,
{ $push: { categories: {name: "category3" } }
)
MongoDB Docs on $push: http://docs.mongodb.org/manual/reference/operator/update/push/