Update document in array MongoDB - node.js

I know this question has been asked in various ways before but I've read them all and I can't get it to work with the below:
Here is a doc example:
"_id" : ObjectId("583659c5be5f0e6f70c95633"),
"firstName" : "da",
"lastName" : "ksk",
"email" : "papap#aol.com",
"surveyResults" : [
{
"mouseTracking" : [ ],
"result" : "White",
"question" : "What color website do you prefer?",
"questionNumber" : "0"
},
{
"mouseTracking" : [ ],
"result" : "Laptop",
"question" : "What device do you use most to browse the web?",
"questionNumber" : "1"
}]
Here is what I am trying to run in mongoose and having no luck:
db.results.update({_id:userIdVar, surveyResults: {$elemMatch: {questionNumber: questionNumVar}}},{$set:{"surveyResults.$.result" : newResultVar}});
I have variables being used and I know they have the proper values to match what is in the places I need to update. Is there something I am missing??

I added a few levels of console logging throughout the conditionals and before and after the update and saw that all of the paths were being traveled. Then I added the line:
mongoose.set('debug', true);
This logs all mongoose interactions with the db. I saw that my command I pasted wasn't even running! Turns out I accidentally left off the
.exec()
at the end so it wasn't executing the command. After a hours of checking and triple checking my syntax, variable types, it was just this.
I did in the process of this learn more about the $ operator and you should read up on it if you don't know it's place in mongoose/mongodb

As far I guess type miss match to element match. In your data example questionNumber is string so if you compare with integer type questionNumVar then it should not work. so to compare need to convert to string (if integer) otherwise should be working.
can try like:
db.results.update({_id:userIdVar, surveyResults: {$elemMatch: {questionNumber: questionNumVar.toString()}}},{$set:{"surveyResults.$.result" : newResultVar}}).exec();
then it should be working.

Related

how to create a new collection in $out in mongodb based on field names?

I am trying to create a new collection based on field names of document in $out.
I have tried bellow command but didn't work.
{ $out: "$fieldName" }
output is: MongoServerError: PlanExecutor error during aggregation :: caused by :: error with target namespace: Invalid collection name: $fieldName.
one of my documents is like the bellow:
{
"_id" : {
"age" : "24",
"gender" : "female"
},
"fieldName" : "engineer",
"name" : "",
"value" : NumberInt(1)
}
I don't think you can do this.
The $out stage specifies a single place to direct the output of the entire pipeline to. There is no guarantee that the field names would all specify the same value (and my guess is that you expect that they don't). We can see in the documentation that the coll (and db) parameters take string values as input rather than expressions which resolve to strings.
If I attempt to run the command in the comments it fails with a warning about the wrong type:
test> db.version()
6.0.1
test> db.foo.aggregate([{ $out: {db: "db_name", coll: { $getField: "x" }} }])
MongoServerError: wrong type for field (coll) object != string
Interestingly, the playground actually doesn't fail. But it looks like that's because it is effectively not evaluating/executing the $out. I can put in a random parameter name for the stage and it still "works", whereas it normally validates all of the parameters. So I think that the playground here is actually misleading.

Updating multiple documents with mongoose not working

I have a Node/Express API using Mongoose, where I am trying to update subdocuments within multiple documents.
Essentially I have a user with a profile that has multiple phone numbers that they can share with another contact.
Each contact has a profile.contacts[] section, that has profile.contacts.phones[] section.
When the main user sharing his/her phone number with other users changes his/her number, I want to update the profile.contacts.phones[] section in all documents, where the _id of that phone number matches.
Here is my code:
Profile.update({'contacts.phones._id':req.body._id}, {
Profile.contacts.phones.phone_number:req,body.phone_number,
Profile.contacts.phones.phone_type:req.body.phone_type
}, {multi:true}, function(err, result){
if(err)
res.send(err)
res.json(result);
})
Here is a sample "Profile" document:
{
"_id" : ObjectId("59c09dca981de33d180df943"),
"last_name" : "Sam",
"first_name" : "Sam",
"owner_id" : "59c09dca981de33d180df940",
"contacts" : [
{
"first_name" : "Joe",
"last_name" : "Public",
"_id" : ObjectId("59c09dca981de33d180df944"),
"phones" : [
{
"phone_number" : "2067155803",
"phone_type" : "home",
"_id" : ObjectId("59bca0b55481370985cac29a")
}
]
}
]
"__v" : 0
}
Based on what I see in the documentation, this should work...
Thanks for any insight!!
I think you would need to use the positional operator to update an object in an array $ that matched the query.
However, this cannot be used for nested arrays:
The positional $ operator cannot be used for queries which traverse more than one array, such as queries that traverse arrays nested within other arrays, because the replacement for the $ placeholder is a single value.
https://docs.mongodb.com/manual/reference/operator/update/positional/
You might have to consider restructuring your documents.

Storing a complex Query within MongoDb Document [duplicate]

This is the case: A webshop in which I want to configure which items should be listed in the sjop based on a set of parameters.
I want this to be configurable, because that allows me to experiment with different parameters also change their values easily.
I have a Product collection that I want to query based on multiple parameters.
A couple of these are found here:
within product:
"delivery" : {
"maximum_delivery_days" : 30,
"average_delivery_days" : 10,
"source" : 1,
"filling_rate" : 85,
"stock" : 0
}
but also other parameters exist.
An example of such query to decide whether or not to include a product could be:
"$or" : [
{
"delivery.stock" : 1
},
{
"$or" : [
{
"$and" : [
{
"delivery.maximum_delivery_days" : {
"$lt" : 60
}
},
{
"delivery.filling_rate" : {
"$gt" : 90
}
}
]
},
{
"$and" : [
{
"delivery.maximum_delivery_days" : {
"$lt" : 40
}
},
{
"delivery.filling_rate" : {
"$gt" : 80
}
}
]
},
{
"$and" : [
{
"delivery.delivery_days" : {
"$lt" : 25
}
},
{
"delivery.filling_rate" : {
"$gt" : 70
}
}
]
}
]
}
]
Now to make this configurable, I need to be able to handle boolean logic, parameters and values.
So, I got the idea, since such query itself is JSON, to store it in Mongo and have my Java app retrieve it.
Next thing is using it in the filter (e.g. find, or whatever) and work on the corresponding selection of products.
The advantage of this approach is that I can actually analyse the data and the effectiveness of the query outside of my program.
I would store it by name in the database. E.g.
{
"name": "query1",
"query": { the thing printed above starting with "$or"... }
}
using:
db.queries.insert({
"name" : "query1",
"query": { the thing printed above starting with "$or"... }
})
Which results in:
2016-03-27T14:43:37.265+0200 E QUERY Error: field names cannot start with $ [$or]
at Error (<anonymous>)
at DBCollection._validateForStorage (src/mongo/shell/collection.js:161:19)
at DBCollection._validateForStorage (src/mongo/shell/collection.js:165:18)
at insert (src/mongo/shell/bulk_api.js:646:20)
at DBCollection.insert (src/mongo/shell/collection.js:243:18)
at (shell):1:12 at src/mongo/shell/collection.js:161
But I CAN STORE it using Robomongo, but not always. Obviously I am doing something wrong. But I have NO IDEA what it is.
If it fails, and I create a brand new collection and try again, it succeeds. Weird stuff that goes beyond what I can comprehend.
But when I try updating values in the "query", changes are not going through. Never. Not even sometimes.
I can however create a new object and discard the previous one. So, the workaround is there.
db.queries.update(
{"name": "query1"},
{"$set": {
... update goes here ...
}
}
)
doing this results in:
WriteResult({
"nMatched" : 0,
"nUpserted" : 0,
"nModified" : 0,
"writeError" : {
"code" : 52,
"errmsg" : "The dollar ($) prefixed field '$or' in 'action.$or' is not valid for storage."
}
})
seems pretty close to the other message above.
Needles to say, I am pretty clueless about what is going on here, so I hope some of the wizzards here are able to shed some light on the matter
I think the error message contains the important info you need to consider:
QUERY Error: field names cannot start with $
Since you are trying to store a query (or part of one) in a document, you'll end up with attribute names that contain mongo operator keywords (such as $or, $ne, $gt). The mongo documentation actually references this exact scenario - emphasis added
Field names cannot contain dots (i.e. .) or null characters, and they must not start with a dollar sign (i.e. $)...
I wouldn't trust 3rd party applications such as Robomongo in these instances. I suggest debugging/testing this issue directly in the mongo shell.
My suggestion would be to store an escaped version of the query in your document as to not interfere with reserved operator keywords. You can use the available JSON.stringify(my_obj); to encode your partial query into a string and then parse/decode it when you choose to retrieve it later on: JSON.parse(escaped_query_string_from_db)
Your approach of storing the query as a JSON object in MongoDB is not viable.
You could potentially store your query logic and fields in MongoDB, but you have to have an external app build the query with the proper MongoDB syntax.
MongoDB queries contain operators, and some of those have special characters in them.
There are rules for mongoDB filed names. These rules do not allow for special characters.
Look here: https://docs.mongodb.org/manual/reference/limits/#Restrictions-on-Field-Names
The probable reason you can sometimes successfully create the doc using Robomongo is because Robomongo is transforming your query into a string and properly escaping the special characters as it sends it to MongoDB.
This also explains why your attempt to update them never works. You tried to create a document, but instead created something that is a string object, so your update conditions are probably not retrieving any docs.
I see two problems with your approach.
In following query
db.queries.insert({
"name" : "query1",
"query": { the thing printed above starting with "$or"... }
})
a valid JSON expects key, value pair. here in "query" you are storing an object without a key. You have two options. either store query as text or create another key inside curly braces.
Second problem is, you are storing query values without wrapping in quotes. All string values must be wrapped in quotes.
so your final document should appear as
db.queries.insert({
"name" : "query1",
"query": 'the thing printed above starting with "$or"... '
})
Now try, it should work.
Obviously my attempt to store a query in mongo the way I did was foolish as became clear from the answers from both #bigdatakid and #lix. So what I finally did was this: I altered the naming of the fields to comply to the mongo requirements.
E.g. instead of $or I used _$or etc. and instead of using a . inside the name I used a #. Both of which I am replacing in my Java code.
This way I can still easily try and test the queries outside of my program. In my Java program I just change the names and use the query. Using just 2 lines of code. It simply works now. Thanks guys for the suggestions you made.
String documentAsString = query.toJson().replaceAll("_\\$", "\\$").replaceAll("#", ".");
Object q = JSON.parse(documentAsString);

Marklogic|NodeJS API - Query on a specific categorie "properties"

I have a json document in my DB that looks like this :
{
"uri" : "/me/myself/and/bd1e0f91656bfc713eb6560eeaad7ad1.json",
"category" : "content",
"format" : "json",
"versionId" : "14697362595356370",
"contentType" : "application/json",
"contentLength" : "1938",
"collections" : ["http://me.myself.com/collectionA"],
"properties" : {
"relatives" : ["/me/myself/and/B.json", "/me/myself/and/A.json"]
},
"content":{}
}
I'm trying to get all documents that have a specific relative in the properties:
qb.where(
qb.scope(
qb.property('relatives'),
qb.word("/me/myself/and/B.json"),
qb.fragmentScope('properties')
))
But i keep getting a large set of document that doesn't fit the query.
Any idea how to do this using the Marklogic NodeJS API?
I see two things that look like they might be problems. The first is qb.fragmentScope('properties'). This tells MarkLogic to look in the document's properties, rather than the document's content. That doesn't look like what you meant, given your sample JSON document.
The second problem is the word query -- "/me/myself/and/B.json" is likely being broken up into its constituent words (me, myself, and, B, json), which are then matching in other documents. You want to match exactly what's there, so try a value query:
qb.where(
qb.scope(
qb.properties('properties'),
qb.value('relatives', '/me/myself/and/B.json')
)
)
Note that the qb.scope and the qb.properties are to restrict the search to just match the value when it appears in relatives under a properties JSON property. This is different from the JSON property-versus-content point made above.
qb.where(
qb.propertiesFragment(
qb.term('/me/myself/and/B.json')
)
)
This worked for me.

How do I create a partial search filter (/^) in mongoDB + mongoose combination?

I have a dataset which looks like this:
{ "id" : "1.2.1", "name" : "abc1" }
{ "id" : "1.2.3", "name" : "abc2" }
{ "id" : "1.2.2", "name" : "abc3" }
{ "id" : "1.2.1", "name" : "abc4" }
{ "id" : "1.2.4", "name" : "abc5" }
I want to get all records that starts with id = 1.2. So in shell I can happily use:
db.collection.find({id : /^1.2/})
And it works fine. But now from node.js / mongoose when I try to issue the same find, I want this 1.2 to be a variable.
My problem is I can't do something like
var q = {"id" : \/\^ + id + "/"};
Get illegal token error. This may be as simple as JS escaping technique that am not aware of.
Any help would be highly appreciated and save mongodb from multiple queries :)
While this is more a generic mongoDB find question, if someone can hint me towards how I can use mongoose for like operator, that would be great.
By the way, I don't want to use regEx here because of performance consideration.
Thanks in advance ...
You do realize that the query you are doing is a regex operation from your first example. But it seems you are having a problem of how to translate this into something dynamic. This is where the $regex operator syntax comes in:
var id = "1.2";
db.collection.find({ "_id": { "$regex": "^" + id } })
That is generally how you approach using the operator outside of the mongo shell.
That catch with using a regex is when you do not anchor to the beginning of a field like you have then you are scanning a full index or possibly even the collection.
When you do anchor, the index can be bound by a lexical match to restrict what is scanned. In your case here it will be the lexical bounds of "1.2" to "1.3".

Resources