How should you query a collection via nested arrays in mongodb (Meteor)? - mongodb

I'm dont think that this is a Meteor specific question, but rather around mongo and building mongo queries.
If I have the following structure,
{
username : someName,
contacts : [
{
userid : asdfae33rtqqxxx,
name : contactName,
status : friend
}
{
userid : asdfae33rtqqxxx,
name : anotherName,
status : pending
}
{
userid : asdfae33rtqqxxx,
name : contactName,
status : blocked
}
]
}
How could I pass in values from this array into a query against the users collection, to a) get the users in this array, or b) get all users in this array from the users collection with a particular status.
If this is not possible, how should I adjust my schema in order to make these sorts of queries possible?

This function will return a Meteor.users cursor based on an array of contacts and an optionally required status:
var usersByContacts = function(contacts, requiredStatus) {
var userIds = _.chain(contacts)
.map(function(c) {
if (requiredStatus) {
if (c.status === requiredStatus)
return c.userid;
} else {
return c.userid;
}
})
.compact()
.value();
return Meteor.users.find({_id: {$in: userIds}});
};
You can use it like this:
var users1 = usersByContacts(thing.contacts);
var users2 = usersByContacts(thing.contacts, 'pending');
This assumes thing has the schema referenced in your question. Also note that if you find this is a common pattern in your code, you should consider turning usersByContacts into a transform.

Related

prepend email addresses with string in mongodb collection using mongoose

My collection has a column named email which contains email address. Now I need to update all these email addresses, appending _01 at the end of the email (before #). Basically if the email is ipman#gmail.com, it should be converted to ipman_01#gmail.com. I know we can use updateMany to update multiple documents using mongoose, but to achieve what I need, I believe I need to use some kind of regex too?
Please try this :
let emails = ['ipman#gmail.com', 'ipman2#gmail.com']
let bulkArr = [];
for (const i of emails) {
let newEmail = i.split("#");
newEmail = newEmail[0] + '_01' + '#' + newEmail[1]
bulkArr.push({
updateOne: {
"filter": { email : i },
"update": { '$set': { email : newEmail } }
}
})
}
let updateResult = await MongooseModel.bulkWrite( bulkArr, { ordered : false })
/** Passing { ordered : false } to make sure update op doesn't fail if updating one document in the middle fails, Also bulkWrite returns write result check documentation for more info */
console.log('matchedCount ::', updateResult.matchedCount, 'modifiedCount ::', updateResult.modifiedCount)
Ref : .bulkWrite()

Meteor queries not working with Meteor.Collection.ObjectID

I'm having a lot of trouble getting query results for certain collections in Meteor.
I have set
idGeneration : 'MONGO'
in the collection definitions, and in the mongo shell these collections look like this :
the document i want, call it documentW (from CollectionA) = {
"_id" : ObjectId("7032d38d35306f4472844be1"),
"product_id" : ObjectId("4660a328bd55247e395edd23"),
"producer_id" : ObjectId("a5ad120fa9e5ce31926112a7") }
documentX (from collection "Products") = {
_id : ObjectId("4660a328bd55247e395edd23")
}
documentY (from collection "Producers") = {
_id : ObjectId("a5ad120fa9e5ce31926112a7")
}
If i run a query like this in Meteor
CollectionA.findOne({ product_id : documentX._id, producer_id : documentY._id})
I'm expecting to get my documentW back... but I get nothing.
When I run this query in the mongo shell
db.collectiona.find({ product_id : ObjectId("4660a328bd55247e395edd23"), producer_id :
ObjectId("a5ad120fa9e5ce31926112a7") })
I get my documentW back no problem.
Of course in Meteor if I call
console.log(documentX._id)
I get this
{ _str : "4660a328bd55247e395edd23" }
Anyone have any ideas what is going on here ? I have tried all kinds of things like
Meteor.Collection.ObjectID(documentX._id._str)
but the search still returns empty...
Running the latest 0.7.0.1 version of Meteor...
I don't know if this answers your question, but I can't put this code in a comment. This code is working for me, trying to follow what I believe you are trying to do:
Products = new Meteor.Collection("products", {
idGeneration: "MONGO"
});
Producers = new Meteor.Collection("producers", {
idGeneration: "MONGO"
});
CollectionA = new Meteor.Collection("a", {
idGeneration: "MONGO"
});
Products.insert({
foo: "bar"
});
Producers.insert({
fizz: "buzz"
});
var documentX = Products.findOne();
var documentY = Producers.findOne();
CollectionA.insert({
product_id: documentX._id,
producer_id: documentY._id
});
var documentW = CollectionA.findOne({
product_id: documentX._id,
producer_id: documentY._id
});
console.log(documentW); // This properly logs the newly created document
This is on 0.7.0.1. Do you see anything in your code that diverges from this?

mongodb $not _id

I need a way to search but not include an _id which is already on the screen in front of the user. For example, I have 3 pet profiles one which the user is already viewing.
On that page I have a heading called My Family. I then run this search:
public function fetch_family($owner)
{
$collection = static::db()->mypet;
$cursor = $collection->find(array('owner' => new MongoId($owner)));
if ($cursor->count() > 0)
{
$family = array();
// iterate through the results
while( $cursor->hasNext() ) {
$family[] = ($cursor->getNext());
}
return $family;
}
}
And it returns all the pets in my family even knowing I am already showing one. So I want to exclude that one _id from the search.
I thought something like this.
$cursor = $collection->find(array('owner' => new MongoId($owner), '$not'=>array('_id'=>new MongoId(INSERT ID HERE))));
However, that just stops the whole thing from running.
You need to do a $ne (not equal) to make sure the current pet you are viewing is excluded from the search by owner.
Example in the mongo shell:
var viewingPetId = ObjectId("515535b6760fe8735f5f6899");
var ownerId = ObjectId("515535ba760fe8735f5f689a");
db.mypet.find(
{
_id: { $ne: viewingPetId },
owner: ownerId
}
)
Use $ne as (notice no need to use ObjectId(), string will autocast to ObjectId):
db.organizations.find({"_id" : {$ne:"563c50e05cdb2be30391e873"}})

MongoDB update sub document array or dictionary

I’m trying to decide which of the following schemas is most efficient for implementation with mongodb. I require to keep track of friend id’s & mutual friend counts for each user in a system (user_id is unique across the collection). The number of friends may be up to 100,000.
Schema 1
{
“_id” : “…”,
“user_id” : “1”,
friends : {
“2” : {
“id” : “2”,
“mutuals” : 3
}
“3” : {
“id” : “3”,
“mutuals”: “1”
}
“4” : {
“id” : “4”,
“mutuals”: “5”
}
}
}
Schema 2
{
“_id” : “…”,
“user_id” : “1”,
friends : [
{
“id” : “2”,
“mutuals” : 3
},
{
“id” : “3”,
“mutuals”: 1
},
{
“id” : “4”,
“mutuals”: 5
}
]
}
Requirements:
Given a user_id and friend id update the document such that if friend id exists increment mutuals by 1, else add new friend with a mutuals of 1
Given a user_id and friend id update the document such that if friend exists and mutual count > 1 then decrement mutual count by 1, else remove friend from document
With a list of ids, lookup in the document to identify which friend ids exist (I know this is something that can be done client side, but am interested in server side solution)
What indexes should be used to speed up the above?
In my work in progress I have implemented much of this with schema 1, but am now starting to realise it may not be as suitable as schema 2. However, I am having trouble finding the most efficient methods for the above questions.
AFAIK, points 1 & 2 cannot be done in a single statement in mongoDB.
You would probably have to query mongodb to check if the particular user_id, friend.id combination exists.
If it does then update else add to friend array.
Refer to JavaSscript code below:
use <dbname>;
var FriendsList;
var FriendId = "9";
var UserId = "1";
var Friends = db.Friends.findOne({"user_id":UserId, "friends.id":FriendId});
if (Friends != null){
print ("Friends is not null");
FriendsList = Friends.friends;
// print (FriendsList.toSource());
for (var i = 0; i < FriendsList.length; i++){
var curFriend = FriendsList[i];
if (curFriend["id"] == FriendId){
curFriend["mutuals"] = curFriend["mutuals"] + 1;
FriendsList[i] = curFriend;
break;
}
}
}
if (Friends == null){
print ("Friends is null");
Friends = db.Friends.findOne({"user_id":UserId});
FriendsList = Friends.friends;
FriendsList.push({"id":FriendId, "mutuals":1});
// print (FriendsList.toSource());
}
Friends.friends = FriendsList;
db.Friends.save(Friends);
Pls share if you find a better way to do this.

Remove nested document with condition in MongoDB

For the following JSON how do I remove the dog whose height is the least
{
_id:0
"name":"Andy",
"pets":[
{
"type":"dog","name":"max","height":120
},
{
"type":"dog","name":"rover","height":44
},
{
"type":"dog","name":"katie","height":100
},
{
"type":"cat","name":"minni"
}
]
}
The problem is the array of subdocuments is not a collection, you can't sort or do something else on it. But if you have an access to any language interface like JavaScript or else it's possible. You just need to extract list of subdocuments, sort them by height, remember the first one and then run the command to pull it from the array based on its name and height.
It can be done for example using this JavaScript code right in the MongoDB shell:
var min = 0; var name = "";
db.animals.find({ query:{"_id" : 0} }).forEach(
function(record){
var sets = record.pets;
min = sets[0].height;
sets.forEach(function(set){
if(set.height <= min)
{min=set.height;
name=set.name;}
});
print(min);
print(name);
query = {"_id": 0}
update = { "$pull" : { "pets" : { "name" : name } } };
db.animals.update(query, update);
})
I suspect the solution is not the most elegant but anyway it works.