Search multiple attributes in Mongo with Meteor - javascript

I've been able to implement a publish method in Meteor that runs a query to my mongo collection through a given attribute when subscribing in the template.js, which works fine but now I'd like to add a multiple attribute search in the same manner. So let's say I have a collection in Mongo where the documents all have the same attributes but with differing values.
{batch:'HAHT020614' color: 'blue', material: 'plastic', printing: true,
model: 'H100', handle: 'plastic', product: 'C010' }
{batch:'HBTH060614' color: 'red', material: 'metal', printing: false,
model: 'V400', handle: 'metal', product: 'P001' }
...
I'm trying to send an object to the publish method which contains all the user chosen fields through reactive vars:
Template.inventory.onCreated( function appBodyOnCreated() {
this.searchQuery = new ReactiveVar({
color: anyItem,
batch: anyItem,
model: anyItem,
material: anyItem,
handle: anyItem,
printing: anyItem,
product: anyItem,
});
this.autorun(function () {
let template = Template.instance();
template.subscribe("stock.search", template.searchQuery.get());
});
});
Then in publication.js:
Meteor.publish('stock.search', function stockQuery(search) {
return Stock.find(
{ $and: [
{color: { $regex : search.color }},
{batch: { $regex : search.batch}},
{product: { $regex : search.product}},
{model: { $regex : search.model}},
{material: { $regex : search.material}},
{handle: { $regex : search.handle}},
{printing: { $regex : search.printing}}
]
},
{ limit: 10, sort: { batch: 1 } });
});
The problem is that some of the search fields will or not be used in the application depending on the user's needs, looking for it to be possible to search all items that are for example both blue and made or metal, and just mix and match whatever is necessary to find.
The object reaches the publish method correctly and I'm able to extract the attributes but the problem resides in the query, as I don't know if it's possible to ask for Mongo to match certain attributes to "any". I've tried to pass on { $exists: true } as a default (and when search field empty) attribute so it matches with any of the documents in the collection but the query doesn't seem to be returning correctly. In this case I'm using regex as some sort of "contains" while the var anyItem is just an empty string.
Is there a proper way to query mongo to match only certain attributes to chosen values while the others stay as "any"?

You could pass only the non-null criteria to the publish method, and build a query with only the given criteria like this:
Meteor.publish('stock.search', function stockQuery(search) {
const criteria = Object.keys(search).map(k => ({ [k]: { $regex: search[k] } }));
return Stock.find(
{ $and: criteria },
{ limit: 10, sort: { batch: 1 } }
);
});

Related

Filter the populated data and then paginate in Mongodb

Hello I am trying to populate the data and then trying to paginate that data.
Here is the example
Schema A (Users)
{
name: 'Demo',
postId: 'someObjectId',
}
Schema B (Posts)
{
id: 'someObjectId',
postName: 'Post 1',
date: 'date of creation'
}
Here is my code
const users = UserModel.find({
name: 'Demo'
}).populate({
path: 'postId',
select: 'date',
match: {'postId.date' : {$lt: 'today'}}
}).page(pagination).limit(20)
Not getting the result needed. Can someone point out what's wrong?
NOTE: I have just given the overview. Please don't take it as real code. I know I haven't written what we would write in javascript
A populate have following things:
Post.find({})
.populate([
// here array is for our memory.
// because may need to populate multiple things
{
path: 'user',
select: 'name',
model:'User',
options: {
sort:{ },
skip: 5,
limit : 10
},
match:{
// filter result in case of multiple result in populate
// may not useful in this case
}
}
]);
.exec((err, results)=>{
console.log(err, results)
});

How to filter only some rows of an array inside a document?

I have a MongoDB collection of documents, using a schema like this:
var schema = new Schema({
name: String,
images: [{
uri: string,
active: Boolean
}]
});
I'd like to get all documents (or filter using some criteria), but retrieve - in the images array - only the items with a specific property (in my case, it's {active: true}).
This is what I do now:
db.people.find( { 'images.active': true } )
But this retrieves only documents with at least one image which is active, which is not what I need.
I know of course I can filter in code after the find is returned, but I do not like wasting memory.
Is there a way I can filter array items in a document using mongoose?
Here is the aggregation you're looking for:
db.collection.aggregate([
{
$match: {}
},
{
$project: {
name: true,
images: {
$filter: {
input: "$images",
as: "images",
cond: {
$eq: [
"$$images.active",
true
]
}
}
}
}
}
])
https://mongoplayground.net/p/t_VxjfiBBMK

Retrive some columns of relations in typeorm

I need to retrieve just some columns of relations in typeorm query.
I have an entity Environment that has an relation with Document, I want select environment with just url of document, how to do this in typeorm findOne/findAndCount methods?
To do that you have to use a querybuilder, here's an example:
return this.createQueryBuilder('environment') // use this if the query used inside of your entity's repository or getRepository(Environment)...
.select(["environment.id","environment.xx","environment.xx","document.url"])
.leftJoin("environment.document", "document")
.where("environment.id = :id ", { id: id })
.getOne();
Sorry I can't add comment to post above. If you by not parsed data mean something like "environment.id" instead of "id"
try this:
return this.createQueryBuilder("environment")
.getRepository(Environment)
.select([
"environment.id AS id",
"environment.xx AS xx",
"document.url AS url",
])
.leftJoin("environment.document", "document")
.where("environment.id = :id ", { id: id })
.getRawOne();
Here is the code that works for me, and it doesn't require using the QueryBuilder. I'm using the EntityManager approach, so assuming you have one of those from an existing DataSource, try this:
const environment = await this.entityManager.findOne(Environment, {
select: {
document: {
url: true,
}
},
relations: {
document: true
},
where: {
id: environmentId
},
});
Even though the Environment attributes are not specified in the select clause, my experience is that they are all returned in the results, along with document.url.
In one of the applications that I'm working on, I have the need to bring back attributes from doubled-nested relationships, and I've gotten that to work in a similar way, shown below.
Assuming an object model where an Episode has many CareTeamMembers, and each CareTeamMember has a User, something like the code below will fetch all episodes (all attributes) along with the first and last name of the associated Users:
const episodes = await this.entityManager.find(Episode, {
select: {
careTeamMembers: {
id: true, // Required for this to work
user: {
id: true,
firstName: true,
lastName: true,
},
}
},
relations: {
careTeamMembers: {
user: true,
}
},
where: {
deleted: false,
},
});
For some reason, I have to include at least one attribute from the CareTeamMembers entity itself (I'm using the id) for this approach to work.

Add to an array - sub-document without duplicate field values

I am trying to add an object to an array in MongoDB. I don't want it to be duplicated.
I am trying to update the user read array by using $addToset in findOneAndUpdate. However, it is inserting duplicate because of timestamp; the timestamp is an important property. I can't negate it. Can I insert based on key like userId? Please let me know.
{
_id: 'ddeecd8b-79b5-437d-9026-d0663b53ad8d',
message: 'hello world notification',
deliverToUsersList: [ '123-xxx-xx', '124-xxx-xx']
userRead: [
{
isOpened: true,
userId: '123-xxx-xx'
updatedOn: new Date(Date.now()).toISOString()
},
{
isOpened: true,
userId: '124-xxx-xx'
updatedOn: new Date(Date.now()).toISOString()
}
]
}
Add an index to the field userId and enable 'Avoid duplicates' in index settings.
I use Robo3T client to do that.
To add new objects without duplicate information into the userRead array, you have check for the duplicate information in the update method's query filter. For example, the following code will not allow adding new object with duplicate userId field value.
new_userId = "999-xxx-xx"
new_doc = { userId: new_userId, isOpened: true, updatedOn: ISODate() }
db.test_coll.findOneAndUpdate(
{ _id: 'ddeecd8b-79b5-437d-9026-d0663b53ad8d', "userRead.userId": { $ne: new_userId } },
{ $push: { "userRead" : new_doc } },
)

How to get result with specific fields in StrongLoop?

I am currently using StrongLoop as my API backend server and Mongodb as data storage engine.
Let's say there is a collection called article. It has two fields title, and content. And there are two frontend pages to display a list of articles and view a single article.
Obviously the data list page only need title field and the view page need both. Currently the GET method of StrongLoop API return all fields including content. It cost extra traffic. Is there any way that can just return specific field?
Mongodb support projection in find() method for this. How can I do the same thing by StrongLoop?
Have you taken a look at the filters offered. http://docs.strongloop.com/display/LB/Querying+models
Query for NodeAPI:
server.models.Student.findOne({where: {RFID: id},fields: {id: true,schoolId: true,classId: true}}, function (err, data) {
if (err)
callback(err);
else {
callback();
}
})
Query for RestAPI :
$http.get('http://localhost:3000/api/services?filter[fields][id]=true&filter[fields][make]=true&filter[fields][model]=true')
.then(function (response) {
}, function (error) {
});
You can use fields projections,
Sample Record:
{ name: 'Something', title: 'mr', description: 'some desc', patient: { name: 'Asvf', age: 20, address: { street: 1 }}}
First Level Projection:
model.find({ fields: { name: 1, description: 1, title: 0 } })
and I think Strong loop is not yet supporting for second-level object filter, does anyone know how to filter second-level object properties or is yet to implement?.
Second Level Projection: (Need help here)
Ex: 2
model.find({ fields: { name: 1, 'patient.name': 1, 'patient.age': 1, 'patient.address': 0 } })
// Which results { name } only

Categories