How to get result with specific fields in StrongLoop? - javascript

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

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)
});

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.

What is the best way to keep track of changes of a document's property in MongoDB?

I would like to know how to keep track of the values of a document in MongoDB.
It's a MongoDB Database with a Node and Express backend.
Say I have a document, which is part of the Patients collection.
{
"_id": "4k2lK49938d82kL",
"firstName": "John",
"objective": "Burn fat"
}
Then I edit the "objective" property, so the document results like this:
{
"_id": "4k2lK49938d82kL",
"firstName": "John",
"objective": "Gain muscle"
}
What's the best/most efficient way to keep track of that change? In other words, I would like to know that the "objective" property had the value "Burn fat" in the past, and access it in the future.
Thanks a lot!
Maintaining/tracking history in the same document is not all recommended. As the document size will keep on increasing leading to
probably if there are too many updates, 16mb document size limit
Performance degrades
Instead, you should maintain a separate collection for history. You might have use hibernates' Javers or envers for auditing for your relational databases. if not you can check how they work. A separate table (xyz_AUD) is maintained for each table (xyz). For each row (with primary key abc) in xyz table, there exist multiple rows in xyz_AUD table, where each row is version of that row.
Moreover, Javers also support MongoDB auditing. If you are using java you can directly use it. No need to write your own logic.
Refer - https://nullbeans.com/auditing-using-spring-boot-mongodb-and-javers/
One more thing, Javers Envers Hibernate are java libraries. But I'm sure for other programming languages also, similar libraries will be present.
There is a mongoose plugin as well -
https://www.npmjs.com/package/mongoose-audit (quite oudated 4 years)
https://github.com/nassor/mongoose-history#readme (better)
Maybe you can change the type of "objective" to array and track the changes in it. the last one of the array is the latest value.
Maintain it as a sub-document like below
{
"_id": "4k2lK49938d82kL",
"firstName": "John",
"objective": {
obj1: "Gain muscle",
obj2: "Burn fat"
}
}
You can also maintain it as an array field but remember, mongodb doesn't allow you to maintain uniqueness in an array field and if you plan to index the "objective" field, you'll have to create a multi key index
I think the simplest solution would be to use and update an array:
const patientSchema = new Schema({
firstName: { type: String, required: true },
lastName: { type: String, required: true },
objective: { type: String, required: true }
notes: [{
date: { type: Date, default: Date.now() },
note: { type: String, required: true }
}],
});
Then when you want to update the objective...
const updatePatientObjective = async (req, res) => {
try {
// check if _id and new objective exist in req.body
const { _id, objective, date } = req.body;
if (!_id || !objective) throw "Unable to update patient's objective.";
// make sure provided _id is valid
const existingPatient = await Patient.findOne({ _id });
if (!existingPatient) throw "Unable to locate that patient.";
// pull out objective as previousObjective
const { objective: previousObjective } = existingPatient;
// update patient's objective while pushing
// the previous objective into the notes sub document
await existingPatient.updateOne({
// update current objective
$set { objective },
// push an object with a date and note (previouseObjective)
// into a notes array
$push: {
notes: {
date,
note: previousObjective
},
},
}),
);
// send back response
res
.status(201)
.json({ message: "Successfully updated your objective!" });
} catch (err) {
return res.status(400).json({ err: err.toString() });
}
};
Document will look like:
firstName: "John",
lastName: "Smith",
objective: "Lose body fat.",
notes: [
{
date: 2019-07-19T17:45:43-07:00,
note: "Gain muscle".
},
{
date: 2019-08-09T12:00:38-07:00,
note: "Work on cardio."
}
{
date: 2019-08-29T19:00:38-07:00,
note: "Become a fullstack web developer."
}
...etc
]
Alternatively, if you're worried about document size, then create a separate schema for patient history and reference the user's id (or just store the patient's _id as a string instead of referencing an ObjectId, whichever you prefer):
const patientHistorySchema = new Schema({
_id: { type: Schema.Types.ObjectId, ref: "Patient", required: true },
objective: { type: String, required: true }
});
Then create a new patient history document when the objective is updated...
PatientHistory.create({ _id, objective: previousObjective });
And if you need to access to the patient history documents...
PatientHistory.find({ _id });

Algolia filter by nested attribute JavaScript

So I am using Algolia.com to index users for quick searching.
An example object in index:
{
id: 1,
username: "john.doe",
name: "John Doe",
attributes: {
gender: "male",
hair_color: "blonde",
eye_color: "blue",
height: 165
}
}
I'd like to filter results by a specific attribute (object key) in the attributes object.
I thought maybe using facetFilters would do the job, but I am unable to get it working.
I have tried many variances of this code:
user_index.search('', {
facets: '*',
facetFilters: ['attributes.hair_color:blonde', 'attributes.eye_color:blue']
}, function(error, data) {
console.log(error, data);
});
-- / --
user_index.search('', {
facets: '*',
facetFilters: ['hair_color:blonde']
}, function(error, data) {
console.log(error, data);
});
Please find the documentation here: https://www.algolia.com/doc/javascript
Just in order to be able to mark this question as answered :
You should add attributes.hair_color to your attributesForFaceting (Display tab in your index dashboard)
You should be able to easily just do
user_index.search('', {
facetFilters: 'attributes.hair_color:blonde'
}, function () {
console.log(arguments);
});
to search.
Sounds like using facet filters is the best way to achieve what you're looking for. The easiest way is to handle those filters is probably to use the Javascript Helper https://github.com/algolia/algoliasearch-helper-js#filtering-results.
You would then only need to call
helper.addFacetRefinement('hair_color', 'blonde').search();

Using results from one RethinkDB query in another?

I want to send a http post with an address, which will grab a doc from the database. I would like to then use that doc's geodata in a getNearest function, ultimately returning the nearest four other locations. How do I go about stringing these two queries together?
r.table('dealer_locations').filter(function(dealer) {
return {
dealer : ("Address").match(request.body.Address)
.getNearest(dealer, {
index: 'geodata',
maxResults: 4
})
}
}).run(conn, function(error, cursor) {
if (error) {
handleError(response, error);
} else {
cursor.toArray(function(error, results) {
if (error) {
handleError(response, error);
} else {
response.send(results);
}
});
}
});
I'll reformulate the question just to make a bit more clear:
Problem
Given a specific document with geodata, I want to also return the four nearest locations to that location.
Solution
First, make sure that you've creating the geo index in the table you want to query:
r.table('dealer_locations').indexCreate('location', { geo: true })
After that, make sure that you've inserted a r.point object into the document. You can't just use geo indexes on just any property. They have to be r.points.
r.table('dealer_locations')
.insert({
name: 'San Francisco',
location: r.point(37.774929, -122.419416)
})
After you've inserted all your documents and they all have r.points property on the same property you created an index for, now you can start querying them.
If you want to get all the nearest locations for a location, you can do as follows:
r.table('dealer_locations')
.filter({ name: 'San Francisco' })(0)
.do(function (row) {
return r.table('dealer_locations')
.getNearest(row('location'), { index: 'location', maxResults: 4 })
})
If you want to append the closets locations to a document, so that you can return both the document and the nearest locations at the same time, you can do that using the merge method.
r.table('dealer_locations')
.filter({ name: 'San Francisco' })(0)
.merge(function (row) {
return {
nearest_locations: r.table('dealer_locations')
.getNearest(row('location'), { index: 'location', maxResults: 4 })
}
})
Finally, if you want to get all the nearest locations, based on an address (supposing your document has both an address property with a string and a location property with an r.point), you can do something like this:
r.table('dealer_locations')
.filter(r.row('address').match(request.body.Address))
(0) // Only get the first document
.do(function (row) {
// Return the 4 documents closest to the 'location' if the previous document
return r.table('dealer_locations')
.getNearest(row('location'), { index: 'location', maxResults: 4 })
})
That being said, the problem with this might be that you might match multiple addresses which are not necessarily the ones you want to match!

Categories