Mongo query item from subarray, having trouble - arrays

I'm relatively new to mongodb, and I came into this company's set up that had the database already set up and running. It looks to me like the structure of this "array" isn't actually a proper array. They are saving info that I need in "offer_info_array" - which then has a nested array with an "offer id" which changes a lot between records, and then nested inside that is the info that I need to select. Here is an example of a record.
{
"_id" : ObjectId("52041af3bbf8057203000004"),
"offer_info_array" : {
"128" : {
"affid" : "68",
"s1" : "YJF"
}
},
"city" : "Cleveland",
"state" : "OH",
"zip" : "44111"
}
So from a whole db of records like this one, I need to find all records that have the "affid" of "68" - I realize this database is not structured correctly, but there's not much I can do about that for records that already exist. The "128" is the offer id that varies from record to record.
If anyone has any insight and can help me out with this, I would greatly appreciate it. Thank you!

You can use $where operator which accepts JavaScript function:
db.items.find({$where: function() {
for(var key in obj.offer_info_array)
if(obj.offer_info_array[key].affid == 68)
return true;
return false; }})
This function looks for properties of offer_info_array object and gets value of property by key. Then we verify if property value has affid property equal to 68. If yes, we return true which means objects matches our query. If there is no properties with affid equal to 68, we return false.
Keep in mind, that $where operator do not use indexes.

Related

MongoDB/PyMongo find_one_and_update and/or find() method doesn't find specified entry, just appends to the end of the collection

I'm attempting to search in an array in my collection, to find by a key named "id". Both the find() method and find_one_and_update() don't return the entry I'm trying to find. The collection is structured like so:
"_id" : {stuff},
"events":[{
"id":"12345",
"date":"01/01"
}, {
"id":"12346",
"date":"02/02"
}]
Trying find() as:
result = db.get_collection("events").find({"events.id" : "12345"})
for item in result:
print(item)
Prints out the entire collection.
Trying to update a specific entry by its id like so will append it to the end.
db.get_collection("events").find_one_and_update({"events.id" : "12345"},{"$set" : {"date" : "somedate"}})
Looks like this afterwards:
"id":"12346",
"date":"02/02"
}], "date" : "somedate"
So, what am I doing wrong with updating and finding here? Every other person seems to have no trouble with this part.
Figured this out on my own, needed to specify which object + field to update in the collection:
db.get_collection("events").find_one_and_update({"events.id" : "12345"},{"$set" : {"events.$.date" : "somedate"}})

Mongodb query to find element value type of nested array or object in a field

I have mongodb data model where I have some array fields that contain embedded objects or arrays. I have some inconsistencies in the field in question because I've tweaked my application logic. Initially, my model looked like this:
Initial Setup of Results collection
"competition" : "competition1",
"stats" : [
{
"stat1" : [],
"stat2" : []
}
]
However, I saw that this wasn't the best setup for my needs. So I changed it to the following:
New Setup of Results collection
"competition" : "competition1",
"stats" : [
{
"stat1" : 3,
"stat2" : 2
}
]
My problem now is that documents that have the initial setup cause an error. So what I want is to find all documents that have the initial setup and convert them to have the new setup.
How can I accomplish this in mongodb?
Here is what I've tried, but I'm stuck...
db.getCollection('results').find({"stats.0": { "$exists": true }})
But what I want is to be able to do something like
db.getCollection('results').find({"stats.0".stat1: { "$type": Array}})
Basically I want to get documents where the value of stats[0].stat1 is of type array and override the entire stats field to be an empty array.
This would fix the errors I'm getting.
$type operator for arrays in older versions works little differently than what you might think than $type in 3.6.
This will work in 3.6
db.getCollection('results').find( { "stats.0.stat1" : { $type: "array" } } )
You can do it couple of ways for lower versions and It depends what you are looking for.
For empty arrays you can just check
{"stats.0.stat1":{$size:0}}
For non empty arrays
{"stats.0.stat1": {$elemMatch:{ "$exists": true }}}
Combine both using $or for finding both empty and non empty array.
For your use case you can use below update
db.getCollection('results').update({"stats.0.stat1":{$size:0}}, {$set:{"stats":[]}})

Can I check if a value is only pushed if a certain field value is not filled already?

I am trying to make a Meteor app to let users push a value to the database. It works ok, but there a small issue. As soon a certain user has pushed his information, i don't want to let the same user create another entry. Or this must be blocked, or the value the user is pushing must be overwritten for the value he is posting the second time. Now I get multiple entry's of the same user.
Here is my code. Hope you can help me here. Thanks in advance.
Estimations.update(userstory._id, {
$addToSet: {
estimations: [
{name: Meteor.user().username, estimation: this.value}
]
}
});
From the mongo docs
The $addToSet operator adds a value to an array unless the value is
already present, in which case $addToSet does nothing to that array.
Since your array elements are objects the value is the entire object, not just the username key. This means a single user can create multiple name, estimation pairs as long as the estimation value is different.
What you can do is remove any value for the user first, then reinsert:
var username = Meteor.user().username;
Estimations.update({ userstory._id },
{ $pull: { estimations: { name: username }}}); // if it doesn't exist this will no-op
Estimations.update({userstory._id },
{ $push: { estimations: { name: username, estimation: this.value }}});
By way of commentary, you've got a collection called Estimations that contains an array called estimations that contains objects with keys estimation. This might confuse future developers on the project ;) Also if your Estimations collection is 1:1 with UserStorys then perhaps the array could just be a key inside the UserStory document?

Return only one element from strings array in elasticsearch

I have array of strings in one field "strArray":
strArray: ['browser:IE', 'device:PC', 'country:USA', 'state:CA']
I need do aggregations by browser (device, country or state). It's not a problem, if I know order of these values in strArray field.
I could to use those structure:
"aggs": {
"deviceAggs": {
"terms": {
"script": "doc['strArray'][1]"
}
}
}
But problem is that order of inserting these strings can be different.
How can I do this ? I think about several ways:
Scripting - use function like as substring and get only "correct" values.
Filtering - it's possible to filter one value (which contains string "device:") from array.
Sorting strArray values to put all values in definite order, but "sort" give me strange result - return only one element (without any filtering).
Don't ask me, why I have this structure (this is not my choice), if we have structure key: value - we would not have problems.
Scripting is only directly possible here.
To get an idea on how to use scripting in aggregations, you can refer this blog.
Something like below should work
for(element in doc['strArray'].values){
if(element.startsWith('browser')){
return element;
}
};
return null;
Both sorting and filtering is done on document level and not element level.
On element level if you can make this array as nested , filtering is possible. That is first you need to change the structure to -
strArray: [
{ "name" : 'browser:IE' } ,
{ "name" : 'device:PC' }
]
And then make the strArray field as nested.
In that case you can do a nested filter based on prefix query ( Using query filter ) and then , do a nested aggregation on the data.

Referencing arrays that are nested within multiple objects within MongoDB with Express js

So in my MongoDB Collection I have this structure:
"_id" : "Object("-----------")
"name" : "John Doe"
"tool" : {
"hammer" : {
"name" : "hammer 1",
"characteristics" : [
{
"length" : "9 inches"
},
{
"weight" : "4 pounds"
}
]
I know the data may seem a little strange but I can't put the actual data online so I had to input some dummy data. So essentially what I would like to do is be able to update the array that is nested within those objects. So I would like to be able to update the weight or add a new characteristic that I haven't previously entered into it. So for example, add in "metal" : "steel" as a new entry into the array. Currently I'm using a Rest API built in Node.js and Express.js to edit the db. When I was trying to figure out how to dig down this deep I was able to do it with an array at the highest level, however I haven't been able to figure out how to access an array when its embedded like this. So what I was wondering if anybody knew if it was even possible to edit an array this far down? I can post code from controller.js and server.js file if needed but I figured I'd see if it's even possible to do before I start posting it. Any help would be greatly appreciated!
You can use findAndModify to $push it into the array. You have to specify the path precisely though:
db.tools.findAndModify( {
query: { name: "John Doe"},
update: { $push: { tool.hammer.characteristics: {metal: "steel"} }
} );

Resources