I have an issue fetching data from a MongoDB database that contains an array and also retrieve elements that are not in the query array.
The database is structured like below:
{
"name": "James",
"visitedPlaces": ['Germany', "Scotland"]
},
{
"name": "John",
"visitedPlaces": ['India', "Russia"]
}
I want to query the VisitedPlaces to return only return people that have not visited Russia and India.
NOTE: I am querying the database with another array.
db.Profile.find(
{ VisitedPlaces: { $elemMatch: { $nin: ["India", "Russia"] } } }
)
You must use $all to query an array that contains both the another array:
db.Profile.find(
{ visitedPlaces: {$not:{$all:["India", "Russia"]}}}
)
Related
is it possible to find out the query array element not in database?
example:
const query = ['aaa','bbb','ccc']
Documents in db:
[{name:'bbb'},{name:'ccc'}]
I want to find query array elements not in database:
return result should be:
['aaa']
I can't find some quickly method to do this except query each element(or batch?) in array
Any one has better method? thanks
Querying for stuff that are -missing- is always a more expensive operation, also there is no "magic" query to do it for you. I recommend using Mongo's distinct method, like so:
const queryArr = ['aaa', 'bbb', 'ccc'];
const allNames = await db.collection.distinct('name');
const notInDb = queryArr.filter(e => !allNames.includes(e));
However if you want to do it in 1 db command you could do something like this:
db.collection.aggregate([
{
$group: {
_id: null,
names: {
"$addToSet": "$name"
}
}
},
{
"$replaceRoot": {
"newRoot": {
results: {
$filter: {
input: [
"aaa",
"bbb",
"ccc"
],
as: "datum",
cond: {
$not: {
"$setIsSubset": [
[
"$$datum"
],
"$names"
]
}
}
}
}
}
}
}
])
Mongo Playground
As you can tell both approaches require you to load all the names into memory, there is no way around this, if your db's scale is too big for these approaches you will have to iterate over the query input and do it one by one.
const queryArr = ['aaa', 'bbb', 'ccc'];
for (let queryName of queryArr) {
const found = await db.collection.findOne({name: queryName})
if (!found) {
//ding
}
}
Assuming you have an index on name field this should be very efficient.
I'm trying to find all users except for a few, like this:
// get special user IDs
var special = db.special.find({}, { _id: 1 }).toArray();
// get all users except for the special ones
var users = db.users.find({_id: {$nin: special}});
This doesn't work because the array that I'm passing to $nin is not and array of ObjectId but an array of { _id: ObjectId() }
Variable special looks like this after the first query:
[ { _id: ObjectId(###) }, { _id: ObjectId(###) } ]
But $nin in the second query needs this:
[ ObjectId(###), ObjectId(###) ]
How can I get just the ObjectId() in an array from the first query so that I can use them in the second query?
Or, is there a better way of achieving what I'm trying to do?
Use the cursor.map() method returned by the find() function to transform the list of { _id: ObjectId(###) } documents to an array of ObjectId's as in the following
var special = db.special.find({}, { _id: 1 }).map(function(doc){
return doc._id;
});
Another approach you can consider is using the $lookup operator in the aggregation framework to do a "left outer join" on the special collection and filtering the documents on the new "joined" array field. The filter should match on documents whose array field is empty.
The following example demonstrates this:
db.users.aggregate([
{
"$lookup": {
"from": "special",
"localField": "_id",
"foreignField": "_id",
"as": "specialUsers" // <-- this will produce an arry of "joined" docs
}
},
{ "$match": { "specialUsers.0": { "$exists": false } } } // <-- match on empty array
])
I want to write a query in mongoDB where I want to find which elements of my array are present in database array 'upvotes' my query is given below
userupvotesmodel.findOne({mobile_no: "1234567890", upvotes : { $in : ["aa", "bdnvh", "563fa4408d88ea6c13c7abba", "djfufhgj", "5625bd9dbe545d2412d4ae62", "fjjshddhjfn", "djfhudh", "jfhshnffj", "sjdhfkskajdk"] } }, function(err, docs) {
if (err) {
console.log('Error Finding query results');
console.log(err);
res.json({success: 0, message : err});
return next(err);
} else {
if (docs) {
console.log('2 mins', currentdate.getMinutes());
console.log('2 secs', currentdate.getSeconds());
console.log('docs', docs);
}
}
});
Now for query above if any single value of my array is present in database then it sends whole upvotes array but I am not able to find out which elements were present for e.g "aa", "bdnvh" etc in database how can I write a query to know which elements are present in the database.
I want to find this in one query by querying for every single element; I know I can do this but I want to do this in any possible single mongoDB query.
I think you don't need to worry about the query and you can just use java script instead. Just iterate through the docs.upvotes array and compare its values with the array you provided.
var my_array = ["aa", "bdnvh", "563fa4408d88ea6c13c7abba", "djfufhgj", "5625bd9dbe545d2412d4ae62", "fjjshddhjfn", "djfhudh", "jfhshnffj", "sjdhfkskajdk"];
// ... your code
// inside of if(docs) statement
var matched_elements = [];
docs.upvotes.forEach(function(element, index, array) {
for (var i = 0; i < my_array.length; i++) {
if (my_array[i] == element) {
matched_elements.push(element);
}
}
});
console.log(matched_elements);
You could use the aggregation framework. First $unwind your upvotes array. Then $match on elements that are in the array provided by you. Than $group it back on _id(or whatever you want) creating "matched_elements" array with $addToSet or $push. The returned documents will have only upvotes elements (in "matched_elements array) that are also in your array.
var my_array = ["aa", "bdnvh", "563fa4408d88ea6c13c7abba", "djfufhgj", "5625bd9dbe545d2412d4ae62", "fjjshddhjfn", "djfhudh", "jfhshnffj", "sjdhfkskajdk"];
db.collection.aggregate( [
{ "$unwind" : "$upvotes" },
{ "$match": { "upvotes": {"$in": my_array} } },
{ "$group" : { "_id":"$_id","matched_elements":{ "$addToSet": "$upvotes" }}}
] );
I have users' collection whose schema is like:
{
_id: unique number,
name: 'asdf',
age: '12',
gender: 'm',
address: [
{area: 'sdf',
city: 'sdq',
state: 'wfw'},
{area: 'asdf',
city: 'sdfs',
state: 'vfdwd'}
]
}
I want to find out the users for whom all the values of state in address should be the value I pass. If even one of the state value doesn't match with the value I pass the user shouldn't be returned.
I tried simple find, aggregation framework with $unwind, $match but nothing seemed to get solution. Can you please help me out...
Thanks
P.S. please bear with multiple addresses for the sake of question. :)
To find out if all array entries match the state "wfw", do an aggregation like the following:
db.users.aggregate([
{ "$project" : {
"test" : {
"$allElementsTrue" : [{
"$map" : {
"input" : "$address",
"as" : "a",
"in" : { "$eq" : ["wfw", "$$a.state"] }
}
}]
}
} },
{ "$match" : { "test" : true } }
])
This aggregation takes each document, maps "state equals 'wfw'" over the address array to get a boolean array, and tests if the entire array is true, storing the result in `test, and then filtering the results based on test. You will need MongoDB 2.6 for support of some of the operators.
I don't know if I understand.
I replicated your document. When you want to retrieve an user by state you can do in many ways
If you search with single value you can do
db.g.find({ "address.state": "wfw" })
and retrieve an user
You can use $all
db.g.find( { "address.state": { $all: ["wfw","vfdwd"] } } ) // retrieve User
db.g.find( { "address.state": { $all: ["wfw","vfdwd","foo"] } } ) // don't retrieve User
or you can use $and
db.g.find( { $and: [ { "address.state":"wfw" },{ "address.state":"vfdwd" }] } )
But I don't know if I understand your question
Update and the correct answer
db.g.find( { "address.state": { $nin: ["wfw"] } } )
Let me Know
Brand new to MongoDB. I'm having difficulty querying by a property of an object nested in an array.
I have the following data structure:
{
"sales":[
{
"item":1234,
"seller":"SellerA",
"buyer":"BuyerA"
},
{
"item":5678,
"seller":"SellerB",
"buyer":"SellerB"
},
{
"item":1122,
"seller":"SellerA",
"buyer":"BuyerA"
},
{
"item":1199,
"seller":"SellerB",
"buyer":"SellerB"
}
]
}
I have other properties at the same level as the "sales" array, but I've simplified for the example.
I am attempting to pull every sales document which has a buyer value of "BuyerB."
I've tried the following:
db.data.find({"sales": {$elemMatch: {buyer: "BuyerB"}}})
db.data.find({"sales.buyer": "BuyerB"}})
These queries run, but return every row of data and not just the ones that have a buyer value of "BuyerB"
I know there has to be a simple answer, but what my searches have turned up is what I've already tried without success.
As you may return more than one element from an array and .find() cannot do this, you can try aggregate such as:
db.c.aggregate({
$match : {
"sales.buyer" : "BuyerB"
}
}, {
$unwind : "$sales"
}, {
$match : {
"sales.buyer" : "BuyerB"
}
}, {
$group : {
_id : "$_id",
sales : {
$push : "$sales"
}
}
});