To find first word of a sentence in mongodb of specified size - mongodb

I want to find the first word from a sentence(phrase) whose size is less than 3 letters.Is there any way i could find it? please suggest.
I have used
.map(function(st){return st.split(" ")[0];}
function it gives me all the first words in array format.But this is not the expected output.
{ "name" : "VAS LAYER BREED FARM PRIVATE LIMITED" }
{ "name" : "UTTARA BROILER BREED FARM PRIVATE LTD" }
{ "name" : "SAI REKHA POULTRY PRIVATE LTD" }
{ "name" : "RUHITECH NUTRITION PRIVATE LTD" }
{ "name" : "SADKAR BROILER AND AGRO FARMS PRIVATE LTD" }
{ "name" : "SADAR POULTRY PRIVATE LTD" }
From this list i need the output to print only the words: ("SAI","VAS") in the output.

You may perform aggregation query.
db.collection.aggregate([
{
$project: {
name: {
$let: {
vars: {
first: {
$arrayElemAt: [
{
$split: [
"$name",
" "
]
},
0
]
}
},
in: {
$cond: [
{
$lte: [
{
$strLenCP: "$$first"
},
3
]
},
"$$first",
""
]
}
}
}
}
},
{
$match: {
name: {
$ne: ""
}
}
},
{
$group: {
_id: null,
name: {
$push: "$name"
}
}
}
])
MongoPlayground

By what I understood from your question may be you are looking for this:
INPUT
var arr = [{ "name" : "VAS LAYER BREEDER FARM PRIVATE LIMITED" },
{ "name" : "UTTARA BROILER BREEDER FARM PRIVATE LIMITED" },
{ "name" : "SAI REKHA POULTRY FARMS PRIVATE LIMITED" }
]
CODE
var outputArr = [];
for(let j=0; j<arr.length; j++){
var chars = arr[j].name.split(' ');
for(let i=0; i<chars.length; i++){
if(chars[i].length <= 3){
outputArr.push(chars[i]);
break;
}
};
}
OUTPUT
outputArr [ 'VAS', 'SAI' ]

The following mongo shell query will print all the words which are less than three characters.
db.test.find().forEach( doc => {
let words = doc.name.split(" ");
for ( let word of words ) {
if ( word.length < 3 ) {
print( word );
break;
}
}
} )
The query will print the character "&".
If you want the query to print words less than or equal to three characters, change the code word.length < 3 to word.length <= 3. This will print "&","SAI" and "VAS".
If you want the query print only words with alphabets (A to Z and a to z), change ( word.length < 3 ) to ( word.length <= 3 && word.match("^[A-Za-z]+$") ). This will print "SAI" and "VAS".

If you want to manage the same with javascript (as your sample code suggests)
// Your question suggests you need words less than 3 length, so you can write
.map((st)=> st.split(" ")[0]).filter((str)=> str.length < 3)
// Your expected output suggests you need words less than or equal to 3 length, so you can write
.map((st)=> st.split(" ")[0]).filter((str)=> str.length <= 3)

Related

how can I modify a field name / key in a nested array of objects in mongodb?

I have a mongodb collection with a number of objects like this:
{
"_id" : "1234",
"type" : "automatic",
"subtypes" : [
{
"_id" : "dfgd",
"name" : "test subtype",
"subjetRequired" : true,
},
{
"_id" : "dfgd",
"name" : "test subtype2",
"subjetRequired" : false,
}
],
"anotherField" : "some value"
}
As you can see, one of the keys in the subtypes array is incorrectly spelled - "subjetRequired" instead of "subjectRequired".
I want to correct that key name. How can I do that.
I'll preface this by saying I've not worked with mongodb very much in the past.
After a lot of researching, the best I could come up with is the following (which doesn't work):
function remap(doc) {
subtypes = doc.subtypes;
var count = 0;
subtypes.forEach(function(subtype){
db.taskType.update({"_id": subtype._id}, {
$set: {"subtypes.subjectRequired" : subtype.subjetRequired},
$unset: {"subtypes.subjetRequired": 1}
});
}
)
}
db.taskType.find({"subtypes.subjetRequired":{$ne:null}}).forEach(remap);
This doesn't work.
I know the loop is correct, as if I replace the other logic with print statements I can access and print the fields who's names I want to modify.
What am I doing wrong here?
You can use this update and avoid using any code, it's also stable so you can execute it multiple times with no fear.
db.collection.updateMany({
"subtypes.subjetRequired": {
$exists: true
}
},
[
{
$set: {
subtypes: {
$map: {
input: "$subtypes",
in: {
$mergeObjects: [
"$$this",
{
subjectRequired: "$$this.subjetRequired",
}
]
}
}
}
}
},
{
$unset: "subtypes.subjetRequired"
}
])
Mongo Playground
I could modify your loop to override the whole array of subtypes:
function remap(doc) {
correctSubtypes = doc.subtypes.map(({ subjetRequired, ...rest }) => ({
...rest,
subjectRequired: subjetRequired,
}));
var count = 0;
db.taskType.findByIdAndUpdate(doc._id, {
$set: {
subtypes: correctSubtypes,
},
});
}

How to split string by more than one char in mongoDB

I'm trying to split this string so i will be able to count how many words of the same length he contains with map reduce later.
For example, for the sentence
SUPPOSING that Truth is a woman--what then?
I will get -
[
{length:”1”, number:”1”},
{length:”2”, number:”1”},
{length:”4”, number:”3”},
{length:”5”, number:”2”},
{length:”9”, number:”1”}
]
How can i do this?
The answer to your question depends very much on your definition of what a word is. If it is a consecutive sequence of A-Z or a-z characters only then here is a completely nuts approach which, however, gives you the exact result you're asking for.
What this code does is effectively
Parse an input string to eliminate non-matching characters (so anything that is not either A-Z or a-z).
Concatenate the resulting cleansed string which will only hold valid characters.
Split the resulting string by the space character.
Calculate the lenght of all found words.
Group by lenght and count instances.
Some beautification of the output.
Given the following input document
{
"text" : "SUPPOSING that Truth is a woman--what then?"
}
the following pipeline
db.collection.aggregate({
$project: { // lots of magic to calulate an array that will hold the lengths of all words
"lengths": {
$map: { // translate a given word into its length
input: {
$split: [ // split cleansed string by space character
{ $reduce: { // join the characters that are between A and z
input: {
$map: { // to traverse the original input string character by character
input: {
$range: [ 0, { $strLenCP: "$text" } ] // we wamt to traverse the entire string from index 0 all the way until the last character
},
as: "index",
in: {
$let: {
vars: {
"char": { // temp. result which will be reused several times below
$substrCP: [ "$text", "$$index", 1 ] // the single character we look at in this loop
}
},
in: {
$cond: [ // some value that depends on whether the character we look at is between 'A' and 'z'
{ $and: [
{ $eq: [ { $cmp: [ "$$char", "#" /* ASCII 64, 65 would be 'A' */] }, 1 ] }, // is our character greater than or equal to 'A'
{ $eq: [ { $cmp: [ "$$char", "{" /* ASCII 123, 122 would be 'z' */] }, -1 ] } // is our character less than or equal to 'z'
]},
'$$char', // in which case that character will be taken
' ' // and otherwise a space character to add a word boundary
]
}
}
}
}
},
initialValue: "", // starting with an empty string
in: {
$concat: [ // we join all array values by means of concatenating
"$$value", // the current value with
"$$this"
]
}
}
},
" "
]
},
as: "word",
in: {
$strLenCP: "$$word" // we map a word into its length, e.g. "the" --> 3
}
}
}
}
}, {
$unwind: "$lengths" // flatten the array which holds all our word lengths
}, {
$group: {
_id : "$lengths", // group by the length of our words
"number": { $sum: 1 } // count number of documents per group
}
}, {
$match: {
"_id": { $ne: 0 } // $split might leave us with strings of length 0 which we do not want in the result
}
}, {
$project: {
"_id": 0, // remove the "_id" field
"length" : "$_id", // length is our group key
"number" : "$number" // and this is the number of findings
}
}, {
$sort: { "length": 1 } // sort by length ascending
})
will produce the desired output
[
{ "length" : 1, "number" : 1.0 },
{ "length" : 2, "number" : 1.0 },
{ "length" : 4, "number" : 3.0 },
{ "length" : 5, "number" : 2.0 },
{ "length" : 9, "number" : 1.0 }
]
This sample aggregation will count words of the same length. Hope it will help you:
db.some.remove({})
db.some.save({str:"red brown fox jumped over the hil"})
var res = db.some.aggregate(
[
{ $project : { word : { $split: ["$str", " "] }} },
{ $unwind : "$word" },
{ $project : { len : { $strLenCP: "$word" }} },
{ $group : { _id : { len : "$len"}, same: {$push:"$len"}}},
{ $project : { len : "$len", count : {$size : "$same"} }}
]
)
printjson(res.toArray());

How to add key to $addToSet in mongoDB

I want to add a key inside mongodb add function. I am doing this right now.
$addToSet : {
"msges":{
time:{"from":uname,"title":title,"msg":msg,"read":false}
}
}
time is a variable that is coming from the paramater. It has time inside it as hh:mm:ss A. But when the query runs, instead of time as key, string "time" gets print as key. Any ideas what should I do?
Enclose your variable in [] :
$addToSet: {
"msges": {
[time]: { "from": uname, "title": title, "msg": msg, "read": false }
}
}
For instance :
var myfield = "custom_field";
db.test.update({
_id: 1
}, {
$addToSet: {
letters: [{
[myfield]: 1
}, {
[myfield]: 2
}]
}
})
It gives :
{ "_id" : 1, "letters" : [ [ { "custom_field" : 1 }, { "custom_field" : 2 } ] ] }

wrong result in MongoDB mapreduce function?

I have Collection "cars" from that want to get count of certified cars as trueCount and flaseCount where certified is boolean.
am issuing the following mapreduce query
map:-
function() { for (var idx = 0; idx < this.cars.length; idx++) {
var key = this.cars[idx].carName;
var value = {
count : 1,
certifiedCheck : this.cars[idx].certified
};
emit(key, value);
} }
reduce:-
function(key, values) {
certifiedCount = { trueCount: 0, falseCount: 0 };
values.forEach(function(value) {
if ( value.certifiedCheck )
certifiedCount.trueCount += value.count;
else
certifiedCount.falseCount += value.count;
});
return certifiedCount;
query:
{ "type": "cars" }
getting the following result :
{ "id" : "carName" , "value" : { "true" : 277.0 , "false" : NaN}};
even though I have 457 documents in the collection.
Please someone help me here to fix this issue.
Thanks in advance
You mixed up your map-reduce: to reduce to two keys "true" and "false" you need to emit these as keys. Then, the reducer will run per key.
As pseudo code:
map:
for each car
evaluate whether it should be true or false
key = (true/false)
emit(key, { count : 1 })
reduce:
(input is true/false as key, array of count-documents as value)
for each value-document
sum up the count
return key, sum
This should yields two documents with true / false as key and the respective sum as value.
You should consider using the aggregation framework for running the aggregation since it achieves the same result albeit faster than MapReduce as aggregation runs natively in the server (C++), MapReduce spawns separate javascript thread(s) to run JS code.
Thus said, if you run the following aggregation pipeline which uses the $cond operator to evaluate the counts based on the logic in the field expression, you will get a similar result:
Because you haven't showed your collection schema, I've assumed the following sample documents with a cars field as array having seen in your mapReduce you are doing a for loop on the cars property:
Populate test collection
db.collection.insert([
{ _id: 1, cars: [ { model: "A", certified: true }, { model: "B", certified: true } ] },
{ _id: 2, cars: [ { model: "A", certified: false }, { model: "B", certified: true } ] },
{ _id: 3, cars: [ { model: "A", certified: true }, { model: "B", certified: false } ] },
{ _id: 4, cars: [ { model: "A", certified: true }, { model: "B", certified: false } ] },
{ _id: 5, cars: [ { model: "A", certified: true }, { model: "B", certified: true } ] }
])
Run aggregation operation
db.collection.aggregate([
{ "$unwind": "$cars" },
{
"$group": {
"_id": "$cars.model",
"trueCount": {
"$sum": {
"$cond": [ "$cars.certified", 1, 0 ]
}
},
"falseCount": {
"$sum": {
"$cond": [ "$cars.certified", 0, 1 ]
}
}
}
}
])
Result:
/* 1 */
{
"_id" : "A",
"trueCount" : 4,
"falseCount" : 1
}
/* 2 */
{
"_id" : "B",
"trueCount" : 3,
"falseCount" : 2
}

Mongodb: find documents with array field that contains more than one SAME specified value

There is three documents in collection test:
// document 1
{
"id": 1,
"score": [3,2,5,4,5]
}
// document 2
{
"id": 2,
"score": [5,5]
}
// document 3
{
"id": 3,
"score": [5,3,3]
}
I want to fetch documents that score field contains [5,5].
query:
db.test.find( {"score": {"$all": [5,5]}} )
will return document 1, 2 and 3, but I only want to fetch document 1 and 2.
How can I do this?
After reading your problem I personally think mongodb not supported yet this kind of query. If any one knows about how to find this using mongo query they defiantly post answers here.
But I think this will possible using mongo forEach method, so below code will match your criteria
db.collectionName.find().forEach(function(myDoc) {
var scoreCounts = {};
var arr = myDoc.score;
for (var i = 0; i < arr.length; i++) {
var num = arr[i];
scoreCounts[num] = scoreCounts[num] ? scoreCounts[num] + 1 : 1;
}
if (scoreCounts[5] >= 2) { //scoreCounts[5] this find occurrence of 5
printjsononeline(myDoc);
}
});
Changed in version 2.6.
The $all is equivalent to an $and operation of the specified values; i.e. the following statement:
{ tags: { $all: [ "ssl" , "security" ] } }
is equivalent to:
{ $and: [ { tags: "ssl" }, { tags: "security" } ] }
I think you need to pass in a nested array -
So try
db.test.find( {"score": {"$all": [[5,5]]}} )
Source
Changed in version 2.6.
When passed an array of a nested array (e.g. [ [ "A" ] ] ), $all can now match documents where the field contains the nested array as an element (e.g. field: [ [ "A" ], ... ]), or the field equals the nested array (e.g. field: [ "A" ]).
http://docs.mongodb.org/manual/reference/operator/query/all/
You can do it with an aggregation. The first step can use an index on { "score" : 1 } but the rest is hard work.
db.test.aggregate([
{ "$match" : { "score" : 5 } },
{ "$unwind" : "$score" },
{ "$match" : { "score" : 5 } },
{ "$group" : { "_id" : "$_id", "sz" : { "$sum" : 1 } } }, // use $first here to include other fields in the results
{ "$match" : { "sz" : { "$gte" : 2 } } }
])