aliatech / loopback-mongo-aggregate-mixin Goto Github PK
View Code? Open in Web Editor NEWLoopback mixin to query MongoDB aggregation pipeline and build the instances from results
License: MIT License
Loopback mixin to query MongoDB aggregation pipeline and build the instances from results
License: MIT License
I don't even know if this repo is still active, but it's worth a shot.
I have a "loaded" hook on a model that uses this mixin in which i calculate some properties and assign it to ctx.data, it works well in the project but when it comes to the aggregate functions seems to be not working, any idea what could it be?
Here some code:
this is the hook:
Listing.observe("loaded", async (ctx: any) => {
if (ctx.data) {
if (ctx.options?.currentUser) {
const currentUser = ctx.options.currentUser;
let isLikedByYou = await Listing.app.models.LikedListing.findOne({
where: {
memberId: currentUser.id,
listingId: ctx.data.id,
},
});
ctx.data["isLikedByYou"] = !!isLikedByYou;
}
}
});
And this is the remote method aggregation which doesn't seem to be keeping the properties i set in the hook:
...
const pipeline: any = [{
$geoNear: {
near: point,
distanceField: "distance",
maxDistance: radius * 6378.1 * 1000,
query: query,
},
}, {
$skip: page * limit,
}, {
$limit: limit,
}];
return {
total: (await Listing.aggregate(count_pipeline))[0]?.total || 0,
page: page,
limit: limit,
listings: await Listing.aggregate({
include: ["agent", "agency"],
aggregate: pipeline,
}),
userSearch: user_search,
}
I couldn't verify if the problem is that the loaded event isn't being fired or if the data is not persisted trough the flow but I suspect it's the first case
Anyway, if you have any more insight on what could it be causing this, it would be much appreciated, btw it's a great mixin, thanks for developing it
Hello ,
I have followed the instructions for adding the aggregate pipeline but i get the below error
Error: Model "Customer" uses unknown mixin: Aggregate
The below is my model-config.json file
{
"_meta": {
"sources": [
"loopback/common/models",
"loopback/server/models",
"../common/models",
"./models"
],
"mixins": [
"loopback/common/mixins",
"loopback/server/mixins",
"../node_modules/loopback-mongo-aggregate-mixin/lib",
"../common/mixins",
"./mixins"
]
},
"User": {
"dataSource": "mongo"
},
"AccessToken": {
"dataSource": "mongo",
"public": false
},
"ACL": {
"dataSource": "mongo",
"public": false
},
"RoleMapping": {
"dataSource": "mongo",
"public": false,
"options": {
"strictObjectIDCoercion": true
}
},
"Role": {
"dataSource": "mongo",
"public": false
},
"Customer": {
"dataSource": "mongo",
"public": true
},
"jobs": {
"dataSource": "mongo",
"public": true
}
}
This is my customer.json file
{
"name": "Customer",
"base": "User",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"customer_id": {
"type": "number",
"required": true
},
"customer_name": {
"type": "string",
"required": true
}
},
"validations": [],
"relations": {
"jobs": {
"type": "hasMany",
"model": "jobs",
"foreignKey": "CustomerId",
"primaryKey": "customer_id",
"options": {
"nestRemoting": true
}
}
},
"acls": [
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$authenticated",
"permission": "ALLOW"
},
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$unauthenticated",
"permission": "DENY"
}
],
"methods": {},
"mixins": {
"Aggregate":true
}
}
One thing I noticed when doing this manually is let's say you're doing something early on in like a bootscript, you need to make sure the database has connected first, you can do this with
if (!app.datasources.db.connector.db) {
app.datasources.db.once('connected', () => {
// Run Stuff
})
} else {
// Run Stuff
}
Hello and thanks for the package! I've been looking for a mixin like this and this is the best one yet.
After using it for a while, I found a few issues. I'll open separate threads for each one, if that's ok.
in lib/aggregation.js the coalesce method returns an $ifNull operator, where the replacement value is 'false' by default and there seems to be no way to overwrite it. When matching a relation field of type boolean to 'false' I'm getting nulls as positive matches.
coalesce (fields, coalesce = false) { const addFields = _.mapValues(fields, (value, key) => { return {$ifNull: [`$${key}`, coalesce]}; }); return this.append({ $addFields: addFields, }); }
there's a test implying that
it('Should aggregate where hasMany relation properties with $unwind', callback)
but after logging the resulting pipeline, I saw that it had no $unwind stage.
The only place in the code where the $unwind stage is built is in the buildLookup function in an if block:
function buildLookup (aggregate, where, parentRelation = null) { _.each(where, (value, key) => { const keys = key.split('.'); const headKey = keys.shift(); const relation = (parentRelation ? parentRelation.modelTo : Model).relations[headKey]; if (!relation) return; const lookupOpts = buildLookupOptsFromRelation(relation, parentRelation); aggregate.lookup(lookupOpts); if (_.includes(['hasOne', 'belongsTo'], relation.type)) { let unwindPath = relation.name; const parentRelationName = _.get(parentRelation, 'name', ''); if (parentRelationName.length) { unwindPath = `${parentRelationName}.${unwindPath}`; } aggregate.unwind({ path: `$${ unwindPath}`, preserveNullAndEmptyArrays: true, }); } /* istanbul ignore else */ if (keys.length) { buildLookup(aggregate, {[keys.join('.')]: value}, relation); } }); }
the $unwind stage is built only when the following condition is true.
if (_.includes(['hasOne', 'belongsTo'], relation.type))
this seemed a bit odd. I tried 2 things:
In both cases all the tests passed, and the results of my requests stayed the same: hasMany relational field filters work only if the relation is 1 level deep and the field by which I'm filtering is not a foreign key.
I'm still investigating and I would appreciate any suggestion !
By the way, what does this comment mean /* istanbul ignore else */ ? :)
Thanks in advance and sorry for jumping in with multiple issues.
Pretty much the title.
Here's the function that looks for relational fields:
Model.whichFieldsAreRelational = function (where) { return _.keys(where).filter((key) => { const headKey = key.split('.')[0]; return Model.relations[headKey]; }); };
if my relational fields are nested like this:
{ where: { or: [ { 'relation.field': someOperator } ] } }
they will be ignored. In simple cases you can flatten the 'where' and bring relation fields to the upper level, but it isn't always possible.
Wondering if this support promises.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.