Editing nested values in mongodb - mongodb

This is my structure and i want to, given both an user id and an item id (the first element in each entry inside the havelist - 5d2807a1eba04543f914d9da in the first entry), change the last value in the array (1 in the first case, 2 in the second case) by either increasing it in one or decreasing it in one.
these fields have no name, so when I browse to them, they are referred to as [0],[1],... [7] (the last one, the one i want to edit)
{
"_id" : "david",
"havelist" : [
[
"5d2807a1eba04543f914d9da",
"aaaa",
"cccc",
null,
"bbbb",
"2",
"",
1
],
[
"5d28079eeba04543f914d62f",
"dddd",
"zzzz",
null,
"bbbb",
"3",
"",
2
],
[
"5d2807bdeba04543f914eb25",
"eeee",
"cccc",
null,
"zzzz",
"3",
"",
1
],
],
"email" : "w#w.com",
}
i tried this
k=USERS_COLLECTION.updateOne({'$and':[{'_id':'david'},{'havelist':{'$elemMatch':{'0':"5d2807a1eba04543f914d9da"}}}]},{'$set':{'havelist.$.7':0}})
in order to attribute 0 to the value
but it didn't work

COLLECTION.find_one_and_update({'$and':[{'_id':'david'},{'havelist':{'$elemMatch':{'0':'5d2807a1eba04543f914d9da'}}}]},{'$set':{'havelist.$.7':0}})
this will succesfully change the value to 0. i'm still searching for an answer on how to increase or decrease by 1 the value.

Related

mongodb how to display Null or none for a field instead of it being blank

The query below will show me the names of people and all their details. Some have addresses, some have not. It shows the addresses where they exist. What do I use if I wanted to show "addresses: "none" " where there are none given? I am also trying to sort by name.
db.test.find({name:{$exists:true}}, {_id:0}, {$sort:{"name":1}})
So far I can select the ones which have names, hide the _id column from view and sort by name. All the addresses (where they exist) are given. It is the "addresses: none" I am finding tricky.
Any pointers? Thank you.
test collection with the following documents
{"_id" : 1, "name" : "Thyame", "address": "Kapan" },
{"_id" : 2, "name" : "Diple", "address": null },
{"_id" : 3, "name" : "Sid" }
and Query is
db.test.aggregate
(
[
{
$project: {
address: { $ifNull: [ "$address", "Null" ] }
}
}
]
);

Get one element from the array of results by index

So every user has 0-3 items in the database. I don't have their indexes, I sort them by creation date, from oldest to youngest. I was wondering if it is possible to get the element from result array by its index by native mongo/mongoose tools.
For example I have these 3 documents in DB for user theguy:
{ "_id" : 1, "name" : "theguy", otherdata: ["data 1", "data 2"] }
{ "_id" : 10, "name" : "theguy", otherdata: ["data 1", "data 2"] }
{ "_id" : 333, "name" : "theguy", otherdata: ["data 1", "data 2"] }
_id will be ObjectId
Then user will try to get some data. He inputs a number in the range from 1 to 3. There can be 0, 1, 2 or 3 entries in database under his name. The example above displays the situation when user has all 3 entries filled. But user doesn't care about all of them right now, he entered the index, he needs only second result out of these 3.
What works for me right now is this:
//user defined the index
index = 2;
//search all docs by this user, but get only ids
let usrlist = await User.find({"name": name}).distinct('_id');
//based on the ids array we can now request exact second document from the database:
//usrlist[index-1] or usrlist[1]
let exactusr = await User.findOne({"_id": usrlist[index-1]});
So the result for exactusr will be:
{ "_id" : 10, "name" : "theguy", otherdata: ["data 1", "data 2"] }
I try to minimize the load by getting only ids, instead of all 3 documents at once. Now the thing is, well, this doesn't look "nice". Getting an array of _id to create another query based on that doesn't seem optimal. And at the end, I don't even know what is better: to do 2 queries(as above) or do 1 query for all user documents and choose the one user needs by index. Documents may be kind of big, containing up to 12000 characters each.
So looking for native ways I found $slice, but I don't think it works with the result array, or I don't understand how.
My attempt of using $slice:
index = 2;
usr = await User.find({"name": name}, {$slice: [index-1, 1]});
Result:
[ {"_id" : 1}, {"_id" : 10}, {"_id" : 333} ]
Expected result:
{ "_id" : 10, "name" : "theguy", otherdata: ["data 1", "data 2"] }
Any ideas? Or other methods that I could make this work?
I created a collection called "sample" and inserted the sample data that you provided.
db.sample.aggregate([{$match : {"name": "theguy"}}]);
{ "_id" : 1, "name" : "theguy", "otherdata" : [ "data 1", "data 2" ] }
{ "_id" : 10, "name" : "theguy", "otherdata" : [ "data 1", "data 2" ] }
{ "_id" : 333, "name" : "theguy", "otherdata" : [ "data 1", "data 2" ] }
Initialize a variable index : var index=1;
Now if we consider the above data, the indexes of the three rows would be 0,1,2. If I want to retrieve the 2nd row with _id : 10, then the index is supposed to be 1. In that case, the aggregate query would look like :
db.sample.aggregate([
{ $match : {"name": "theguy"}},
{ $skip : index},
{ $limit : 1 }
]);
In case you want your index value to mean the position i.e. in this case the position is 2, then modify the query like :
var index=2;
db.sample.aggregate([
{ $match : {"name": "theguy"}},
{ $skip : index-1},
{ $limit : 1 }
]);
Try this solution & let us know, if it worked for you!

How to add an element to an array based an index position

I have the following document:
{
"_id" : 5,
"quizzes" : [
{ "x": 111, "score" : 10, "index" : 2 },
{ "x": 2, "score" : 8, "index" : 1 },
{ "x": 13, "score" : 5, "index" : 0 },
{ "x": 4, "score" : 6, "index" : 3 }
]
}
Now on the UI side I have the ability to add new quizzes which could be added anywhere in the list (start, end or any index above).
I can also remove a quiz which means the list index above changes. Also I can reorder a quiz which means indexes above change for those quizzes after the reordered quiz.
Now, in the mongo world how is the best way to handle these scenarios?
Should i just read the whole document and replace the whole of the quizzes sections (note my actual document will have a lot more data in the arrays)?
Or do I depending on the action I am doing (add, remove, reorder) apply a specific action ($push, etc) to be more specific on my what I am doing?

How to set the value of multiple arrays at the same time in MongoDB?

Schema
"_id" : ObjectId(""),
"top_level_array" : [
{
"array_one" : [
"1",
"2",
"3",
"4",
"5"
],
"name" : "user name",
"array_two" : [
"1",
"2"
],
"array_three" : [
"1",
"2"
]
},
{
// another document
},
{
// another document
}
]
Question
I can successfully set the value of a single array with:
db.users.update({email_addr:"email#email.com", "top_level_array.name":"user name"},{$set:{"top_level_array.$.array_one":["just one value now"]}})
How can I set the value of multiple arrays at the same time?
This worked for me:
Separate the different arrays to update with a comma eg:
db.users.update({email_addr:"email#email.com", "top_level_array.name":"user name"},{$set:{"top_level_array.$.array_one":["just one value now"],"top_level_array.$.array_two":["another value"]}})
And to clarify the context of this solution, the query is conditional in that it searches for a match on name within a document, and then updates arrays that are at the same level as name ie array_one and array_two.

Can I utilize indexes when querying by MongoDB subdocument without known field names?

I have a document structure like follows:
{
"_id": ...,
"name": "Document name",
"properties": {
"prop1": "something",
"2ndprop": "other_prop",
"other3": ["tag1", "tag2"],
}
}
I can't know the actual field names in properties subdocument (they are given by the application user), so I can't create indexes like properties.prop1. Neither can I know the structure of the field values, they can be single value, embedded document or array.
Is there any practical way to do performant queries to the collection with this kind of schema design?
One option that came to my mind is to add a new field to the document, index it and set used field names per document into this field.
{
"_id": ...,
"name": "Document name",
"properties": {
"prop1": "something",
"2ndprop": "other_prop",
"other3": ["tag1", "tag2"],
},
"property_fields": ["prop1", "2ndprop", "other3"]
}
Now I could first run query against property_fields field and after that let MongoDB scan through the found documents to see whether properties.prop1 contains the required value. This is definitely slower, but could be viable.
One way of dealing with this is to use schema like below.
{
"name" : "Document name",
"properties" : [
{
"k" : "prop1",
"v" : "something"
},
{
"k" : "2ndprop",
"v" : "other_prop"
},
{
"k" : "other3",
"v" : "tag1"
},
{
"k" : "other3",
"v" : "tag2"
}
]
}
Then you can index "properties.k" and "properties.v" for example like this:
db.foo.ensureIndex({"properties.k": 1, "properties.v": 1})