How to prevent errors when working with procedures in recursive functions? - racket

I am trying to create a group of procedures that, together, count the frequency of word(s) in a list. The format of the output should be like the following: '(("happy" 132) ("amazing" 2) ("funny" 600) ("death" 0)).
I have made this so far:
(define count-words
(lambda (list-of-words filename)
(if (null? list-of-words)
0
(cons (tally-value (file->words filename) (car list-of-words)) (count-words (cdr list-of-words (file->words filename)))))
It works fine when only given one word in the search list, but when it attempts to search for the second word, it gives the following error:
file->words: contract violation
expected: string?
given: '("Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam" "Sam"...
I believe it is trying to convert the list in the same way it successfully converted the file, which is not possible. How can I prevent my code from rerunning this part of the function and just have it use the list it made in the first go around?

Related

Mongodb remove field from Array objects all documents?

I have a collection that has documents looks something like this:
_id : 21353456,
product : "xy",
text : "asdf",
reviews : [
{
username : "User1",
userID: 12
text : "hi",
},
{
username : "User2",
userID: 123
text : "hi1",
}
]
}
I want to remove the field in username from the objects in this array.
I tried this
db.collection.update({}, {$unset: {"reviews.username": 1}}, {multi: true})
I tried with even updateMany and it just matchedCouunt and acknowledged.
Try using the positional identifier and do something like:
db.collection.update({}, {$unset: {"reviews.$[].username":true}}, {multi:true})
Personally I prefer using the method below:
db.collection.update({"reviews.username": {$exists: true}}, {$unset: {"reviews.$.username":true}}, {multi:true})
Because sometimes you should check if it exists before you do the deleting (this way you won't get any errors).

How to update a one to many relationship MongoDB

If i have a Person collection that has many addresses, how can I insert multiple addresses that belong to a person? I know how to do it if it were a one to one relationship where I would simply do something like:
db.persons.update({_id: '12345'}, {$set: {'address': '12345 fake st'}})
However, that won't work for a one to many relationship since the person's address would get replaced every time someone adds an address to that person. Can someone help? Thank you in advance.
If you want a person to hold many addresses then you could make address an array and you could insert new addresses using the push operator.
db.persons.update(
{ "_id": "12345" },
{ "$push": { "address": "12345 fake st"}}
);
Your schema would then look like this:
{
"_id" : "12345",
"address" : [
"12345 fake st",
"6789 second st",
...
]
}
Of course you can make this as complex as required. So if you need to store extra information for each address, you can instead insert subdocuments into the address array.
{
"_id" : "12345",
"address" : [
{
"number" : "6789",
"street" : "second st",
"primary" : false
},
{
"number" : "12345",
"street" : "fake st",
"primary" : true
},
...
]
}
As noted in the comments, if you want to ensure that there are no duplicates in your array, you should use the $addToSet operator, which provides that functionality.

How to query nested document in mongodb?

I have document with nested document reviews:
{
"_id" : ObjectId("53a5753937c2f0ef6dcd9006"),
"product" : "Super Duper-o-phonic",
"price" : 11000000000,
"reviews" : [
{
"user" : "fred",
"comment" : "Great!",
"rating" : 5
},
{
"user" : "Tom",
"comment" : "Great again!",
"rating" : 5
},
{
"user" : "Tom",
"comment" : "I agree with fred somewhat",
"rating" : 4
}
]
}
I want to find only those reviews whose rating is 5.
Final query should select product price and two documents from reviews whose rating is 5.
The last query I tried is :
db.testData.find({'reviews':{$elemMatch:{'rating':{$gte:5}}}}).pretty()
It's strange but it doesn't work.
How to do this in mongodb?
If you only want a single sub-doc from reviews whose rating is 5, you can use the $ positional projection operator to include the first element of reviews that matches your query:
db.test.find({'reviews.rating': 5}, {product: 1, price: 1, 'reviews.$': 1})
If you want all reviews elements with a rating of 5 (instead of just the first) you can use aggregate instead of find:
db.test.aggregate([
// Only include docs with at least one 5 rating review
{$match: {'reviews.rating': 5}},
// Duplicate the docs, one per reviews element
{$unwind: '$reviews'},
// Only include the ones where rating = 5
{$match: {'reviews.rating': 5}},
// Only include the following fields in the output
{$project: {product: 1, price: 1, reviews: 1}}])
Take a look up here: MongoDB - how to query for a nested item inside a collection?
Just in case you thought about this:
If you try to accomplish this with $elemMatchit will jsut return the first matching review.
http://docs.mongodb.org/manual/reference/operator/projection/elemMatch/

MongoDB Why this error : can't append to array using string field name: comments

I have a DB structure like below:
{
"_id" : 1,
"comments" : [
{
"_id" : 2,
"content" : "xxx"
}
]
}
I update a new subdocument in the comments feild. It is OK.
db.test.update(
{"_id" : 1, "comments._id" : 2},
{$push : {"comments.$.comments" : {_id : 3, content:"xxx"}}}
)
after that the DB structure:
{
"_id" : 1,
"comments" : [
{
"_id" : 2,
"comments" : [
{
"id" : 3,
"content" : "xxx"
}
],
"content" : "xxx"
}
]
}
But when I update a new subdocument in the comment field that _id is 3, There is a error:
db.test.update(
{"_id" : 1, "comments.comments.id" : 3},
{$push : {"comments.comments.$.comments" : {id : 4, content:"xxx"}}}
)
error message:
can't append to array using string field name: comments
Well, it makes total sense if you think about it. MongoDb has the advantage and the disadvantage of solving magically certain things.
When you query the database for a specific regular field like this:
{ field : "value" }
The query {field:"value"} makes total sense, it wouldn't in case value is part of an array but Mongo solves it for you, so in case the structure is:
{ field : ["value", "anothervalue"] }
Mongo iterates through all of them and matches "value" into the field and you don't have to think about it. It works perfectly.. at only one level, because it's impossible to guess what you want to do if you have multiple levels
In your case the first query works because it's the case in this example:
db.test.update(
{"_id" : 1, "comments._id" : 2},
{$push : {"comments.$.comments" : {_id : 3, content:"xxx"}}}
)
Matches _id in the first level, and comments._id at the second level, it gets an array as a result but Mongo is able to solve it.
But in the second case, think what you need, let's isolate the where clause:
{"_id" : 1, "comments.comments.id" : 3},
"Give me from the main collection records with _id:1" (one doc)
"And comments which comments inside have and id=3" (array * array)
The first level is solved easily, comments.id, the second is not possible due comments returns an array, but one more level is an array of arrays and Mongo gets an array of arrays as a result and it's not possible to push a document into all the records of the array.
The solution is to narrow your where clause to obtain an unique document in comments (could be the first one) but it's not a good solution because you never know what is the position of the document you're looking for, using the shell I think the only option to be accurate is to do it in two steps. Check this query that works (not the solution anyway) but "solves" the multiple array part fixing it to the first record:
db.test.update(
{"_id" : 1, "comments.0.comments._id" : 3},
{$push : {"comments.0.comments.$.comments" : {id : 4, content:"xxx"}}}
)

mongodb: array contains and array don't contains

How do I check if list1 contains A and list2 don't contains A?
both list1 and list2 are array (list2 can be empty or not set)
I tried query:
{
'list1':'A',
'list2':{ '$ne':'A'}
}
but I'm getting results with list1 contains A and list2 contains A.
if I remove 'list2':{ '$ne':'A'} ...results is the same, likes list2 part don't metter
strange ...am I doing something wrong?
I was updating it wrong. Should be this:
db.test.update({ _id: X }, {'$push': {"a2": 'A'}});
Or this:
db.test.update({ _id: X }, {'$set': {"a2": ['A']}});
the document looks like this:
{ "_id" : ObjectId("4dbacb40696b6ede04c5ef97"), "a1" : [ "A" ], "a2" :
{ "0" : "A" } }
Where it's correct that {"0": "A"} is not equal to A