Mongoose and limiting the results of field schema - mongodb

I have a collection called "Binders"
In this collection I have a field called "docs" that references a "Docs" schema
I want to pull a "Binder" by id with findOne() which is no problem.
But I want to limit the returned "Docs" by a field comparison
I am somewhat new to mongoose but if i had pseudo my query...
Binder.findOne({_id: req.binder._id}, {$where {docs.type = 'type1'}}, function(error, binder) {
// So this would return my full binder but only the docs where their .type = 'type1'
});
Where I get lost is the limiting based on a Schema
{$where {docs.type = 'type1'}}
So if I pulled a binder and it had 50 docs, but none of the docs were of type1 I would still get my binder info, however there would be no documents in the Docs field.
Hope that makes sense.

If I'm following you right and docs is an array, then you can use the $elemMatch projection operator to do this:
Binder.findOne({_id: req.binder._id}, {docs: {$elemMatch: {type: 'type1'}}}, ...

Related

DataStax Stargate Document API

What does the a JSON blob with search filters, allowed operators: $eq, $ne, $in, $nin, $gt, $lt, $gte, $lte, $exists in the Swagger documentation that is shown in the DataStax Document API Swagger UI, it's not that documented so I want to ask if the query string is based on MongoDB?
The Document API exposed on top of Cassandra is provided by the open source project Stargate, indeed developed by Datastax and embedded in their Saas solution Astra.
The JSON query String than you created is parsed and converted in a proper CQL query under the hood.
Source code doesn't lie you can find the full code here and specially parsing of the where clause here
public List<FilterCondition> convertToFilterOps(
List<PathSegment> prependedPath,
JsonNode filterJson) {
List<FilterCondition> conditions = new ArrayList<>();
if (!filterJson.isObject()) {
throw new DocumentAPIRequestException("Search was expecting a JSON object as input.");
}
ObjectNode input = (ObjectNode) filterJson;
Iterator<String> fields = input.fieldNames();
while (fields.hasNext()) {
String fieldName = fields.next();
if (fieldName.isEmpty()) {
throw new DocumentAPIRequestException(
"The field(s) you are searching for can't be the empty string!");
}
...
The query string is pretty similar in spirit to what you'd find with Mongo.
Here are some sample where clauses to give an idea:
{"name": {"$eq": "Eric"}} - simple enough, matches documents that have a field name with value Eric
{"a.age": {"$gt": 0}} - You can also reference nested fields in a document
{"friends.[0].name": {"$in": ["Cassandra"]}} - Array elements are referenced using [], this would match if the document's first friend is named Cassandra.
{"friends.*.age": {"$gte": 24}} - Wildcard * can be used to match any element in an array, or any field at a particular level of nesting. This matches any friend whose age is >= 24.

How to fetch just the "_id" field from MongoDB find()

I wish to return just the document id's from mongo that match a find() query.
I know I can pass an object to exclude or include in the result set, however I cannot find a way to just return the _id field.
My thought process is returning just this bit of information is going to be way more efficient (my use case requires no other document data just the ObjectId).
An example query that I expected to work was:
collection.find({}, { _id: 1 }).toArray(function(err, docs) {
...
}
However this returns the entire document and not just the _id field.
You just need to use a projection to find what ya want.
collection.find({filter criteria here}, {foo: 0, bar: 0, _id: 1});
Since I don't know what your document collection looks like this is all I can do for you. foo: 0 for example is exclude this property.
I found that using the cursor object directly I can specify the required projection. The mongodb package on npm when calling toArray() is returning the entire document regardless of the projection specified in the initial find(). Fixed working example below that satisfies my requirements of just getting the _id field.
Example document:
{
_id: new ObjectId(...),
test1: "hello",
test2: "world!"
}
Working Projection
var cursor = collection.find({});
cursor.project({
test1: 0,
test2: 0
});
cursor.toArray(function(err, docs) {
// Importantly the docs objects here only
// have the field _id
});
Because _id is by definition unique, you can use distinct to get an array of the _id values of all documents as:
collection.distinct('_id', function(err, ids) {
...
}
you can do like this
collection.find({},'_id').toArray(function(err, docs) {
...
}

Exact match on the embedded document when field order is not known

The doc says:
To specify an equality match on the whole embedded document, use the
query document { <field>: <value> } where <value> is the
document to match. Equality matches on an embedded document require an
exact match of the specified <value>, including the field order.
Using the dot notation is not a fully satisfying solution, as it also matches docs embedding docs with more fields than than required. And I may not know which are the other possible fields, making it impossible to explicitly exclude undesirable fields.
Querying each possible combination of fields is also not desired, as I do not want to write (#fields)! queries.
How then can I find a doc that contains an embedded doc when I do not care about the order of the fields?
For example, assume a collection of documents following this schema, but the order of the fields of inner are not known or may vary, depending on how/when they were inserted:
{
_id: ObjectId()
inner: {
some_field: some_value,
some_other_field: some_other_value
}
}
If I want to get all documents containing an inner object as {some_field: some_value, some_other_field: some_other_value}, I would want to
db.collection.find({inner:{some_field: some_value, some_other_field: some_other_value}})
but I will miss the docs with an inner object as {some_other_field: some_other_value, some_field: some_value}.
Using the dot notation like
db.collection.find({"inner.some_field": some_value, "inner.some_other_field": some_other_value})
will match all the documents I am interested in, but also include noise like
{
_id: ObjectId()
inner: {
some_field: some_value,
some_other_field: some_other_value,
some_undesired_field: some_undesired_value
}
}
One way of framing this problem is that you want those two fields (in any order) and no other fields in your result.
Dot notation solves the first part of your problem and you can solve the second part by making sure inner only has 2 fields.
db.collection.find({
"inner.some_field": some_value,
"inner.some_other_field": some_other_value,
$where: function() { return Object.keys(this.inner).length === 2 }
})
I believe this is the easiest approach. The drawback to this approach is that $where can be slow for reasons detailed here: Mongodb Query based on number of fields in a record
Try passing the document that you want to find. It works for me but the order of fields is important too.
db.collection.find({"inner": {"some_field": some_value, "some_other_field": some_other_value }}).pretty();
I just read that field order is unknown. So you have to use $or to list the possibilities such as:
db.collection.find({$or:[{"inner": {"some_field": some_value, "some_other_field": some_other_value }}, {"inner": {"some_other_field": some_other_value, "some_field": some_value }}]}).pretty();

spring data mongo - mongotemplate count with query hint

The mongo docs specify that you can specify a query hint for count queries using the following syntax:
db.orders.find(
{ ord_dt: { $gt: new Date('01/01/2012') }, status: "D" }
).hint( { status: 1 } ).count()
Can you do this using the mongo template? I have a Query object and am calling the withHint method. I then call mongoTemplate.count(query); However, I'm pretty sure it's not using the hint, though I'm not positive.
Sure, there are a few forms of this including going down to the basic driver, but assuming using your defined classes you can do:
Date date = new DateTime(2012,1,1,0,0).toDate();
Query query = new Query();
query.addCriteria(Criteria.where("ord_dt").gte(date));
query.addCriteria(Criteria.where("status").is("D"));
query.withHint("status_1");
long count = mongoOperation.count(query, Class);
So you basically build up a Query object and use that object passed to your operation, which is .count() in this case.
The "hint" here is the name of the index as a "string" name of the index to use on the collection. Probably something like "status_1" by default, but whatever the actual name is given.

Finding an Embedded Document by a specific property in Mongoose, Node.js, MongodDB

For this app, I'm using Node.js, MongoDB, Mongoose & Express
So I have a Param Object that contains an array of Pivots, and I want to read certain data from the pivots as outlined below
---in models.js-------------------------
var Pivot = new Schema({
value : String
, destination : String
, counter : Number
});
var Param = new Schema({
title : String
, desc : String
, pivots : [Pivot]
});
------------- in main.js --------------
var Param = db.model('Param');
app.get('/:title/:value', function(req, res){
Param.findOne({"title":req.param('title')}, function(err, record){
console.log(record.pivots);
record.pivots.find({"value":req.param('value')}, function(err, m_pivot){
pivot.counter++;
res.redirect(m_pivot.destination);
});
record.save();
});
});
I know that the code works until console.log(record.pivots), since i got a doc collection with the right pivot documents inside.
However, there does not seem to be a find method to let me match an embedded document by the 'value' property defined in the schema. Is it possible to search through this array of embedded documents using .find() or .findOne() , and if not, is there some easy way to access it through mongoose?
varunsrin,
This should do it
app.get('/:title/:value', function(req, res) {
Param.findOne({'pivots.value': req.param('value'), "title":req.param('title')}},
function(err, record) {
record.pivot.counter++;
res.redirect(m_pivot.destination);
record.save();
});
});
Note the pluralization of the query to match the field name in your schema
You can querying using embedded document properties like this:
{'pivot.value': req.param('value')}}
Update in response to comment:
app.get('/:title/:value', function(req, res) {
Param.findOne({'pivot.value': req.param('value'), "title":req.param('title')}},
function(err, record) {
record.pivot.counter++;
res.redirect(m_pivot.destination);
record.save();
});
});
I solved it temporarily using a simple for loop to parse the object array as follows:
for (var i=0; i <record.pivots.length; i++){
if (record.pivots[i].value == req.param('value')){
res.redirect(record.pivots.destination);
}
}
However, I still think that Mongoose must have a simpler way of interacting with embedded documents - and this loop is somewhat slow, especially when the number of embedded documents grows large.
If anyone has any suggestions for a faster way to search this object array either in js or with a mongoose function, please post below.
the biggest problem with this is that if your req has some fields empty (that should act as wildcard), you will not find anything since mongo tries to match empty params as well, so searching for {"user":"bob", "color":""} is not the same as {"user":"bob", "color":"red"} or {"user":"bob"}. this means that you have to first create a query object and filter out any unused parameters before you pass it in, and if you create a query object, you can no longer do something like "user.name=..." because mongo interperets this as an error since it does not first resolve the object literal into a string.
Any ideas on this problem?
ps. You'd think it would be easy enough to make an object like:
user.name="bob"; user.color:"green"; user.signup.time="12342561"
and then just use user as a query object :/
I think you are looking for the "$in" keyword?
As in:
{a: {$in: [10, "hello"]}}
source: MongoDB Queries CheatSheet