In mongodb i saved document like this.
"Angela_Merkel": {
"birthPlace": "Hamburg,West_Germany",
"thumbnail": "http://commons.wikimedia.org/wiki/Special:FilePath/Angela-Merkel-2014.jpg?width=300",
"almaMater": "Leipzig_University",
"birthDate": "1954-07-17",
"spouse": "Joachim_Sauer"
}
There are many person's information like this way. Now if I want to get all the information of "Angela_Merkel" or only a particular like "birthdDate" of "Angela_Merkel" then what will be the query?
Like chridam says would be more practical that you refactor your documents like this:
{"name": "Angela_Merkel",
"birthPlace": "Hamburg,West_Germany",
"thumbnail": "http://commons.wikimedia.org/wiki/Special:FilePath/Angela-Merkel-2014.jpg?width=300",
"almaMater": "Leipzig_University",
"birthDate": "1954-07-17",
"spouse": "Joachim_Sauer"
}
Inside the "people" collection (Its a convention name the collections as plurals, beeing a set of documents)
Then you could do:
db.people.find({"name":"Angela Merkel"},{"_id":0," "birthdDate":1 })
To get:
{"birthDate": "1954-07-17"}
Anyway you can get with your document what you want this way:
"birthDate":
db.person.find({"Angela_Merkel.birthDate": "1954-07-17"})
or all the info:
db.person.find({"Angela_Merkel": { $exists: true}})
But doesn't have much sense do it this way.
PD: Edited to include last part.
Related
I have a mongo collection where a field is supposed to point to another document's id in the same collection, but instead it is pointing to its "number". I need to perform an update on them but I'm having some problems on forming the query. Could you help me?
The structure of the document is like this:
{
"_id": "269410e2-cebf-40f1-a81f-fdce34185cdc",
"number": 1471,
"alternativeLocationId": "9871",
"locationType": "DUMMY"
},
{
"_id": "2945b24a-b82f-45a9-ad06-a884379b5597",
"number": 9871,
"locationType": "MAIN"
}
So as asked, I'd need to make document with number 1471 "alternativeLocationId" to be "2945b24a-b82f-45a9-ad06-a884379b5597" instead of 9871 (Note that the referenced documents are not locationType "DUMMY" nor have this alternativeLocationId field).
The query I've done so far goes like this, but when executed its not doing any changes:
db.location.find({alternativeLocationId: {$exists:true}}).forEach(
function (loc) {
var correctLocation = db.location.findOne({number: loc.alternativeLocationId});
db.location.update(
{_id: loc._id},
{$set: {alternativeLocationId: correctLocation._id} }
);
}
);
As mentioned by user20042973 the issue was the type mismatch between alternativeLocationId and locationNumber, after converting the value to int when looking for it, it works perfectly.
I'm trying to use the Model.deleteMany() function from mongoose. I'm trying to do something like this:
MyModel.deleteMany({"_id": {$in: ['objectid 1', 'objectid 2'
But this only deletes the first element of the matches from DB (in this case, if 'objectid 1' exists in the DB, it deletes that, but if it isn't nothing happens (both n and deletedCount is 0 in the returned data from the function). I tried using something other than the _id as a query, and this worked. If I had three elements with the same 'name' field, I could delete these.
I tried _id both with and without quotation marks. I also tried converting the object id strings to actual object ids before passing them to deleteMany, but this had no difference either. I have also of course tried to google this, but everything I've found are examples of usage, where it looks like I'm doing the exact same thing as the various blog posts.
I haven't added much code here because I don't really see what else I could be adding. I'm already printing out the input to the $in object, and this is correct. The strangest thing, I think, is that the first element of the list is deleted. Is it treated as a deleteOne request for some reason? are there any config options I need?
As per request, I've added the query and the documents I'd hope to delete:
//Request
MemberModel.deleteMany({"_id": {$in: [
5ee4f6308631dc413c7f04b4,
5ee4f6308631dc413c7f04b5,
5ee4f6308631dc413c7f04b6
]}};
//Expected to be deleted
[
{
"_id": "5ee4f62f8631dc413c7f04b5",
"firstName": "Name",
"lastName": "Nameson",
"__v": 0
},
{
"_id": "5ee4f62f8631dc413c7f04b6",
"firstName": "Other",
"lastName": "Person",
"__v": 0
}
]
If you have any ideas for what I could try, that would be much appreciated.
I have different types of data that would be difficult to model and scale with a relational database (e.g., a product type)
I'm interested in using Mongodb to solve this problem.
I am referencing the documentation at mongodb's website:
http://docs.mongodb.org/manual/tutorial/model-referenced-one-to-many-relationships-between-documents/
For the data type that I am storing, I need to also maintain a relational list of id's where this particular product is available (e.g., store location id's).
In their example regarding "one-to-many relationships with embedded documents", they have the following:
{
name: "O'Reilly Media",
founded: 1980,
location: "CA",
books: [12346789, 234567890, ...]
}
I am currently importing the data with a spreadsheet, and want to use a batchInsert.
To avoid duplicates, I assume that:
1) I need to do an ensure index on the ID, and ignore errors on the insert?
2) Do I then need to loop through all the ID's to insert a new related ID to the books?
Your question could possibly be defined a little better, but let's consider the case that you have rows in a spreadsheet or other source that are all de-normalized in some way. So in a JSON representation the rows would be something like this:
{
"publisher": "O'Reilly Media",
"founded": 1980,
"location": "CA",
"book": 12346789
},
{
"publisher": "O'Reilly Media",
"founded": 1980,
"location": "CA",
"book": 234567890
}
So in order to get those sort of row results into the structure you wanted, one way to do this would be using the "upsert" functionality of the .update() method:
So assuming you have some way of looping the input values and they are identified with some structure then an analog to this would be something like:
books.forEach(function(book) {
db.publishers.update(
{
"name": book.publisher
},
{
"$setOnInsert": {
"founded": book.founded,
"location": book.location,
},
"$addToSet": { "books": book.book }
},
{ "upsert": true }
);
})
This essentially simplified the code so that MongoDB is doing all of the data collection work for you. So where the "name" of the publisher is considered to be unique, what the statement does is first search for a document in the collection that matches the query condition given, as the "name".
In the case where that document is not found, then a new document is inserted. So either the database or driver will take care of creating the new _id value for this document and your "condition" is also automatically inserted to the new document since it was an implied value that should exist.
The usage of the $setOnInsert operator is to say that those fields will only be set when a new document is created. The final part uses $addToSet in order to "push" the book values that have not already been found into the "books" array (or set).
The reason for the separation is for when a document is actually found to exist with the specified "publisher" name. In this case, all of the fields under the $setOnInsert will be ignored as they should already be in the document. So only the $addToSet operation is processed and sent to the server in order to add the new entry to the "books" array (set) and where it does not already exist.
So that would be simplified logic compared to aggregating the new records in code before sending a new insert operation. However it is not very "batch" like as you are still performing some operation to the server for each row.
This is fixed in MongoDB version 2.6 and above as there is now the ability to do "batch" updates. So with a similar analog:
var batch = [];
books.forEach(function(book) {
batch.push({
"q": { "name": book.publisher },
"u": {
"$setOnInsert": {
"founded": book.founded,
"location": book.location,
},
"$addToSet": { "books": book.book }
},
"upsert": true
});
if ( ( batch.length % 500 ) == 0 ) {
db.runCommand( "update", "updates": batch );
batch = [];
}
});
db.runCommand( "update", "updates": batch );
So what is doing in setting up all of the constructed update statements into a single call to the server with a sensible size of operations sent in the batch, in this case once every 500 items processed. The actual limit is the BSON document maximum of 16MB so this can be altered appropriate to your data.
If your MongoDB version is lower than 2.6 then you either use the first form or do something similar to the second form using the existing batch insert functionality. But if you choose to insert then you need to do all the pre-aggregation work within your code.
All of the methods are of course supported with the PHP driver, so it is just a matter of adapting this to your actual code and which course you want to take.
tricky mongodb querying problem: I have a collection 'Accounts' with a bunch of documents that (simplified and with actual data swapped for non-real-world values) look like this:
{"_id": "<Mongo Binary Data>",
"Roles": {
"D7753879C7020F8ECF947122FA211413": {
"_id": "<Mongo Binary Data>",
"OrgName": "ACME",
"Rolename": "Coyote Liaison",
},
"CFA7722E6799170706E4C5FFF3F01E63": {
"_id": "<Mongo Binary Data>",
"OrgName": "ACME",
"Rolename": "MembershipAdmin",
},
"C7020F8ECF947122FAGIGHFVFF3F7753": {
"_id": "<Mongo Binary Data>",
"OrgName": "Initech",
"Rolename": "MembershipAdmin",
}
}
}
The keys in the Roles array are a combination of a role id and an org id which are then hashed, which makes it very quick to query (once an Account object has been loaded up from MongoDB and into C#) for if an Account has a given role for a given org, ie, is a user a MembershipAdmin for Initech.
Now I want to query for users who have a role for ANY org, which in pseudo-SQL could be expressed as 'SELECT all accounts which have at least one role object where Rolename = THISROLENAME'. Ie, get all users which are MembershipAdmins.
I have tried this:
{
Roles.Rolename: "MembershipAdmin"
}
and this
{
Roles: {"Rolename": "MembershipAdmin"}
}
and this
{
Roles: {"$elemMatch": {"Rolename": "MembershipAdmin"}}
}
...to no avail and have seen several answers, etc, saying that the only way out of this is to push the associative array key down into the subobjects, which I don't want to do as it makes the primary function of this data structure (checking if an account has a given role for a given organisation) very quick indeed. The use case I describe above doesn't have to be mega-fast as it is part of an admin user's responsibility so I'm happy to make them wait a few moments - so queries with excessive recursion, etc, are OK in this case.
Does anyone have any ideas how to make this work without refactoring the data structure? At my wit's end with this.
Many thanks,
G
[EDIT: the structure above is not queryable, see the accepted answer for an erudite but quick explanation as to why not and what you should properly do to fix it. If you're OK with a hacky workaround, though, you can store a copy of the data in a BsonArray alongside it and query against that using $elemMatch]
There is no way to do with you want with that schema, nor is the schema very practical to begin with. You should change your schema to this :
{"_id": "<Mongo Binary Data>",
"Roles": [
{
"hash": "D7753879C7020F8ECF947122FA211413",
"_id": "<Mongo Binary Data>",
"OrgName": "ACME",
"Rolename": "Coyote Liaison",
},
{
"hash": "CFA7722E6799170706E4C5FFF3F01E63",
"_id": "<Mongo Binary Data>",
"OrgName": "ACME",
"Rolename": "MembershipAdmin",
},
{
"hash": "C7020F8ECF947122FAGIGHFVFF3F7753",
"_id": "<Mongo Binary Data>",
"OrgName": "Initech",
"Rolename": "MembershipAdmin",
}
]
}
The reason why using nonstable values as document field names is so unpractical is exactly because it complicates most querying. The only upside is that some queries might be a little faster but given the fact that it also causes issues for indexing it's almost always a bad idea.
I would strongly suggest to change your schema to the above rather than look for solutions that allow your current schema.
EDIT: As mentioned in the discussion below it is technically possible to create the needed query using the $where operator. If you cannot do it any other way it means you have a schema smell and a potential scaling and performance bottleneck that will be diabolically hard to fix when your software goes live. Don't use $where. Ever.
[EDIT]: This is wrong. Works if it were an array and not a hash structure. That, anyway was already tried in the question.
Roles.Rolename can be used if there is an index on it.
db.collectionname.ensureIndex({"Roles.Rolename": 1 })
and then,
db.collectionname.find({"Role.Rolename": "MembershipAdmin"})
# returning only the organization field. Not tested though
db.collectionname.find({"Role.Rolename": "MembershipAdmin"},{"Role.Orgname": 1})
Indexing Embedded fields from MongoDB docs has info on it.
Here is how you do it:
db.Accounts.find({
$where: function () {
for (var index in this.Roles)
if (this.Roles[index].Rolename == "THISROLENAME")
return this;
}
});
Note: I'm not sure why they designed regular MongoDB queries to not work if each of your embedded documents in the array has its own key, but hopefully in the future they'll fix that issue.
In the meantime this solution works great. I've tested it and it seems pretty fast.
Can anyone explain to me why the following doesn't work:
Assuming the following document structure:
{
"_id": "520fb089a6cb538b1843cdf3cca39a15",
"_rev": "2-f96c27d19bf6cb10268d6d1c34799931",
"type": "nosql",
"location": "AZ",
"date": "2012/03/01 00:00:00",
"amount": 1500
}
And a Map function defined like so:
function(doc) {
var saleDate = new Date(doc.date);
emit([doc.location,saleDate.getFullYear(),saleDate.getMonth()+1],doc.amount);
}
And using the built in _sum function for the reducer.
When you execute this (with group=true) you get results like this:
{"rows":[
{"key":["AZ",2012,2],"value":224},
{"key":["AZ",2012,3],"value":1500},
{"key":["WA",2011,12],"value":1965},
{"key":["WA",2012,1],"value":358}
]}
Now if you change the query to something like this:
http://127.0.0.1:5984/default/_design/nosql/_view/nosql_test?group_level=2
You get results like this:
{"rows":[
{"key":["AZ",2012],"value":1724},
{"key":["WA",2011],"value":1965},
{"key":["WA",2012],"value":358}
]}
So with that in mind if I wanted to find out all sales in 2011 for "WA" could I not execute something like this:
http://127.0.0.1:5984/default/_design/nosql/_view/nosql_test?group_level=2&key=["WA",2011]
This example has been taken from the useful videos over at NoSQL tapes.
http://nosqltapes.com/video/understanding-mapreduce-with-mike-miller
You always need to give a range of keys, because filtering is done on map's results, not on reduce.
For example, the following parameters should work (if properly url-encoded):
?group_level=2&startkey=["WA",2011]&endkey=["WA",2011,{}]
You can read about view collation to understand how it works.