Find by sub-documents field value with case insensitive - mongodb

I know this is a bit of newb question but I'm having a hard time figuring out how to write a query to find some information. I have several documents (or orders) much like the one below and I am trying to see if there is any athlete with the name I place in my query.
How do I write a query to find all records where the athleteLastName = Doe (without case sensitivity)?
{
"_id" : ObjectId("57c9c885950f57b535892433"),
"userId" : "57c9c74a0b61b62f7e071e42",
"orderId" : "1000DX",
"updateAt" : ISODate("2016-09-02T18:44:21.656Z"),
"createAt" : ISODate("2016-09-02T18:44:21.656Z"),
"paymentsPlan" :
[
{
"_id" : ObjectId("57c9c885950f57b535892432"),
"customInfo" :
{
"formData" :
{
"athleteLastName" : "Doe",
"athleteFirstName" : "John",
"selectAttribute" : ""
}
}
}
]
}

You need to use dot notation to access the embedded documents and regex because you want case insensitive.
db.collection.find({'paymentsPlan.customInfo.formData.athleteLastName': /Doe/i}

Related

MongoDB .Net driver 2.0 Builders Filter (field to array comparison)

I need to get all usernames from "followingList.username" and compare with
posts' usernames, if there any match need to add that one to an array.
Person Model
{
"_id" : ObjectId("554f20f5c90d3c7ed42303e1"),
"username" : "fatihyildizhan",
"followingList" : [
{
"_id" : ObjectId("55505b6ca515860cbcf7901d"),
"username" : "gumusluk",
"avatar" : "avatar.png"
},
{
"_id" : ObjectId("58505b6ca515860cbcf7901d"),
"username" : "yalikavak",
"avatar" : "avatar.png"
},
{
"_id" : ObjectId("58305b6ca515860cbcf7901d"),
"username" : "gumbet",
"avatar" : "avatar.png"
}
]
}
Post Model
{
"_id" : ObjectId("554f2df2a388R4b425b89833"),
"username" : "yalikavak",
"category" : "Summer",
"text" : "blue voyage with yacht"
},
{
"_id" : ObjectId("554f2df2a388P4b425b89833"),
"username" : "yalikavak",
"category" : "Winter",
"text" : "is coming ..."
},
{
"_id" : ObjectId("554f2df2a388K4b425b89833"),
"username" : "gumbet",
"category" : "Fall",
"text" : "there are many trees"
}
I try to get result code block as below but couldn't figure it out.
var filter = Builders<Post>.Filter.AnyEq("username", usernameList);
var result = collection.Find(filter).ToListAsync().Result;
Can you help me with this?
Thanks.
The logic is flipped, what you need is an $in query if I understand your use case correctly:
var filter = Builders<Post>.Filter.In("username", usernameList);
var result = collection.Find(filter).ToListAsync().Result;
In your case, username is a simple field and you want to match against a list of candidates. AnyEq is used to check that, from an embedded list of complex objects, at least one matches a criterion. That still translates to a simple query in MongoDB, but requires to 'reach into' the object which needs a more complicated syntax.

Resolving MongoDB DBRef array using Mongo Native Query and working on the resolved documents

My MongoDB collection is made up of 2 main collections :
1) Maps
{
"_id" : ObjectId("542489232436657966204394"),
"fileName" : "importFile1.json",
"territories" : [
{
"$ref" : "territories",
"$id" : ObjectId("5424892224366579662042e9")
},
{
"$ref" : "territories",
"$id" : ObjectId("5424892224366579662042ea")
}
]
},
{
"_id" : ObjectId("542489262436657966204398"),
"fileName" : "importFile2.json",
"territories" : [
{
"$ref" : "territories",
"$id" : ObjectId("542489232436657966204395")
}
],
"uploadDate" : ISODate("2012-08-22T09:06:40.000Z")
}
2) Territories, which are referenced in "Map" objects :
{
"_id" : ObjectId("5424892224366579662042e9"),
"name" : "Afghanistan",
"area" : 653958
},
{
"_id" : ObjectId("5424892224366579662042ea"),
"name" : "Angola",
"area" : 1252651
},
{
"_id" : ObjectId("542489232436657966204395"),
"name" : "Unknown",
"area" : 0
}
My objective is to list every map with their cumulative area and number of territories. I am trying the following query :
db.maps.aggregate(
{'$unwind':'$territories'},
{'$group':{
'_id':'$fileName',
'numberOf': {'$sum': '$territories.name'},
'locatedArea':{'$sum':'$territories.area'}
}
})
However the results show 0 for each of these values :
{
"result" : [
{
"_id" : "importFile2.json",
"numberOf" : 0,
"locatedArea" : 0
},
{
"_id" : "importFile1.json",
"numberOf" : 0,
"locatedArea" : 0
}
],
"ok" : 1
}
I probably did something wrong when trying to access to the member variables of Territory (name and area), but I couldn't find an example of such a case in the Mongo doc. area is stored as an integer, and name as a string.
I probably did something wrong when trying to access to the member variables of Territory (name and area), but I couldn't find an example
of such a case in the Mongo doc. area is stored as an integer, and
name as a string.
Yes indeed, the field "territories" has an array of database references and not the actual documents. DBRefs are objects that contain information with which we can locate the actual documents.
In the above example, you can clearly see this, fire the below mongo query:
db.maps.find({"_id":ObjectId("542489232436657966204394")}).forEach(function(do
c){print(doc.territories[0]);})
it will print the DBRef object rather than the document itself:
o/p: DBRef("territories", ObjectId("5424892224366579662042e9"))
so, '$sum': '$territories.name','$sum': '$territories.area' would show you '0' since there are no fields such as name or area.
So you need to resolve this reference to a document before doing something like $territories.name
To achieve what you want, you can make use of the map() function, since aggregation nor Map-reduce support sub queries, and you already have a self-contained map document, with references to its territories.
Steps to achieve:
a) get each map
b) resolve the `DBRef`.
c) calculate the total area, and the number of territories.
d) make and return the desired structure.
Mongo shell script:
db.maps.find().map(function(doc) {
var territory_refs = doc.territories.map(function(terr_ref) {
refName = terr_ref.$ref;
return terr_ref.$id;
});
var areaSum = 0;
db.refName.find({
"_id" : {
$in : territory_refs
}
}).forEach(function(i) {
areaSum += i.area;
});
return {
"id" : doc.fileName,
"noOfTerritories" : territory_refs.length,
"areaSum" : areaSum
};
})
o/p:
[
{
"id" : "importFile1.json",
"noOfTerritories" : 2,
"areaSum" : 1906609
},
{
"id" : "importFile2.json",
"noOfTerritories" : 1,
"areaSum" : 0
}
]
Map-Reduce functions should not be and cannot be used to resolve DBRefs in the server side.
See what the documentation has to say:
The map function should not access the database for any reason.
The map function should be pure, or have no impact outside of the
function (i.e. side effects.)
The reduce function should not access the database, even to perform
read operations. The reduce function should not affect the outside
system.
Moreover, a reduce function even if used(which can never work anyway) will never be called for your problem, since a group w.r.t "fileName" or "ObjectId" would always have only one document, in your dataset.
MongoDB will not call the reduce function for a key that has only a
single value

Filtering Mongo items by multiple fields and subfields

I have the following items in my collection:
> db.test.find().pretty()
{ "_id" : ObjectId("532c471a90bc7707609a3d4f"), "name" : "Alice" }
{
"_id" : ObjectId("532c472490bc7707609a3d50"),
"name" : "Bob",
"partner_type1" : {
"status" : "rejected"
}
}
{
"_id" : ObjectId("532c473e90bc7707609a3d51"),
"name" : "Carol",
"partner_type2" : {
"status" : "accepted"
}
}
{
"_id" : ObjectId("532c475790bc7707609a3d52"),
"name" : "Dave",
"partner_type1" : {
"status" : "pending"
}
}
There are two partner types: partner_type1 and partner_type2. A user cannot be accepted partner in the both of types. But he can be a rejected partner in partner_type1 but accepted in the another, for example.
How can I build Mongo query that fetches the users that can become partners?
When your user can only be accepted in one partner-type, you should turn it around: Have a field accepted_as:"partner_type1" or accepted_as:"partner_type2". For people who aren't accepted yet, either have no such field or set it to null.
In both cases, your query to get any non-accepted will then be:
{
data.accepted_as: null
}
(null matches both non-existing fields as well as fields explicitly set to null)
For me the logical schema would be this:
"partner : {
"type": 1,
"status" : "rejected"
}
At least that keeps the paths consistent between documents.
So if you want to stay away from using mapReduce type methods to find out "which field" it is on, and otherwise use plain queries and the aggregation pipeline, then don't vary field paths on documents. If you alter the "data" then that is the most consistent form.

indexing vs normalization when optimizing for read speed

Having an architecture discussion with a coworker and we need to find an answer for this. Given a set of millions of data points that look like:
data =
[{
"v" : 1.44,
"tags" : {
"account" : {
"v" : "1055",
"name" : "Circle K"
}
"region" : "IL-East"
}
}, {
"v" : 2.25,
"tags" : {
"account" : {
"v" : "1055",
"name" : "Circle K"
}
"region" : "IL-West"
}
}]
and that we need to query on the fields in the tags collection (e.g. where account.name == "Circle K"), would there be any speed benefit to normalizing the account field to this:
accounts =
[{
_id : 507f1f77bcf86cd799439011,
v: "1055",
name : "Circle K"
}]
data =
[{
"v" : 1.44,
"tags" : {
"account" : 507f1f77bcf86cd799439011
"region" : "IL-East"
}
}, {
"v" : 2.25,
"tags" : {
"account" : 507f1f77bcf86cd799439011
"region" : "IL-West"
}
}]
I suspect I'll have to build 2 db's for this and just see what the speed looks like. The question is, is mongo better at querying on BSON IDs vs. strings? The db in question will be about 1:10 write vs. read.
The most important thing here is to make sure that you have enough RAM for your working set. That includes the space for the "tags.account.name" index and the expected query result set.
As for the key size. You use ObjectID-as-string above, which you should not do. Leave the real ObjectIDs in as their size is quite a bit smaller. If you really have a lot of small documents, then you might even want to think about shorting your field names as well.

Find one document in mongodb with a preference toward "Starts With"

I have a mongo database of names.
Let's say it looks like this:
{ "_id" : ObjectId("513a18c1f9e9b5c19fd80014"), "name" : "Mary Sue" }
{ "_id" : ObjectId("513a18d9f9e9b5c19fd80015"), "name" : "Tammy Sue" }
{ "_id" : ObjectId("513a18e4f9e9b5c19fd80016"), "name" : "Sueellen" }
{ "_id" : ObjectId("513a18eaf9e9b5c19fd80017"), "name" : "Ellen" }
{ "_id" : ObjectId("513a195af9e9b5c19fd80018"), "name" : "Sue" }
{ "_id" : ObjectId("513a1ccaf9e9b5c19fd80019"), "name" : "Eddie" }
I would like to be able to perform a (case-insensitive) query for a single result which will prioritize the return value like so:
If "name" starts with my string, then return the first alphabetical "starts with" result.
Otherwise, if name contains my string, then return the first alphabetical result.
Examples:
A search for /sue/i should return "Sue".
A search for /e/i should return "Eddie".
A search for /len/i should return "Ellen".
A search for /ue/i should return "Mary Sue".
Is it possible to do this without either doing 2 separate calls (one for /^len/i, then for /len/i if I got 0 results), or finding every match and parsing the results myself?
I happen to be using node.js and mongoose here, but a generic mongo answer would also be fine so I can understand the concepts.
This is just a matter of the right regexp. You can specify multiple matches in a regexp. It's worth having a look at http://regex.learncodethehardway.org/book/ and get a deeper understanding of the regexp. OR download 2.4 from www.mongodb.org and try out the new text index option
http://docs.mongodb.org/manual/release-notes/2.4/#text-indexes