Replacing type in MongoDB - mongodb

I have a MongoDB database where I'm trying to replace all arrays of a particular element with a concatenated string. Currently, most of the documents have an array of strings. I need a script that will find each of the documents that have an array (some have been updated by hand already) and replace that array with a concatenation of what's currently there.
So far, I have:
var cursor = db.recipe.find({ directions: { $type : 4 } });
while ( cursor.hasNext() ) {
var doc = cursor.next();
db.recipe.update(
{ _id : doc._id },
{ $set : { directions : { $concat : { doc.directions } } } }
);
};
Unfortunately, it keeps complaining about an unexpected '.'. I'm assuming that I'm using $concat incorrectly.

$concat is an aggregation operator. To simply find and update, use plain javascript.
Currently, most of the documents have an array of strings. I need a script that will find each of the documents that have an array
When you apply $type:4 to an array, it in turn checks if any of the elements inside the directions field is an array, only if it finds one it returns true.
eg: for input ["x","y"] it would return false, and for [["x"],"y"] it would return true.
Since your array contains only strings, you need to use the $where operator to find the type.
db.recipe.find({$where:"Array.isArray(this.directions)"}).forEach(function(doc){
var value = doc.directions.join();
db.recipe.update({"_id":doc._id},{$set:{"directions":value}});
})

Related

findAndModify an Object Using Variable Key

I am trying to update a record which has a variable key and a pre-existing object associated with that key, as such:
[variable_name] : {
date : 'XXXX'
}
How can I use findAndModify() to insert more key-values into that structure, without removing the existing data? I could not figure it out using $set and an update object that resembled updateObj[variable_name] = { newProp : newData }.
Use the $set operator together with the dot notation to specify a new field in an embedded document. For example, since the newProp field does not exist and you specify a dotted path for that non-existent field, $set will add the new field with the specified value thus creating the embedded documents as needed to fulfill the dotted path to the field. The {upsert: true} flag creates a new document when no document matches the query criteria. The default value is false, which does not insert a new document when no match is found:
db.collection.update(
{ "variable_name.date": ISODate("2015-09-23T18:06:00.696Z") },
{ $set: { "variable_name.newProp": "zzz" } },
{ "upsert": true }
)
Too late of an answer for the question but hope it helps others.
What you can do is the following, using Template Literals and Computed Property
const key = `${variable_name}.newProp`;
db.update({_id: myId}, { $set: { [key]: newData } });

How to query nested arrays in mongodb from command line

I have some data structured like this in a mongo collection:
{ "list" : [ { "update_time" : NumberLong(1426690563), "provider" : NumberLong(4) } ] }
What I would like to do is query for all of the entries where the list includes a provider value of 4.
If it helps, all of the list arrays contain only one element.
What I am trying right now is this:
db.collection.find(
{
list: {
provider: 4
}
}
)
This does not work though, and always returns an empty set
Use the dot notation i.e. concatenate the name of the field that contains the array, with a dot (.) and the name of the field in the embedded document and use that as your query:
db.collection.find({"list.provider": 4})
You can also use the $elemMatch operator to specify multiple criteria on an array of embedded documents such that at least one embedded document satisfies all the specified criteria:
db.collection.find({
list: {
$elemMatch: {
provider: 4
}
}
})

Mongo $in query with case-insensitivity

I'm using Mongoose.js to perform an $in query, like so:
userModel.find({
'twitter_username': {
$in: friends
}
})
friends is just an array of strings. However, I'm having some case issues, and wondering if I can use Mongo's $regex functionality to make this $in query case-insensitive?
From the docs:
To include a regular expression in an $in query expression, you can
only use JavaScript regular expression objects (i.e. /pattern/ ). For
example:
{ name: { $in: [ /^acme/i, /^ack/ ] } }
One way is to create regular Expression for each Match and form the friends array.
var friends = [/^name1$/i,/^name2$/i];
or,
var friends = [/^(name1|name2)$/i]
userModel.find({"twitter_username":{$in:friends }})
Its a little tricky to do something like that
at first you should convert friends to new regex array list with:
var insesitiveFriends = [];
friends.forEach(function(item)
{
var re = new RegExp(item, "i");
insesitiveFriends.push(re);
})
then run the query
db.test.find(
{
'twitter_username':
{
$in: insesitiveFriends
}
})
I have the sample documents in test collection
/* 0 */
{
"_id" : ObjectId("5485e2111bb8a63952bc933d"),
"twitter_username" : "David"
}
/* 1 */
{
"_id" : ObjectId("5485e2111bb8a63952bc933e"),
"twitter_username" : "david"
}
and with var friends = ['DAvid','bob']; I got both documents
Sadly, mongodb tends to be pretty case-sensitive at the core. You have a couple of options:
1) Create a separate field that is a lowercase'd version of the twitter_username, and index that one instead. Your object would have twitter_username and twitter_username_lc. The non-lowerecase one you can use for display etc, but the lowercase one you index and use in your where clause etc.
This is the route I chose to go for my application.
2) Create a really ugly regex from your string of usernames in a loop prior to your find, then pass it in:
db.users.find({handle:{$regex: /^benhowdle89|^will shaver|^superman/i } })
Note that using the 'starts with' ^ carrot performs better if the field is indexed.

How can I use a $elemMatch on first level array?

Consider the following document:
{
"_id" : "ID_01",
"code" : ["001", "002", "003"],
"Others" : "544554"
}
I went through this MongoDB doc for elemmatch-query & elemmatch-projection, but not able to figure it out how to use the same for the above document.
Could anyone tell me how can I use $elemMatch for the field code?
You'll want to use the $in operator rather than $elemMatch in this case as $in can be used to search for a value (or values) inside a specific field. $in requires a list of values to be passed as an array. Additionally, and for your case, it will find either a single value, or by searching in an array of values. The entire matching document is returned.
For example, you might use it like this:
db.mycodes.find( { code: { $in: ["001"] } } )
Which could be simplified to just be:
db.mycodes.find({ code: "001" })
As MongoDB will look in an array for a single match like above ("001").
Or if you want to search for "001" or "002":
db.mycodes.find( { code: { $in: ["001", "002"] } } )
$in documentation
If you're simply looking to match all documents with an array containing a given value, you can just specify the value on the reference to that array, e.g.
db.mycodes.find( { code: '001' } )
Which thus would return you all documents that contained '001' in their code array

Can a mongo query using the $in clause, return results in the same order as in the $in list?

I am using the $in clause within a mongodb query. However, I want my results to be ordered exactly as my input list used in the $in clause.
The results I get back are in the order the objects were inserted.
All the documentation I have read so far tells me that the sort cursor function only works with document fields. Does this mean that I cannot do the above?
MongoDB does not insert in any particular order. You can sort your results though.
See:
http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24in
db.updates.ensureIndex( { ts : 1 } ); // ts == timestamp
var myFriends = myUserObject.friends; // let's assume this gives us an array of DBRef's of my friends
var latestUpdatesForMe = db.updates.find( { user : { $in : myFriends } } ).sort( { ts : -1 } ).limit(10);