Mongoose find document by key whose value is a complex object - node.js

I am using Mongoose to do a search for documents in a collection based on the criteria described below:
Document:
{
"_id": {
"$oid": "5a60621e20205641281f7c2f"
},
"key1": [
{
"available": true,
"required": true,
"name": "Name-1"
},
{
"available": true,
"required": true,
"name": "Name-2"
},
{
"available": true,
"required": true,
"name": "Name-3"
}
],
"__v": 0
}
I want to perform a search based on property key1. So basically what I want to do is pass the json object as search pattern below and get the result as the document above in return
[
{
"available": true,
"required": true,
"name": "Name-1"
},
{
"available": true,
"required": true,
"name": "Name-2"
},
{
"available": true,
"required": true,
"name": "Name-3"
}
]
Is there a way that I can achieve this?

You can check $eq in mongodb docs $eq.
const selector = [
{
"available": true,
"required": true,
"name": "Name-1"
},
{
"available": true,
"required": true,
"name": "Name-2"
},
{
"available": true,
"required": true,
"name": "Name-3"
}
];
Model.find({key1: {$eq: selector}}, (error, result) => {
if(!err) {
res.send(result);
}
});
Model is the collection you fetch from.

Related

Filtering In mongoose aggregation

I have a listing object with filters. I need to create an aggregation to filter the listings based on filters.
This is a listing object example
[
{
"_id": "5c484ec4cb1e150b1efce101",
"details": {
"title": "Villa 1",
"description": "Description about Villa 1"
},
"options": {
"features": [
"5c482b1ff9f18807694040c6",
"5c482b2ef9f18807694040c7",
"5c482b3af9f18807694040c8",
"5c482b49f9f18807694040c9"
],
"filters": [
{
"filter": "5c482c2c2abe2f07ccd5d428",
"value": 7
},
{
"filter": "5c482c392abe2f07ccd5d429",
"value": 3
},
{
"filter": "5c482c462abe2f07ccd5d42a",
"value": 5
},
{
"filter": "5c482c4d2abe2f07ccd5d42b",
"value": 2
},
{
"filter": "5c482c562abe2f07ccd5d42c",
"value": true
},
{
"filter": "5c482c612abe2f07ccd5d42d",
"value": true
}
]
},
"memberOnly": false,
"feature": false,
"active": true,
"Category": "5c45af3b5ccf2c20833a547a"
},
{
"_id": "5c484ec4cb1e150b1efce101",
"details": {
"title": "Villa 2",
"description": "Description about Villa 2"
},
"options": {
"features": [
"5c482b1ff9f18807694040c6",
"5c482b2ef9f18807694040c7",
"5c482b3af9f18807694040c8",
"5c482b49f9f18807694040c9"
],
"filters": [
{
"filter": "5c482c2c2abe2f07ccd5d428",
"value": 5
},
{
"filter": "5c482c392abe2f07ccd5d429",
"value": 7
},
{
"filter": "5c482c462abe2f07ccd5d42a",
"value": 2
},
{
"filter": "5c482c4d2abe2f07ccd5d42b",
"value": 6
},
{
"filter": "5c482c562abe2f07ccd5d42c",
"value": true
},
{
"filter": "5c482c612abe2f07ccd5d42d",
"value": true
}
]
},
"memberOnly": false,
"feature": false,
"active": true,
"Category": "5c45af3b5ccf2c20833a547a"
}
]
I need a mongoose aggregation to be able to filter between objects based on the filter and its value
example:
i need to filter listings that have
filter "5c482c2c2abe2f07ccd5d428" with a value $gte:4
filter "5c482c4d2abe2f07ccd5d42b" with a value $gte:3
filter "5c482c612abe2f07ccd5d42d" with a true value
When I filter the expected result should be
[
{
"_id": "5c484ec4cb1e150b1efce101",
"details": {
"title": "Villa 1",
"description": "Description about Villa 1"
},
"options": {
"features": [
"5c482b1ff9f18807694040c6",
"5c482b2ef9f18807694040c7",
"5c482b3af9f18807694040c8",
"5c482b49f9f18807694040c9"
],
"filters": [
{
"filter": "5c482c2c2abe2f07ccd5d428",
"value": 7
},
{
"filter": "5c482c392abe2f07ccd5d429",
"value": 3
},
{
"filter": "5c482c462abe2f07ccd5d42a",
"value": 5
},
{
"filter": "5c482c4d2abe2f07ccd5d42b",
"value": 2
},
{
"filter": "5c482c562abe2f07ccd5d42c",
"value": true
},
{
"filter": "5c482c612abe2f07ccd5d42d",
"value": true
}
]
},
"memberOnly": false,
"feature": false,
"active": true,
"Category": "5c45af3b5ccf2c20833a547a"
}
]
the solution was
Listing.find({
$and: [{
'options.filters': {
$elemMatch: {
filter: this.mongoose.Types.ObjectId('5c482c2c2abe2f07ccd5d428'),
value: {$gte: 4}
}
}
}, {
'options.filters': {
$elemMatch: {
filter: this.mongoose.Types.ObjectId('5c482c4d2abe2f07ccd5d42b'),
value: {$gte: 3}
}
}
}, {
'options.filters': {
$elemMatch: {
filter: this.mongoose.Types.ObjectId('5c482c612abe2f07ccd5d42d'),
value: true
}
}
});

Building mongoose queries programmatically

I'm trying to do a kind of a complex mongoose query. I have searched around and found solutions asking for single variables(with if) and building the query object, but I wanted to know if there is a simpler way because I would be building a query with about 40 non required parameters, for example right now in my req.body I have:
{ searchParams:
{ publicationType: 'Venta',
subtype: [ 'Duplex', 'Cabaña' ],
address:
{
streetNumber: '1312',
streetName: 'José María Bosch',
city: 'Villa Bosch',
state: 'Buenos Aires',
country: 'Argentina' }
}
}
All the names are the same in the database, what I have to do I search if publicationType matches, if the address matches and finally for sub type if any of those array items matches since subtype is a string. Is there any way to do it without an if for each field?
Right now I did this simple query to try out
router.post('/search', (req, res, next) => {
model.Dwelling.find(req.body.searchParams).lean().exec().then(
dwellings => res.send({dwellings})
).catch(next);
});
It works on publicationType and subtype buy not on address.
this is an object in my data base:
{
"_id": {
"$oid": "5af06167ea174f00142bab91"
},
"publicationType": "Alquiler",
"address": {
"latitude": -34.59250669999999,
"altitude": -58.571258599999965,
"streetNumber": "1312",
"streetName": "José María Bosch",
"city": "Villa Bosch",
"state": "Buenos Aires",
"country": "Argentina"
},
"type": "Residencial",
"subtype": "Casa",
"currency": "",
"price": 50000,
"occupationStatus": "Disponible",
"spaces": {
"rooms": 3,
"floors": 0,
"bedrooms": 0,
"closets": 0,
"bathRoom": 0,
"toilette": 0,
"living": false,
"livingDining": false,
"diningRoom": false,
"kitchen": false,
"kitchenDining": false,
"terrace": false,
"balcony": false,
"backYard": false,
"swimmingPool": false,
"barbecue": false,
"garage": "No",
"laundryRoom": "No"
},
"features": {
"status": "Desconocido",
"orientation": "Desconocida",
"luminosity": "Desconocida",
"heating": [
{
"value": "No posee",
"label": "No posee"
}
],
"refurbished": false,
"repair": "No"
},
"services": {
"gas": true,
"water": true,
"sewer": true,
"phone": true,
"pavement": true,
"electricity": true,
"cableTv": true
},
"legal": {
"bank": false,
"prof": false
},
"generalDescription": "Desc 1",
"privateDescription": "Desc 2",
"siocId": 713675,
"createdAt": {
"$date": "2018-05-07T14:23:35.230Z"
},
"updatedAt": {
"$date": "2018-05-07T14:23:35.230Z"
},
"__v": 0
}

loopback model json array of objects strict filter

I'm using strongloop loopback v3 REST API with mongoDB as datasource. My model order.json is
{
"name": "order",
"base": "PersistedModel",
"strict": true,
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"orderNo": {
"type": "string"
},
"lines": {
"type": [
{
"type": {
"description": "string",
"quantity": "number"
}
}
]
}
},
"validations": [],
"relations": {},
"acls": [],
"methods": {}
}
I set "strict": true so that the model accepts only predefined properties. But this does not work for properties in the array lines.
I.E. if you post this object to the API you get an ValidationError (Code 422) as expected:
{
"orderNo": "xyz",
"someOtherProp": "hello",
"lines": [
{
"description": "abc",
"quantity": 5
}
]
}
But if you post this JSON object loopback saves the object to mongoDB
{
"orderNo": "xyz",
"lines": [
{
"description": "abc",
"quantity": 5,
"someOtherProp": "hello"
}
]
}
My question is about if there are any flags to be set in the model JSON to validate an array of objects? Or do I have to validate the nested documents by my own via the order.js model extension file?
define the lines as another model and make it relation with type embedsMany in order model.
lines model
{
"name": "line",
"base": "Model",
"strict": true,
"idInjection": true,
"properties": {
"description": {
"type": "string"
},
"quantity":{
"type":"number"
}
}
}
order model
{
"name": "order",
"base": "PersistedModel",
"strict": true,
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"orderNo": {
"type": "string"
}
},
"validations": [],
"relations": {
"lines":{
"type": "embedsMany",
"model": "line",
"property": "lines"
}
},
"acls": [],
"methods": {}
}
this way loopback will validate line model

Filter on level 2 properties

I use LoopBack with MongoDB connector.
Models:
Application for a job:
{
"name": "application",
"plural": "applications",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"jobId": {
"type": "string",
"required": true
},
"staffId": {
"type": "string",
"required": true
}
},
"validations": [],
"relations": {},
"acls": [],
"methods": {}
}
Job:
{
"name": "job",
"plural": "jobs",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"title": {
"type": "string",
"required": true
}
},
"validations": [],
"relations": {
"applications": {
"type": "hasMany",
"model": "application",
"foreignKey": "jobId",
"primaryKey": "id"
}
},
"acls": [],
"methods": {}
}
If the user's id (staffId) is in the applications array I need to not show for user this job.
Example:
We have array of jobs with applications
[
{
id: 1,
title: "Job 1",
applications: [
{
jobId: 1,
staffId: 1
},
{
jobId: 1,
staffId: 2
}
]
},
{
id: 2,
title: "Job 2",
applications: [
{
jobId: 2,
staffId: 1
}
]
}
]
If user's id (staffId) is 2 then user sees only "Job 2".
I tried something like this:
/jobs?filter[include][applications]&filter[where not][applications][elemMatch][staffId]=2
But it doesn't work.
Any suggestions?
Thanks.
At this state of the development you can't filter on a level 2 properties with REST: https://github.com/strongloop/loopback/issues/517 (defect till 2014).
What you can do instead is to define a custom emote method that will contain something like:
var userId = 2;
Jobs.find({
include: {
relation: 'applications',
scope: {
where: {staffId: {eq: userId}},
}
}
}, function (err, jobs) {
});

Model Relation in Loopback framework

I want to establish a relation between two models, following the json files:
{
"name": "Surveyor",
"plural": "surveyors",
"base": "PersistedModel",
"strict": true,
"idInjection": true,
"options": {
"validateUpsert": true
},
"mongodb": {
"collection": "surveyors"
},
"settings": {
"mongodb": {
"allowExtendedOperators": true
}
},
"properties": {
"name": {
"type": "string",
"required": true
},
"surname": {
"type": "string",
"required": true
},
"dateOfBirth": {
"type": "date"
},
"birthPlace": {
"type": "string"
},
"provinceOfBirth": {
"type": "string"
},
"gender": {
"type": "string"
},
"registrationNumber": {
"type": "string",
"required": true,
"unique": true
}
},
"validations": [],
"relations": {}
},
"acls": [],
"methods": {}
}
{
"name": "Dossier",
"plural": "dossiers",
"base": "PersistedModel",
"strict": true,
"idInjection": true,
"options": {
"validateUpsert": true
},
"mongodb": {
"collection": "dossiers"
},
"settings": {
"mongodb": {
"allowExtendedOperators": true
}
},
"properties": {
"delCode": {
"type": "string",
"required": true
},
"client": {
"name": {
"type": "string",
"required": true
},
"surname": {
"type": "string",
"required": true
},
"taxCode": {
"type": "string"
}
},
"surveyor": {
"type": "string"
},
"examination": {
"type": "object"
},
"appointment": {
"type": "date"
},
"status": {
"type": "string",
"required": true
},
"notes": {
"type": "string"
}
},
"validations": [],
"relations": {},
"acls": [],
"methods": {}
}
One dossier can have one surveyor, as key I want to use the property surveyor in the Dossier Model, corresponding to the property registrationNumber in the Surveyor Model.
I want to query the dossier Model and obtain the surveyor data in the dossier doc:
[GET] /api/dossiers
or with a filter:
[GET] /api/dossiers?filter={"include":"surveyor"}
if it is possible I want to configure the relation in the json file, not in the code.
You can use the relation generator to create that for you. In your case you're going to need to create a belongsTO relation from Dossier to Surveyor. If you want to query the other way around (dossiers from a surveyor) you're going to need to create a hasMany relation from Surveyor to Dossier.
In short, for what you've asked, you're going to end up with something like this:
In Dossier model:
...
"relations": {
"surveyor": {
"type": "belongsTo",
"model": "Surveyor",
"foreignKey": "surveyor",
"primaryKey": "registrationNumber"
}
}
...

Resources