npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

mingru

v0.63.0

Published

SQL Generator for mingru-models

Downloads

22

Readme

mingru (WIP)

Build Status MEAN Module npm version Node.js Version

Convert mingru-models to Go code.

All APIs are subject to change before 1.0.0

Goals:

  • No performance penalty at runtime, mingru is an SQL builder, not an ORM
  • Strongly typed models, models are defined in TypeScript not Go
  • Currently focuses on Go and MySQL/MariaDB

NOTE: The documentation below is outdated!!! We are working on a brand new documentation site. Stay tuned.


A quick example

Step 1: Define models

Models are defined in mingru-models. Let's create a simple user model user.ts:

// ----------- User table model (user.ts) -----------
import * as mm from 'mingru-models';

class User extends mm.Table {
  id = mm.pk();
  name = mm.varChar(100);
  sig = mm.text().nullable;
  age = mm.int();
}

export default mm.table(User);

Step 2: Define actions

Create another file (userTA.ts) for table actions and import the user table (user.ts) we just defined above:

// ----------- User table actions (userTA.ts) -----------
import * as mm from 'mingru-models';
import user from './user';

export class UserTA extends mm.TableActions {
  // Select a user by ID.
  selectUser = mm.selectRow(user.id, user.name, user.sig).byID();
  // Select all users and order by their names.
  selectAllUsers = mm.selectRows(user.id, user.name, user.sig).orderByAsc(user.name);
  // Select a single user signature field by ID.
  selectUserSig = mm.selectField(user.sig).byID();

  // Update a user by ID.
  updateUser = mm.updateOne().setParams(user.name, user.sig).byID();

  // Update all user signatures to an empty string.
  updateAllSigToEmpty = mm.unsafeUpdateAll().set(user.sig, mm.sql`''`);

  // Delete a user by ID.
  deleteByID = mm.deleteOne().byID();

  // Delete all users by a specified name.
  deleteByName = mm.deleteSome().whereSQL(user.name.isEqualToParam());

  // Delete all users.
  deleteAll = mm.unsafeDeleteAll();

  // Insert a new user.
  insertUser = mm
    .insertOne()
    .set(user.sig, mm.sql`'Default signature!'`)
    .setParams(user.name, user.age);
}

export default mm.tableActions(user, UserTA);

Step 3: Generate Go Code

Install mingru-tsconfig:

yarn add mingru-tsconfig -D

Set extends to mingru-tsconfig in your local tsconfig.json:

{
  "extends": "mingru-tsconfig"
}

Create a mingru.ts file, which will start the build process to generate Go and SQL code.

import * as mr from 'mingru';
// Import table actions.
import userTA from './userTA';
// Import tables if you need to generate CREATE TABLE SQL files.
import user from './user';

(async () => {
  const dialect = new mr.MySQL();
  // Build Go code to '../da/` directory.
  const builder = new mr.Builder(dialect, './data_access_layer/', {
    // Clean build directory on each build.
    cleanBuild: true,
  });

  const actions = [userTA];
  const tables = [user];
  // Start the build process by calling the `build` method.
  await builder.buildAsync(async () => {
    // Generate Go source files.
    await builder.buildActionsAsync(actions);
    // Generate CREATE TABLE SQL files.
    await builder.buildCreateTableSQLFilesAsync(tables);
  });
})();

It's also recommended to use ts-node and add a build command to package.json scripts section:

{
  "scripts": {
    "build": "ts-node mingru.ts"
  }
}

Now you can build your project using yarn build.

Below is the code generated by mingru:

/******************************************************************************************
 * This file was automatically generated by mingru (https://github.com/mgenware/mingru)
 * Do not edit this file manually, your changes will be overwritten.
 ******************************************************************************************/

package da

import "github.com/mgenware/mingru-go-lib"

// TableTypeUser ...
type UserAGType struct {
}

// User ...
var User = &UserAGType{}

// ------------ Actions ------------

// DeleteAll ...
func (mrTable *UserAGType) DeleteAll(mrQueryable mingru.Queryable) (int, error) {
	result, err := mrQueryable.Exec("DELETE FROM `user`")
	return mingru.GetRowsAffectedIntWithError(result, err)
}

// DeleteByID ...
func (mrTable *UserAGType) DeleteByID(mrQueryable mingru.Queryable, id uint64) error {
	result, err := mrQueryable.Exec("DELETE FROM `user` WHERE `id` = ?", id)
	return mingru.CheckOneRowAffectedWithError(result, err)
}

// DeleteByName ...
func (mrTable *UserAGType) DeleteByName(mrQueryable mingru.Queryable, name string) (int, error) {
	result, err := mrQueryable.Exec("DELETE FROM `user` WHERE `name` = ?", name)
	return mingru.GetRowsAffectedIntWithError(result, err)
}

// InsertUser ...
func (mrTable *UserAGType) InsertUser(mrQueryable mingru.Queryable, name string, age int) (uint64, error) {
	result, err := mrQueryable.Exec("INSERT INTO `user` (`sig`, `name`, `age`) VALUES ('Default signature!', ?, ?)", name, age)
	return mingru.GetLastInsertIDUint64WithError(result, err)
}

// UserTableSelectAllUsersResult ...
type UserTableSelectAllUsersResult struct {
	ID   uint64
	Name string
	Sig  *string
}

// SelectAllUsers ...
func (mrTable *UserAGType) SelectAllUsers(mrQueryable mingru.Queryable) ([]*UserTableSelectAllUsersResult, error) {
	rows, err := mrQueryable.Query("SELECT `id`, `name`, `sig` FROM `user` ORDER BY `name`")
	if err != nil {
		return nil, err
	}
	result := make([]*UserTableSelectAllUsersResult, 0)
	defer rows.Close()
	for rows.Next() {
		item := &UserTableSelectAllUsersResult{}
		err = rows.Scan(&item.ID, &item.Name, &item.Sig)
		if err != nil {
			return nil, err
		}
		result = append(result, item)
	}
	err = rows.Err()
	if err != nil {
		return nil, err
	}
	return result, nil
}

// UserTableSelectUserResult ...
type UserTableSelectUserResult struct {
	ID   uint64
	Name string
	Sig  *string
}

// SelectUser ...
func (mrTable *UserAGType) SelectUser(mrQueryable mingru.Queryable, id uint64) (*UserTableSelectUserResult, error) {
	result := &UserTableSelectUserResult{}
	err := mrQueryable.QueryRow("SELECT `id`, `name`, `sig` FROM `user` WHERE `id` = ?", id).Scan(&result.ID, &result.Name, &result.Sig)
	if err != nil {
		return nil, err
	}
	return result, nil
}

// SelectUserSig ...
func (mrTable *UserAGType) SelectUserSig(mrQueryable mingru.Queryable, id uint64) (*string, error) {
	var result *string
	err := mrQueryable.QueryRow("SELECT `sig` FROM `user` WHERE `id` = ?", id).Scan(&result)
	if err != nil {
		return result, err
	}
	return result, nil
}

// UpdateAllSigToEmpty ...
func (mrTable *UserAGType) UpdateAllSigToEmpty(mrQueryable mingru.Queryable) (int, error) {
	result, err := mrQueryable.Exec("UPDATE `user` SET `sig` = ''")
	return mingru.GetRowsAffectedIntWithError(result, err)
}

// UpdateUser ...
func (mrTable *UserAGType) UpdateUser(mrQueryable mingru.Queryable, id uint64, name string, sig *string) error {
	result, err := mrQueryable.Exec("UPDATE `user` SET `name` = ?, `sig` = ? WHERE `id` = ?", name, sig, id)
	return mingru.CheckOneRowAffectedWithError(result, err)
}
CREATE TABLE `user` (
	`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
	`name` VARCHAR(100) NOT NULL,
	`sig` TEXT NULL DEFAULT NULL,
	`age` INT NOT NULL,
	PRIMARY KEY (`id`)
)
CHARACTER SET=utf8mb4
COLLATE=utf8mb4_unicode_ci
;

Step 4: Use the generated code in your Go project

func main() {
	// Open a DB connection on localhost.
	db, err := sql.Open("mysql", "root:123456@/test")
	if err != nil {
		panic(err)
	}

	// Select all user profiles.
	users, err := da.User.SelectAllUserProfiles(db)
	if err != nil {
		panic(err)
	}

	// Loop through the result.
	for _, user := range users {
		fmt.Printf("ID: %v, Name: %v, Sig: %v\n", user.ID, user.Name, user.Sig)
	}
}

More examples

For a more detailed and runnable example, visit mingru-go-example

Advanced Topics

Default Values

MySQL doesn't allow you to use a non-constant value as a default value for a column because CREATE TABLE doesn't allow it. mingru supports arbitrary default values for both CREATE and UPDATE actions by simply passing default values into generated SQL.

Pagination

limit

Pagination can be achieved by calling limit following a call to selectRows:

selectUsersWithLimit = mm.selectRows(user.id, user.name).limit();

Implementations should expose arguments to set the underlying SQL LIMIT and OFFSET values, here is the Go method signature generated by mingru from the action above:

func (mrTable *UserAGType) SelectUsersWithLimit(mrQueryable mingru.Queryable, limit int, offset int, max int) ([]*SelectUsersWithLimitResult, int, error)

selectPage

Pagination can also be done via selectPage method, selectPage usually generates a method built upon the SQL LIMIT and OFFSET clauses but exposes higher level arguments thus provides more convenience:

selectPagedUsers = mm.selectPage(user.id, user.name);

mingru converts the action above to the following Go func:

func (mrTable *UserAGType) SelectPagedUsers(mrQueryable mingru.Queryable, page int, pageSize int) ([]*SelectPagedUsersResult, bool, error)

Notice the limit and offset arguments are gone, page and pageSize are exposed instead. Also the second return value changed from rowsFetched(int) to hasNextPage(bool).