Find the documents in mongoose whose array fields exactly match - mongodb

I'm using Mongoose and have a schema like this:
var chat = new mongoose.Schema({
chatId : String,
members : [{
id : String,
name : String
}]
});
Suppose I have two chat document like this
{
chatId : 'Edcjjb',
members : [
{
id : 'a1',
name : 'aaa'
},
{
id : 'b1',
name : 'bbb'
}
]
}
{
chatId : 'Fxcjjb',
members : [
{
id : 'a1',
name : 'aaa'
},
{
id : 'b1',
name : 'bbb'
},
{
id : 'c1',
name : 'ccc'
}
]
}
I want to find all those documents which have only specfied members Id.
For example, if I specify a1 and b1
then only the first document should be retrieved as the second document contains id c1 as well.
And if I specifiy a1,b1,c1
then only second document should be specified.
Please tell me how to do this in mongoose

You can specify a clause on the array size, like
{ members : { $size : 2 } } in your first example and
{ members : { $size : 3 } } in the second one.
Can that work for you?
EDIT: I should also mention that the other part of the query should be
{ "members.id": { $all: [ "a1" , "b1" ] } }
and, for the second example,
{ "members.id": { $all: [ "a1" , "b1", "c1" ] } }

Related

MongoDB remove all matching items from sub-sub-array

Just wondering what the best way to accomplish this is. I can think of some janky ways, but they don't seem right.
What I'm trying to do is remove all sub-sub-array objects from a documents. Like follows:
SCHEMA
schema {
person: Array<{
id: string;
posts: Array<{
id: string,
comments: Array<{
id: string
tagged_person_id: string;
}>
}>
}>
}
What I am looking for some way to delete all comments in every post for each person where the comment has tagged_person_id == some_id. This isn't my actually use-case, but it represents the same concept.
I know how to use $pull to remove from a subarray for one subdocument, but just not sure how to accomplish all of this in one query, or if it's even possible.
As per JIRA ticket SERVER-1243 and the documentation, starting with MongoDB v3.5.12, given the following document:
{
"posts" : [
{
"comments" : [
{
"tagged_person_id" : "x"
},
{
"tagged_person_id" : "y"
}
]
},
{
"comments" : [
{
"tagged_person_id" : "x"
}
]
},
{
"comments" : [
{
"tagged_person_id" : "y"
}
]
}
]
}
You can run this update:
db.collection.update({}, {
$pull : {
"posts.$[].comments" : {"tagged_person_id": "x"}
}
})
in order to remove all comments where tagged_person_id is equal to "x".
Result:
{
"posts" : [
{
"comments" : [
{
"tagged_person_id" : "y"
}
]
},
{
"comments" : []
},
{
"comments" : [
{
"tagged_person_id" : "y"
}
]
}
]
}

How do I add an array of elements in MongoDB to an array in an existing document?

In MongoDB, I'm trying to write a query to add elements from an array to an existing document, but instead of adding the elements as objects:
property: ObjectID(xxx)
the elements are getting added as just
ObjectID(xxx)
Forgive me if I get the terminology wrong. I'm completely new to MongoDB; I normally only work with relational databases. How do I properly add these new elements?
I have a collection called auctions which has two fields: ID and properties. Properties is an array of objects named property. Here's an example with two auction documents:
** I changed the object IDs to make them easier to reference in our discussion
Collection db.auctions
{
"_id" : ObjectId("abc"),
"properties" : [
{
"property" : ObjectId("prop1")
},
{
"property" : ObjectId("prop2")
},
{
"property" : ObjectId("prop3")
}]
}
{
"_id" : ObjectId("def"),
"properties" : [
{
"property" : ObjectId("prop97")
},
{
"property" : ObjectId("prop98")
}]
}
I want to add 3 new properties to auction "abc". How do I do this?
Here's is what I tried:
I have an array of properties that looks like this:
Array PropsToAdd
[
ObjectId("prop4"),
ObjectId("prop5"),
ObjectId("prop6")
]
I wrote an update query to push these properties into the properties array in auctions:
db.auctions.update(
{"_id": "abc"}
,
{ $push: { properties: { $each: PropsToAdd } } }
);
This query gave the result below. Notice that instead of adding elements named property with a value from my array, it's just added my values from my array. I obviously need to add that "property" part, but how do I do that?
Collection db.auctions (_id "abc" only)
{
"_id" : ObjectId("abc"),
"properties" : [
{
"property" : ObjectId("prop1")
},
{
"property" : ObjectId("prop2")
},
{
"property" : ObjectId("prop3")
},
ObjectId("prop4"),
ObjectId("prop5"),
ObjectId("prop6"),
ObjectId("prop7")]
}
The result I'm looking for is this:
Collection db.auctions (_id "abc" only)
{
"_id" : ObjectId("abc"),
"properties" : [
{
"property" : ObjectId("prop1")
},
{
"property" : ObjectId("prop2")
},
{
"property" : ObjectId("prop3")
},
{
"property" : ObjectId("prop4")
},
{
"property" : ObjectId("prop5")
},
{
"property" : ObjectId("prop6")
}
}
Here is some further information on that array of properties I'm adding. I get it from running these queries. Perhaps one of them needs changed?
This query gets an array of current properties:
var oldActiveProperties = db.properties.distinct( "saleNumber", { "active": true, "auction": ObjectId("abc") } );
Then those results are used to find properties in the new file that weren't in the old file:
var PropsToAdd = db.newProperties.distinct(
"_id"
, { "saleNumber": { "$nin": oldActiveProperties }, "active": true}
);
The resulting array is what I need to add to the auctions collection.
Use the JavaScript's native map() method to map the array into an array of documents. The following shows this:
var PropsToAdd = db.newProperties.distinct("_id",
{ "saleNumber": { "$nin": oldActiveProperties }, "active": true}
).map(function (p) { return { property: p }; });
db.auctions.update(
{"_id": "abc"},
{ $push: { "properties": { "$each": PropsToAdd } } }
);

Update Object at Specific Array Index in MongoDB

I have a collection of the form
{ id : 1,
data: [ [ { name : "alice" }, { name : "bob" } ],
[ { name : "dan" }, { name : "rob" } ] ] }
and the structure of the array has meaning. How would I update the first element ([0][0]) and set name = "alex". I've seen many questions addressing how to update array elements that match a query but not specific elements. To be clear, after the update, the record should look like this:
{ id : 1,
data: [ [ { name : "alex" }, { name : "bob" } ],
[ { name : "dan" }, { name : "rob" } ] ] }
Assuming, you have created the structure with some purpose, which ideally becomes tougher to query, you could update it by specifying the index explicitly:
db.collection.update({"id":1},{$set:{"data.0.0.name":"alex"}})
If we don't have a fixed position and is known at runtime or Dynamic
then this approach works perfectly
var setObject = {};
setObject["board."+ x +"."+ y] = player;
gamesColl.update({_id: realId},{
$set:setObject
}, function(err,doc){
console.log(err,doc);
});
you can go further!!
if you want to paste indexes as a variable use string template like this
db.collection.update({"id":1},{$set:{[`data.${index}.${index}.name`]:"alex"}})

search in combination two field in Mongodb

I use from Mongodb and my database schema like this:
firstName: 'vahid',
lastName: 'kh',
age: 12
I want find all records that firstname + lastname likes 'vahid kh'. In SQL, this would be:
Select * from table where firstName + lastName like '%vahid kh%'
Seems this is available only in 2.4, which was released today.
> db.temp.save({ "firstName" : "Bob", "lastName" : "Smith", Age : 12 });
> db.temp.find();
{ "_id" : ObjectId("5148a2a00477cddcdece1b34"), "firstName" : "Bob", "lastName" : "Smith", "Age" : 12 }
> db.temp.aggregate({ $project : { "name" : { $concat : [ "$firstName", " ", "$lastName" ] } } });
{
"result" : [
{
"_id" : ObjectId("5148a2a00477cddcdece1b34"),
"name" : "Bob Smith"
}
],
"ok" : 1
}
You can use $regex, this way you can use partial matches.
Something like that:
db.collection.find( { field: /acme.*corp/i } )
Here is somewhat similar question with answer in php: MongoRegex and search multiple rows in collection
Docs about mongoregex are here: http://docs.mongodb.org/manual/reference/operator/regex/
Edit:
I just read your comment with query in sql. Simple solution could be to make field fullname and search it with $regex, it is kind of db denormalization, where you store somewhat redundant data.
Or even easier, this should do the job too:
db.collection.find( { firstName: /*vahid/i, lastName: /kh*/i } )
To search against a combination of two or more fields, you need to use the aggregation framework. It lets you sort by the combinations of fields, and you don't need to store (denormalize) any extra fields:
db.collection.aggregate([
{
$match: {
// Optional criteria to select only some documents to process, such as...
deleted: null
}
},
{
$addFields: {
// Need to prefix fields with '$'
fullName: { $concat: [ "$firstName", "$lastName" ] },
}
},
{
$search: { fullName: /.*vakid kh.*/ },
}
]);
Explanation:
the $addFields aggregation pipeline stage creates dynamic, on-the-fly fields
$concat creates the fullName field by concatenating the first and last name
$search does a regular expression search, which is the MongoDB equivalent to the SQL LIKE operator.
I have code in expressjs code aggregate is bit slow then find so I have use conditional based and use regular express to find true results, I am sharing with you nodejs code I hope it useful for you or my other code lover friends.
router.get('/publisher/:q',function(req,res){
var q = ucfirst( req.params['q'] );
var qSplit = q.split(" ");
var db = mongojs(CONNECTION_STRING, ['tags_helper','users']);
var query = { "status" : 1, isAdmin : { $exists : false } };
console.log(qSplit);
if( qSplit.length > 1 )
{
query["firstName"] = new RegExp(qSplit[0],"i");
query["lastName"] = new RegExp(qSplit[1],"i");
}
else
{
qSplit[0] = new RegExp(qSplit[0],"i");
qSplit[1] = new RegExp(qSplit[0],"i");
//query.push( { $or : [ {"firstName": new RegExp(q,"i")},{"lastName": new RegExp(q,"i")} ] } );
query["$or"] = [ {"firstName": new RegExp(q,"i")},{"lastName": new RegExp(q,"i")} ];
}
db.users.find( query,{_id : 1,firstName:1,lastName:1,image:1,profileName:1}).limit(10,function (err, users) {
//TODO your further code ..
});
});
Have a happy coding day.

Case Insensitive search with $in

How to search a column in a collection in mongodb with $in which includes an array of elements for search and also caseInsensitive matching of those elements in the column ?
Use $in with the match being case insensitive:
Data example:
{
name : "...Event A",
fieldX : "aAa"
},
{
name : "...Event B",
fieldX : "Bab"
},
{
name : "...Event C",
fieldX : "ccC"
},
{
name : "...Event D",
fieldX : "dDd"
}
And we want documents were "fieldX" is contained in any value of the array (optValues):
var optValues = ['aaa', 'bbb', 'ccc', 'ddd'];
var optRegexp = [];
optValues.forEach(function(opt){
optRegexp.push( new RegExp(opt, "i") );
});
db.collection.find( { fieldX: { $in: optRegexp } } );
This works for $all either.
I hope this helps!
p.s.: This was my solution to search by tags in a web application.
You can use $elemMatch with regular expressions search, e.g. let's search for "blue" color in the following collection:
db.items.save({
name : 'a toy',
colors : ['red', 'BLUE']
})
> ok
db.items.find({
'colors': {
$elemMatch: {
$regex: 'blue',
$options: 'i'
}
}
})
>[
{
"name": "someitem",
"_id": { "$oid": "4fbb7809cc93742e0d073aef"},
"colors": ["red", "BLUE"]
}
]
This works for me perfectly.
From code we can create custom query like this:
{
"first_name":{
"$in":[
{"$regex":"^serina$","$options":"i"},
{"$regex":"^andreW$","$options":"i"}
]
}
}
This will transform to following in mongo after query:
db.mycollection.find({"first_name":{"$in":[/^serina$/i, /^andreW$/i]}})
Same for "$nin".
This is pretty simple
const sampleData = [
RegExp("^" + 'girl' + "$", 'i'),
RegExp("^" + 'boy' + "$", 'i')
];
const filerObj = { gender : {$in : sampleData}};
The way to do it in Java is:
List<String> nameList = Arrays.asList(name.split(PATTERN));
List<Pattern> regexList = new ArrayList<>();
for(String name: nameList) {
regexList.add(Pattern.compile(name , Pattern.CASE_INSENSITIVE));
}
criteria.where("Reference_Path").in(regexList);
Here is my case insensitive search (query) with multiple condition (regex) from data of array, I've used $in but it doesn't support case insensitive search.
Example Data
{
name : "...Event A",
tags : ["tag1", "tag2", "tag3", "tag4]
},
{
name : "...Event B",
tags : ["tag3", "tag2"]
},
{
name : "...Event C",
tags : ["tag1", "tag4"]
},
{
name : "...Event D",
tags : ["tag2", "tag4"]
}
My query
db.event.find(
{ $or: //use $and or $or depends on your needs
[
{ tags : {
$elemMatch : { $regex : '^tag1$', $options : 'i' }
}
},
{ tags : {
$elemMatch : { $regex : '^tag3$', $options : 'i' }
}
}
]
})