Mongo: If any array position matches single query - mongodb

Assuming this data structure:
{
"title": "Foo bar",
"username": "jackwreid"
},
{
"title": "Bish bosh",
"username": "lizziebump"
},
{
"title": "Ham nam",
"username": "lizziebump"
},
{
"title": "Blub blub",
"username": "jvarn"
}
And assuming
var userFollowing = ["lizziebump", "jackwreid"]
How would I do a db.posts.find() for posts where the username matches any of the contents of the userFollowing array?
I'm trying to build a query that returns posts by users in the current user's following list and I can only find docs on how to do this the other way around where the query returns posts if a single query string is in any array position.

You can use the $in operator
db.collection.find({username: {$in: userFollowing}})

Related

Search and update in array of objects MongoDB

I have a collection in MongoDB containing search history of a user where each document is stored like:
"_id": "user1"
searchHistory: {
"product1": [
{
"timestamp": 1623482432,
"query": {
"query": "chocolate",
"qty": 2
}
},
{
"timestamp": 1623481234,
"query": {
"query": "lindor",
"qty": 4
}
},
],
"product2": [
{
"timestamp": 1623473622,
"query": {
"query": "table",
"qty": 1
}
},
{
"timestamp": 1623438232,
"query": {
"query": "ike",
"qty": 1
}
},
]
}
Here _id of document acts like a foreign key to the user document in another collection.
I have backend running on nodejs and this function is used to store a new search history in the record.
exports.updateUserSearchCount = function (userId, productId, searchDetails) {
let addToSetData = {}
let key = `searchHistory.${productId}`
addToSetData[key] = { "timestamp": new Date().getTime(), "query": searchDetails }
return client.db("mydb").collection("userSearchHistory").updateOne({ "_id": userId }, { "$addToSet": addToSetData }, { upsert: true }, async (err, res) => {
})
}
Now, I want to get search history of a user based on query only using the db.find().
I want something like this:
db.find({"_id": "user1", "searchHistory.somewildcard.query": "some query"})
I need a wildcard which will replace ".somewildcard." to search in all products searched.
I saw a suggestion that we should store document like:
"_id": "user1"
searchHistory: [
{
"key": "product1",
"value": [
{
"timestamp": 1623482432,
"query": {
"query": "chocolate",
"qty": 2
}
}
]
}
]
However if I store document like this, then adding search history to existing document becomes a tideous and confusing task.
What should I do?
It's always a bad idea to save values are keys, for this exact reason you're facing. It heavily limits querying that field, obviously the trade off is that it makes updates much easier.
I personally recommend you do not save these searches in nested form at all, this will cause you scaling issues quite quickly, assuming these fields are indexed you will start seeing performance issues when the arrays get's too large ( few hundred searches ).
So my personal recommendation is for you to save it in a new collection like so:
{
"user_id": "1",
"key": "product1",
"timestamp": 1623482432,
"query": {
"query": "chocolate",
"qty": 2
}
}
Now querying a specific user or a specific product or even a query substring is all very easily supported by creating some basic indexes. an "update" in this case would just be to insert a new document which is also much faster.
If you still prefer to keep the nested structure, then I recommend you do switch to the recommended structure you posted, as you mentioned updates will become slightly more tedious, but you can still do it quite easily using arrayFilters for updating a specific element or just using $push for adding a new search

mongodb $ causing error The positional operator did not find the match needed from the query

I’ve been trying to update the data in my mongoDB.
I want to update all products with a new productName field.
my data looks something like:
{
"id": "12345",
"products": [{
"id": 0
"productCode": "test",
"status": "PENDING",
},
{
"id": 1
"productCode": "test",
"status": "COMPLETE",
}],
}
When I try the following. I get this error The positional operator did not find the match needed from the query.
db.customers.updateMany(
{ id: "12345" },
{ $set: {
"products.$.productName": "Name here" }
}
)
If I do account.0.productName then it’s fine and updates. I’m not sure why $ is not working for me
db.customers.updateMany(
{ id: "12345" },
{ $set: {
"products.0.productName": "Name here" }
}
)
Positional operator is not working because you are not using the array into the find (first object)
If you try this query it will work as expected because you have the position finding by products.id.
Otherwise, if you don't have the position into array where update, yo can't use $ operator in this way. You need this query:
db.collection.update({
"id": "12345",
},
{
"$set": {
"products.$[].newField": "test2"
}
},
{
"multi": true
})
Mongo playground example here
Using $[] you can reference the array and add the value into each object.
$[] docs here
It says:
The all positional operator $[] indicates that the update operator should modify all elements in the specified array field.
That's exactly we want :)

How to search through a list of objects nested inside an array with Spring data MongoDB?

I have got a collection of documents and each documents contains a nested array of objects.
{
"id": "309324739",
"debters": [
{
"user": {
"name": "John Doe",
"internal": true
},
"debt": 1463,
},
{
"user": {
"name": "Alex Tree",
"internal": false
},
"debt": 53443,
},
}
What I'm trying to do is to return find the document by id and then find inside the debters list that has a false flag?
I tried the following query...
Debters findByIdAndDebters_User_InternalIsFalse(#Param("id") String id,);
But I'm getting an error saying that it can find "internal" property. What am I doing wrong and how can I loop through array using this magic mongo repository query?
you need to write a native query for that which is similar to
#Query("{'debters.user.internal':false,'_id':''}")
Debters findByIdAndDebtersUserInternalIsFalse(#Param("id") String id,);

mongodb: return an array of document ids

Is it possible to query mongodb to return array of matching document id values, without the related keys?
Please consider following 'parent' data structur:
{
"_id": ObjectId("52448e4697fb2b775cb5c3a7"),
"name": "Peter",
"children": [
{
"name": "joe"
}
]
},
{
"_id": ObjectId("52448e4697fb2b775cb5c3b6"),
"name": "Marry",
"children": [
{
"name": "joe"
}
]
}
I would to query for an array of parent _ids whose children have the name "joe"
For provided sample data, I would like the following output returned from mongo:
[ObjectId("52448e4697fb2b775cb5c3a7"), ObjectId("52448e4697fb2b775cb5c3b6")]
I know that I can query for an output like this, which also contains the keys
[{"_id": ObjectId("52448e4697fb2b775cb5c3a7")}, {"_id": ObjectId("52448e4697fb2b775cb5c3b6")}]
However I need to push above array to another document with an update operation like this:
db.statistic.update({"date": today}, {$push: {"children": [ObjectId("52448e4697fb2b775cb5c3a7"), ObjectId("52448e4697fb2b775cb5c3b6")]}}, true, false)
I would like to avoid sorting out the document structure, in case it is possible to just return an array containing the appropriate values using mongo
It should be possible by
db.coll.distinct("_id", {"children.name": "joe"})

MongoDB and complex query on ordered arrays

I've the following entity persisted on mongo in a users collection:
{
"fullname": "Luke Tomson",
"nicknames": [
{
"name": "lukeone",
"created": ISODate("2011-05-18T19:42:56.411Z")
},
{
"name": "magicluke",
"created": ISODate("2012-01-12T19: 42: 56.411Z")
},
{
"name": "superluke",
"created": ISODate("2012-03-27T19: 42: 56.411Z")
}
}
]
}
How do I get the last nickname name for a user whose fullname is "Luke Tomson" ? And more specifically, how do I retrieve in the mongo shell a specific index (the first or the last) of a sorted array that was sorted upon a specific field (in this case, created).
Thanks
From the documentation, the second object passed to a .find is a selection of what portions of the object you want returned.
So, what you want is basically:
db.users.find({"fullname": "Luke Tomson"}, {"nicknames": { $slice: -1 } });
Though you might also want to return the _id if there are multiple "Luke Tomson"s in your DB and you need to uniquely identify them.