How to find nodes with an object that contains a string value - mongodb

I'm struggling to create a find query that finds nodes that contain "Item1".
{
"_id" : ObjectId("589274f49bd4d562f0a15e07"),
"Value" : [["Item1", {
"Name" : "John",
"Age" : 45
}], ["Item2", {
"Address" : "123 Main St.",
"City" : "Hometown",
"State" : "ZZ"
}]]
}
In this example, "Item1" is not a key/value pair, but rather just a string that is part of an array that is part of a larger array. This is a legacy format so I can't adjust it unfortunately.
I've tried something like: { Value: {$elmemmatch:{$elemmatch:{"Item1"}}}, but that is not returning any matches. Similarly, $regex is not working since it only seems to match on string objects (and the overall object is not a string, but a string in an array in an array).

It seems like you should use the $in or $eq operator to match value.
So try this:
db.collection.find({'Value':{$elemMatch:{$elemMatch:{$in:['Item1']}}}})
Or run this to get the specific Item
db.collection.find({},{'Value':{$elemMatch:{$elemMatch:{$in:['Item1']}}}})
Hope this helps.

var data = {
"_id":"ObjectId('589274f49bd4d562f0a15e07')",
"Value":[
[
"Item1",
{
"Name":"John",
"Age":45
}
],
[
"Item2",
{
"Address":"123 Main St.",
"City":"Hometown",
"State":"ZZ"
}
]
]
}
data.Value[0][0] // 'Item1'
Copy and paste on repl it works.
There was an error on structure ofr your data

Related

Mongodb query to return field value

I am trying to construct a Mongodb query to return a field value. My JSON looks like this:
"question" : "Global_Deployment",
"displayOrder" : 1,
"answerOptions" : {
"fieldId" : "1001",
"fieldType" : "radiobutton",
"fieldName" : "Global Deployment?",
"fieldLabel" : "Global Deployment?",
"helpText" : "Help will go here",
"emailTagFormControl" : "Global_Deployment?",
"source" : "custom",
"status" : "active",
"required" : "true",
"multiSelect" : "false",
"purgeFlag" : "false",
"enableAuditTrack" : "false",
"fields" : [],
"fieldValue" : "Yes",
"options" : [
{
"optionName" : "Yes"
},
{
"optionName" : "No"
}
],
"comments" : {
"commentId" : "C1001",
"commentDetails" : []
}
My query to reach the field with the fieldName "Global Deployment" is this:
db.getCollection('requests').find({"sections.questions.answerOptions.fieldName":"Global Deployment?"})
What I want to know is what to add to this query to return the value of "fieldValue", which is on a different line in the JSON. I am new to Mongodb. Any help would be greatly appreciated.
1) If you've multiple documents in DB with "fieldName" : "Global Deployment?", then .find() would return all the matching documents i.e; in the output what you get is an array of documents then you need to iterate through the array to get answerOptions.fieldValue for each document, Check the below scenario, as I've explained there are chances of getting multiple documents if "sections.questions.answerOptions.fieldName" is not an unique field.
db.getCollection('requests').find({"sections.questions.answerOptions.fieldName":"Global Deployment?"}, {'sections.questions.answerOptions.fieldValue':1})
Output of find :
/* 1 */
[{
"_id" : ObjectId("5d4e19826e173840500f5674"),
"answerOptions" : {
"fieldValue" : "Yes"
}
},
/* 2 */
{
"_id" : ObjectId("5d4e19826e073840500f5674"),
"answerOptions" : {}
}]
If you only need documents which has fieldValue in it then do this :
db.getCollection('requests').find({"sections.questions.answerOptions.fieldName":"Global Deployment?", 'sections.questions.answerOptions.fieldValue':{$exists: true}}, {'answerOptions.fieldValue':1})
Ok now you've array of documents then do iterate thru each to retrieve your value, check this mongoDB cursor tutorial .
2) If you think fieldName is unique across collection, then you can use .findOne() , which would exactly return one document (In case if you've multiple matching documents it would return first found doc) :
db.getCollection('requests').findOne({"sections.questions.answerOptions.fieldName":"Global Deployment?"}, {'sections.questions.answerOptions.fieldValue':1})
Output of findOne :
{
"_id" : ObjectId("5d4e19826e173840500f5674"),
"answerOptions" : {
"fieldValue" : "Yes"
}
}
If you see .find({},{}) has two arguments, second one is called projection which literally be useful if you want to retrieve only required fields in the response, By default mongoDB will return the entire document what ever you've posted in the question will be retrieved, Data in mongoDB flows as JSON's so operating will be similar to using JSON's, Here you can retrieve the required fields out of result, but for best use of network efficiency if you don't need entire document you'll only get the required fields using projection.
You can specify the second condition separated by comma. Either you are trying to filter data with $and or with $or
With simple approach:
{"sections.questions.answerOptions.fieldName":"Global Deployment?","sections.questions.answerOptions.fieldValue":"Yes" }
By using $and method:
.find(
{
$and: [
{"sections.questions.answerOptions.fieldName":"Global Deployment?"},
{"sections.questions.answerOptions.fieldValue":"Yes"}
]
}
)
Same way you can use $or method. Just replace $and with $or.
Edit:
If you want to retrieve specific value (in your case fieldValue), query would be:
db.getCollection('requests').find({
"sections.questions.answerOptions.fieldName":"Global Deployment?"
}).map(function(item){
return item.fieldValue
})
The correct answer here is the method .distinct() (docs)
In your case try it like this:
db.getCollection('requests').find({"sections.questions.answerOptions.fieldName":"Global Deployment?"}).distinct('fieldValue');
That will return only the value you want.
If you use findOne you can use dot notation.
For example, if we start with creating a collection to test using the following to get close to your sample:
db.stackOverflow.insertOne({
sections: {
questions: {
question: "Global_Deployment",
displayOrder: 1,
answerOptions: {
fieldId: "1001",
fieldType: "radiobutton",
fieldName: "Global Deployment?",
fieldLabel: "Global Deployment?",
helpText: "Help will go here",
emailTagFormControl: "Global_Deployment?",
source: "custom",
status: "active",
required: "true",
multiSelect: "false",
purgeFlag: "false",
enableAuditTrack: "false",
fields: [],
fieldValue: "Yes",
options: [
{
optionName: "Yes",
},
{
optionName: "No",
},
],
comments: {
commentId: "C1001",
commentDetails: [],
},
},
},
},
})
then, this query will return "Yes".
db.stackOverflow.findOne({}).sections.questions.answerOptions.fieldValue

Append a string to the end of an existing field in MongoDB

I have a document with a field containing a very long string. I need to concatenate another string to the end of the string already contained in the field.
The way I do it now is that, from Java, I fetch the document, extract the string in the field, append the string to the end and finally update the document with the new string.
The problem: The string contained in the field is very long, which means that it takes time and resources to retrieve and work with this string in Java. Furthermore, this is an operation that is done several times per second.
My question: Is there a way to concatenate a string to an existing field, without having to fetch (db.<doc>.find()) the contents of the field first? In reality all I want is (field.contents += new_string).
I already made this work using Javascript and eval, but as I found out, MongoDB locks the database when it executes javascript, which makes the overall application even slower.
Starting Mongo 4.2, db.collection.updateMany() can accept an aggregation pipeline, finally allowing the update of a field based on its current value:
// { a: "Hello" }
db.collection.updateMany(
{},
[{ $set: { a: { $concat: [ "$a", "World" ] } } }]
)
// { a: "HelloWorld" }
The first part {} is the match query, filtering which documents to update (in this case all documents).
The second part [{ $set: { a: { $concat: [ "$a", "World" ] } } }] is the update aggregation pipeline (note the squared brackets signifying the use of an aggregation pipeline). $set (alias of $addFields) is a new aggregation operator which in this case replaces the field's value (by concatenating a itself with the suffix "World"). Note how a is modified directly based on its own value ($a).
For example (it's append to the start, the same story ):
before
{ "_id" : ObjectId("56993251e843bb7e0447829d"), "name" : "London
City", "city" : "London" }
db.airports
.find( { $text: { $search: "City" } })
.forEach(
function(e, i){
e.name='Big ' + e.name;
db.airports.save(e);
}
)
after:
{ "_id" : ObjectId("56993251e843bb7e0447829d"), "name" : "Big London
City", "city" : "London" }
Old topic but i had the same problem.
Since mongo 2.4, you can use $concat from aggregation framework.
Example
Consider these documents :
{
"_id" : ObjectId("5941003d5e785b5c0b2ac78d"),
"title" : "cov"
}
{
"_id" : ObjectId("594109b45e785b5c0b2ac97d"),
"title" : "fefe"
}
Append fefe to title field :
db.getCollection('test_append_string').aggregate(
[
{ $project: { title: { $concat: [ "$title", "fefe"] } } }
]
)
The result of aggregation will be :
{
"_id" : ObjectId("5941003d5e785b5c0b2ac78d"),
"title" : "covfefe"
}
{
"_id" : ObjectId("594109b45e785b5c0b2ac97d"),
"title" : "fefefefe"
}
You can then save the results with a bulk, see this answer for that.
this is a sample of one document i have :
{
"_id" : 1,
"s" : 1,
"ser" : 2,
"p" : "9919871172",
"d" : ISODate("2018-05-30T05:00:38.057Z"),
"per" : "10"
}
to append a string to any feild you can run a forEach loop throught all documents and then update desired field:
db.getCollection('jafar').find({}).forEach(function(el){
db.getCollection('jafar').update(
{p:el.p},
{$set:{p:'98'+el.p}})
})
This would not be possible.
One optimization you can do is create batches of updates.
i.e. fetch 10K documents, append relevant strings to each of their keys,
and then save them as single batch.
Most mongodb drivers support batch operations.
db.getCollection('<collection>').update(
// query
{},
// update
{
$set: {<field>:this.<field>+"<new string>"}
},
// options
{
"multi" : true, // update only one document
"upsert" : false // insert a new document, if no existing document match the query
});

How do I query a hash sub-object that is dynamic in mongodb?

I currently have a Question object and am not sure how to query for it?
{ "title" : "Do you eat fast food?"
"answers" : [
{
"_id" : "506b422ff42c95000e00000d",
"title" : "Yes",
"trait_score_modifiers" : {
"hungry" : 1
}
},
{
"_id" : "506b422ff42c95000e00000e",
"title" : "No",
"trait_score_modifiers" : {
"not-hungry" : -1
}
}]
}
I am trying to find questions where the trait_score_modifieres is queried (sometimes it exists, sometimes not)
I have the following but it is not dynamic:
db.questions.find({"answers.trait_score_modifiers.not-hungry":{$exists: true}})
How could i do something like this?
db.questions.find({"answers.trait_score_modifiers.{}.size":{$gt: 0}})
You should modify the schema so you have consistent key names to query on. I ran into a similar problem using the aggregation framework, see question: Total values from all keys in subdocument
Something like this should work (not tested):
{
"title" : "Do you eat fast food?"
"answers" : [
{
"title" : "Yes",
"trait_score_modifiers" : [
{"dimension": "hungry", "value": 1}
]
},
{
"title" : "No",
"trait_score_modifiers" : [
{"dimension": "not-hungry", "value": -1}
]
}]
}
You can return all questions that have a dynamic dimension (e.g. "my new dimension") with:
db.questions.find("answers.trait_score_modifiers.dimension": "my new dimension")
Or limit the returned set to questions that have a specific value on that dimension (e.g. > 0):
db.questions.find(
"answers.trait_score_modifiers": {
"$elemMatch": {
"dimension": "my new dimension",
"value": {"$gt": 0}
}
}
)
Querying nested arrays can be a bit tricky, be sure to read up on the documentation In this case, $elemMatch is needed because otherwise you return a document that has some trait_score_modifier my new dimension but the matching value is in the dimension key of a different array element.
You need $elemMatch criteria in your query.
Refer to: http://docs.mongodb.org/manual/reference/projection/elemMatch/
Let me know if you need the query.

How to do query on multiple nested data fields in MongoDB

So, what I'm trying to do is query all documents that have a City of 'Paris' and a State of 'France'. I need to do some kind of join, but I haven't been able to figure out how to construct it.
I'm using the c# driver, but I'll gladly accept help using any method.
{
"_id" : ObjectId("519b407f3c22a73a7c29269f"),
"DocumentID" : "1",
"Meta" : [{
"Name" : "City",
"Value" : "Paris",
}, {
"Name" : "State",
"Value" : "France",
}
}]
}
{
"_id" : ObjectId("519b407f3c22a73a7c29269g"),
"DocumentID" : "2",
"Meta" : [{
"Name" : "City",
"Value" : "Paris",
}, {
"Name" : "State",
"Value" : "Texas",
}
}]
}
The $elemMatch operator is used to indicate that all the conditions within it must be matched by the same array element. So (to switch to shell syntax) to match all documents which have meta city Paris you would do
db.collection.find( {Meta:{$elemMatch:{Name:"City",Value:"Paris"}}} )
This assures you won't match something which has Name: "somethingelse", Value: "Paris" somewhere in its array with a different array element matching the Name:"City".
Now, default combination for combining query conditions is "and" so you can continue adding attributes:
db.collection.find( {Meta: {
$elemMatch:{Name:"City",Value:"Paris"},
$elemMatch:{Name:"State",Value:"France"}
}
}
)
Now if you want to add another condition you keep adding it but if you want a NOT then you do it like this:
db.collection.find( {Meta: {
$elemMatch:{Name:"City",Value:"Paris"},
$elemMatch:{Name:"State",Value:"France"},
$not: {$elemMatch:{Name:"Arrondissement",Value:"Louvre"}}
}
}
)
I might be answering my own question here, but I'm new to MongoDB, so while this appears to give me the results I'm after, it might not be the optimum approach.
var result = collection.Find(
Query.And(
Query.ElemMatch("Meta", Query.EQ("Name", "City")),
Query.ElemMatch("Meta", Query.EQ("Value", "Paris")),
Query.ElemMatch("Meta", Query.EQ("Name", "State")),
Query.ElemMatch("Meta", Query.EQ("Value", "France")))
);
Which leads to a follow up - how would I get all of the documents whose 'City' is 'Paris' and 'State' is 'France' but whose 'Arrondissement' is not 'Louvre'?

MongoDB pullAll objects with multiple arguments

I just want to remove several Objects in from my array in mongoDB using pullAll
db.collection.update({'_id': ObjectId(".....")}, { $pullAll : { 'notifications' : [{'type' : type}, {'id': id}]} })
Why is this not working? What is the correct syntax?
Update:
the document is:
{
"_id" : ObjectId("......"),
"notifications" : [ { "type" : "aaa",
"id" : "123" },
{ "type" : "bbb",
"id" : "123" },
{ "type" : "ccc",
"id" : "234" }]
}
Your problem may be one of two places:
First your update has a syntax issue:
db.collection.update({'_id': ObjectId(".....")},
{ $pullAll :
{ 'notifications' : [{'type' : type}, {'id': id}]
}
}
)
should be:
db.collection.update({'_id': ObjectId(".....")},
{ $pullAll :
{ 'notifications' : [{'type' : type, 'id': id}]
}
}
)
Note I removed }, { and joined type and id into a single JSON subdocument.
The other issue is your array elements seem to have id values which are strings of form "123" - are you sure you are passing a string to your update statement? String "123" is not equal to integer 123.
To use $pullAll you need to match the whole object exactly. Why not just use $pull, I'm sure it would suit your needs