MongoDB split string and Update - database

I have document structure like this:
{
"_id" : ObjectId("5a3a77d9d274eb44bc85d7c8"),
"b_id" : 3,
"b_display_tag" : "Veg Curry, Non Veg Curry"
}
I am able to get the first element from the string in b_display_tag using split and aggregate query:
db.blogs.aggregate([
{$project: {"b_display_tag": {$arrayElemAt:[{$split: ["$b_display_tag" , ","]}, 0]}}}
])
Result:
/* 1 */
{
"_id" : ObjectId("5a3a77d9d274eb44bc85d7c8"),
"b_display_tag" : "Veg Curry"
}
How can I update b_display_tag in the whole document with the first element from comma separated string?

Use below query to update b_display_tag. It will split b_tags using separator(') and update b_display_tag with first index.
db.blogs.find().forEach(function (blog)
{
if (blog.b_tags)
{
blog.b_display_tag=blog.b_tags.split(',');
blog.b_display_tag=blog.b_display_tag[0];
db.blogs.save(blog);
}
});

You can use $out in the aggregation pipeline to write it into a new collection, since here we need to update the existing collection blogs use the same collection name in $out.
db.blogs.aggregate([
{$project:
{
_id:1,
b_id:1,
b_display_tag:{$arrayElemAt:[{$split:["$b_display_tag", ","]}, 0]}
}
},
{$out:"blogs"}
]);

Related

How do I properly insert a document with object that contains another object

In mongodb I'm wondering how you can properly insert json to the db from the command line with an object that contains an object?
db.burgers.insert(
[
{'burger': {
'patty': 'beef',
'cheese': false,
'toppings': [
'ketchup', 'onions', 'pickles'
]
}}
]
)
When I try the code above, db.burgers.burger.find() returns 0, when I am expecting it to return 1
The following is how they get returned.
db.burgers.find().pretty()
{
"_id" : ObjectId("5f87b58afd426d861d55b5ff"),
"burger" : {
"patty" : "beef",
"cheese" : false,
"toppings" : [
"ketchup",
"onions",
"pickles"
]
}
}
In mongodb I'm wondering how you can properly insert json to the db
from the command line with an object that contains an object?
Your inserting the document into the collection is correct. You can use insertOne, insertMany or insert methods to insert documents. The insertOne is specific for insert one document at a time. insert and insertMany can be used to insert one or more documents at a time.
From the command line, mongo shell:
> var doc = { 'burger': {
'patty': 'beef',
'cheese': false,
'toppings': [ 'ketchup', 'onions', 'pickles' ]
} }
> db.burgers.insertOne(doc)
{
"acknowledged" : true,
"insertedId" : ObjectId("5f87f68dd039d5cb61ae2251")
}
Note the _id field is created by the MongoDB.
You can use insertMany to insert many documents - an array of JSON documents. For example,
var docs_array = [ {... }, {... }, ... ]
db.burgers.insertMany(docs_array)
When I try the code above, db.burgers.burger.find() returns 0, when I
am expecting it to return 1
You can query the document using the find or findOne methods. The find returns a cursor (with multiple documents) and the findOne always returns one document (or a null if no document is found). For example:
> db.burgers.findOne()
> db.burgers.findOne( { _id: ObjectId("5f87f68dd039d5cb61ae2251") } )
The above queries return the newly inserted document, above.
Your query db.burgers.burger.find() is not correct, I think. If you want only the burger field ( this a nested document) to be printed, then use projection, like this:
> db.burgers.findOne( {}, { burger: 1, _id: 0 } )
{
"burger" : {
"patty" : "beef",
"cheese" : false,
"toppings" : [
"ketchup",
"onions",
"pickles"
]
}
}
Note the projection allows output only the specific fields, and exclude others. In the above query, the _id field is excluded.
If you want to count documents in the collection use this:
> db.burgers.countDocuments({})
Reference:
Insert
Documents
Query
Documents
Query on Nested
Documents

MongoDB Aggregate search with comma separated string list in array

I have a MongoDB question. I have a search in an aggregation with $match.
Search should check an array if one of the values matches a value of the array inside the documents.
As an example:
var stringList = 'general,online,offline'; //--> should check each value of this list
and two documents as an example
{
"_id" : ObjectId("5e8f3a64ec717a0013d2f1f9"),
"category" : [
"general",
"online",
"internal",
"miscellaneous"
]},
{
"_id" : ObjectId("5e8f3afeec717a0013d2f1fa"),
"category" : [
"offline"
]
}
I´ve tried a lot but I don´t found out how it is possible to check each value of the string list with each value in the category array. My example should show both documents, but if I use $in I don´t get any result.
What I tried is:
Split the list by comma and map
use of $elemMatch
use if $in
use combination of $elemMatch and $in
I hope I could explain my problem with my aggregation.
Thx everyone for his help.
You should be able to split the string on , with .split function, then pass this in to a $in query.
var stringList = 'general,online,offline';
db.documents.find( { "category" : { $in : stringList.split(",") } } );
{ "_id" : ObjectId("5e8f3a64ec717a0013d2f1f9"), "category" : [ "general", "online", "internal", "miscellaneous" ] }
{ "_id" : ObjectId("5e8f3afeec717a0013d2f1fa"), "category" : [ "offline" ] }
You can also do this in a $match in an aggregation query.
> db.documents.aggregate([
{ $match : { category: { $in : stringList.split(",") } }}
])
Here is an example, where we can search the array name from by using the regex method in the query.
var x = ["sai","test","jacob","justin"],
regex = x.join("|");
db.documents.find({
"firstName": {
"$regex": regex,
"$options": "i"
}
});

Mongodb query on reference document

I have two schema defined in mongoose as follows,
Model: Article
var articleSchema = new mongoose.Schema({
sequenceId: String
title: String,
abstract: String,
authors: [String],
date: Date,
words: [{
selectedWord: String,
details: { type: Schema.Types.ObjectId, ref: 'Word' }
}],
otherTags: [String]
})
Model: Word
var wordSchema = new mongoose.Schema({
preferredWord: String,
synonyms: [String],
category: String
})
Now I am trying to get 2 sets of results for the following scenarios,
Get all the articles that have 'wordAbc' and/or 'wordXyz' in either selectedWord, preferredWord or synonyms
Get all the unique words in selectedWord, preferredWord and synonyms across all the articles in the database
What would be the best/efficient way perform query using mongoose?
For the first result I tried that partial query but getting the CastError message,
Article.find({})
.populate( 'words', null, { 'details': {$in: ['wordAbc', 'wordXyz']}} )
.exec(function(err, docs) {});
I think you can achieve both the things you want using aggregation pipeline.
Get all the articles that have 'wordAbc' and/or 'wordXyz' in either
selectedWord, preferredWord or synonyms
First you will need to populate all the words in details field of words array, and then match the articles based on selectedWord, preferredWord or synonyms.
This can be achieved like this:
Article.aggregate([{
$unwind : {
path :"$words",
preserveNullAndEmptyArrays :true
}
},{
$lookup : {
from : "words",
localField : "words.details",
foreignField : "_id",
as : "words.details"
}
},{
$unwind : {
path : "$words.details",
preserveNullAndEmptyArrays : true
}
},{
$match : {
$or : [{
"words.selectedWord" : {$in: ['wordAbc', 'wordXyz']}
},{
"words.details.preferredWord" : {$in: ['wordAbc', 'wordXyz']}
},{
"words.details.synonyms" : {$in: ['wordAbc', 'wordXyz']}
}]
}
},{
$group : {
_id : "$_id",
title : {$first : "$title"},
abstract : {$first : "$abstract"},
authors : {$first : "$authors"},
date : {$first : "$date"},
words: {$push : "$words"},
otherTags: {$first : "$otherTags"}
}
}])
Get all the unique words in selectedWord, preferredWord and synonyms
across all the articles in the database
In this case you will have to unwind words array and then populate words.details from words collection, and then unwind synonyms array, so that we can create a set of selectedWord, preferredWord and synonyms across all the articles, and then finally make a whole set of all the unique words in the last stage of aggregation pipeline.
This can be achieved like this:
Article.aggregate([{
$project : {
words : "$words"
}
},{
$unwind : "$words"
},{
$lookup : {
from : "words",
localField : "words.details",
foreignField : "_id",
as : "words.details"
}
},{
$unwind : "$words.details"
},{
$unwind : "$words.details.synonyms"
},{
$project : {
selectedWord : "$words.selectedWord",
preferredWord : "$words.details.preferredWord",
synonyms : "$words.details.synonyms"
}
},{
$group : {
_id : "null",
selectedWord : {$addToSet :"$selectedWord"},
preferredWord : {$addToSet :"$preferredWord"},
synonyms : {$addToSet :"$synonyms"}
}
},{
$project : {
commonWords : {$setUnion : ["$selectedWord","$preferredWord","$synonys"]}
}
}])
Explanation of 2nd aggregation.
$project : We want only words, so i carried on words field of all the articles and removed all other unnecessary fields from the pipeline.
$unwind : we need to unwind the words array, so that we can $lookup words.details from words collection in the next stage of pipeline
$lookup : populate details from words collection.
$unwind : Since $lookup returns an array, we need to unwind it to make it an object
$unwind : unwind words.details.synonyms, so that we can group them and create array of unique words in next stage of pipeline, At this stage, individual documents in aggregation pipeline will look something like this:
{
words : {
selectedWord :"someword",
details : {
preferredWord : "otherword",
synonym : "synonymword"
}
}
}
$project : we needed this flatten the object structure. After this stage individual document in the pipeline would look like this:
{
selectedWord :"someword",
preferredWord : "otherword",
synonym : "synonymword"
}
$group : combine all the selectedWord into one array, preferredWord into one array, and synonyms in one array, $addToSet is used to remove duplicate objects
$project : combine all the 3 arrays and create one array of unique Words
For details information on all the used operators of mongoDB, read respective documentations.
$setUnion documentation
$addToSet documentation
$project documentation
$unwind documentation
Documentation of all the operators of mongodb aggregation pipeline
I hope this helps you out

MongoDB remove an item from an array inside an array of objects

I have a document that looks like this:
{
"_id" : ObjectId("56fea43a571332cc97e06d9c"),
"sections" : [
{
"_id" : ObjectId("56fea43a571332cc97e06d9e"),
"registered" : [
"123",
"e3d65a4e-2552-4995-ac5a-3c5180258d87"
]
}
]
}
I'd like to remove the 'e3d65a4e-2552-4995-ac5a-3c5180258d87' in the registered array of only the specific section with the _id of '56fea43a571332cc97e06d9e'.
My current attempt is something like this, but it just returns the original document unmodified.
db.test.findOneAndUpdate(
{
$and: [
{'sections._id': ObjectId('56fea43a571332cc97e06d9e')},
{'sections.registered': 'e3d65a4e-2552-4995-ac5a-3c5180258d87'}
]
},
{
$pull: {
$and: [
{'sections._id': ObjectId('56fea43a571332cc97e06d9e')},
{'sections.registered': 'e3d65a4e-2552-4995-ac5a-3c5180258d87'}
]
}
})
I've looked in to $pull, but I can't seem to figure out how to make it work on an array of nested objects containing another array. The $pull examples all seem to deal with only one level of nesting. How do I remove the matching entry from the registered array of the item in the sections array with the _id that I supply?
You need to use the positional $ update operator to remove the element from your array. You need this is because "sections" is an array of sub-documents.
db.test.findOneAndUpdate(
{ "sections._id" : ObjectId("56fea43a571332cc97e06d9e") },
{ "$pull": { "sections.$.registered": "e3d65a4e-2552-4995-ac5a-3c5180258d87" } }
)

How to retrieve a specific field from a subdocument array with mongoose

I'm trying to get a specific field from a subdocument array
I'm not gonna include any of the fields in the parent doc
Here is the sample document
{
"_id" : ObjectId("5409dd36b71997726532012d"),
"hierarchies" : [
{
"rank" : 1,
"_id" : ObjectId("5409df85b719977265320137"),
"name" : "CTO",
"userId" : [
ObjectId("53a47a639c52c9d83a2d71db")
]
}
]
}
I would like to return the rank of the hierarchy if the a userId is in the userId array
here's what I have so far in my query
collectionName.find({{hierarchies:
{$elemMatch : {userId: ObjectId("53a47a639c52c9d83a2d71db")}}}
, "hierarchies.$.rank", function(err,data){}
so far it returns the entire object in the hierarchies array I want, but I would like to limit it to just the rank property of the object.
The projection available to .find() queries generally in MongoDB does not do this sort of projection for internal elements of an array. All you can generally do is return the "matched" element of the array entirely.
For what you want, you use the aggregation framework instead, which gives you more control over matching and projection:
Model.aggregate([
{ "$match": {
"hierarchies.userId": ObjectId("53a47a639c52c9d83a2d71db")
}},
{ "$unwind": "$hierarchies" },
{ "$match": {
"hierarchies.userId": ObjectId("53a47a639c52c9d83a2d71db")
}},
{ "$project": {
"rank": "$hierarchies.rank"
}}
],function(err,result) {
})
That basically matches the documents, filters the array content of the document to just the match and then projects only the required field.

Resources