Git Product home page Git Product logo

loopback-mongo-aggregate-mixin's People

Contributors

akeri avatar davidkrpt avatar dependabot[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

loopback-mongo-aggregate-mixin's Issues

Aggregate doesn't fire "loaded" event or does not keep changes made in the loaded hook

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

Error: Model "Customer" uses unknown mixin: Aggregate

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


Make sure connector has connected

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
}

$ifNull operator's replacement expression is false.

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

$unwind stage is not built for hasMany relation fields

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:

  1. inverting the condition so that $unwind is built only for hasMany relations.
  2. removing the if block altogether.

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.

Relational fields are ignored if they are nested 2 levels or deeper in the 'where' parameter

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.