Code Monkey home page Code Monkey logo

Comments (3)

csbenjamin avatar csbenjamin commented on September 15, 2024

Additional Suggestion for Custom Type Projections Integration

Another approach might be to structure these functions as methods of the custom type itself, similar to how we currently implement the apply, transform, create, and compose methods. In fact, I believe this could be a more streamlined solution compared to passing these functions via the ShareDB constructor. It would also offer the added advantage of enabling projections for multiple types, with all associated logic encapsulated within the type implementation itself.

from sharedb.

alecgibson avatar alecgibson commented on September 15, 2024

Hi ๐Ÿ‘‹๐Ÿผ thanks for raising the issue. I can't see a reason to not add this in principle. Unfortunately, I think this is a huge task; as I'm sure you've seen, projection logic is scattered throughout the codebase and will need some work to tidy up into a more generically-usable shape.

I agree that the correct place for the logic is on the type itself; ideally sharedb core should be as type-agnostic as possible (somewhere where projections don't excel at all...).

If you want to start work on a PR, we can try to help you along the way.

from sharedb.

csbenjamin avatar csbenjamin commented on September 15, 2024

Hi,

Thank you for the prompt response and for considering the feature request. I appreciate your insights on the current state of the codebase.

As a temporary workaround, given that I'm using Webpack, I've overridden the projections.js file with my own implementation. It seems to be working well for now. Here's a snippet of my custom projections code for reference:

import {AOAction, AOActionTransformPaths} from './modules/backend/websocket/conexty-type';

exports.projectSnapshot = projectSnapshot;
exports.projectSnapshots = projectSnapshots;
exports.projectOp = projectOp;
exports.isSnapshotAllowed = isSnapshotAllowed;
exports.isOpAllowed = isOpAllowed;


// Project a snapshot in place to only include specified fields
function projectSnapshot(fields: {[key: string]: true}, snapshot: any) {
    // Only json0 supported right now
    // if (snapshot.type && snapshot.type !== json0.uri) {
    //     throw new Error(ERROR_CODE.ERR_TYPE_CANNOT_BE_PROJECTED, 'Cannot project snapshots of type ' + snapshot.type);
    // }
    snapshot.data = projectData(fields, snapshot.data);
}

function projectSnapshots(fields: {[key: string]: true}, snapshots: any) {
    for (let i = 0; i < snapshots.length; i++) {
        const snapshot = snapshots[i];
        projectSnapshot(fields, snapshot);
    }
}

function projectOp(fields: {[key: string]: true}, op: {create?: any, op?: AOAction[]}) {
    if (op.create) {
        projectSnapshot(fields, op.create);
    }
    if (op.op) {
        op.op = projectEdit(fields, op.op);
    }
}

function projectEdit(fields: {[key: string]: true}, op: AOAction[]) {
    // So, we know the op is a JSON op
    const result = [];

    for (let i = 0; i < op.length; i++) {
        const c = op[i];
        if (c.op !== 'transformPaths') {
            const path = c.path;

            if (path.length > 0) {
                // The path has a first element. Just check it against the fields.
                if (fields[path[0]]) {
                    result.push(c);
                }
            }
        } else {
            // Transform paths
            const paths = c.paths;
            const newPaths = [];
            for (let j = 0; j < paths.length; j++) {
                const p = paths[j];
                if (p.length > 0) {
                    if (fields[p[0]]) {
                        newPaths.push(p);
                    }
                }
            }
            if (newPaths.length > 0) {
                result.push({...c, paths: newPaths} as AOActionTransformPaths);
            }
        }
    }
    return result;
}

function isOpAllowed(knownType: any, fields: {[key: string]: true}, op: {create?: any, op?: AOAction[]}) {
    if (op.create) {
        return isSnapshotAllowed(fields, op.create);
    }
    if (op.op) {
        // if (knownType && knownType !== json0.uri) return false;
        return isEditAllowed(fields, op.op);
    }
    // Noop and del are both ok.
    return true;
}

// Basically, would the projected version of this data be the same as the original?
function isSnapshotAllowed(fields: {[key: string]: true}, snapshot: any) {
    // if (snapshot.type && snapshot.type !== json0.uri) {
    //     return false;
    // }
    if (snapshot.data == null) {
        return true;
    }
    // Data must be an object if not null
    if (typeof snapshot.data !== 'object' || Array.isArray(snapshot.data)) {
        return false;
    }
    for (const k in snapshot.data) {
        if (!fields[k]) return false;
    }
    return true;
}

function isEditAllowed(fields: {[key: string]: true}, op: AOAction[]) {
    for (let i = 0; i < op.length; i++) {
        const c = op[i];
        if (c.op !== 'transformPaths') {
            const path = c.path;
            if (path.length === 0) {
                return false;
            } else if (!fields[path[0]]) {
                return false;
            }
        } else {
            const paths = c.paths;
            for (let j = 0; j < paths.length; j++) {
                const p = paths[j];
                if (p.length === 0) {
                    return false;
                } else if (!fields[p[0]]) {
                    return false;
                }
            }
        }
    }
    return true;
}

function projectData(fields: {[key: string]: true}, data: any) {
    // Return back null or undefined
    if (data == null) {
        return data;
    }
    // If data is not an object, the projected version just looks like null.
    if (typeof data !== 'object' || Array.isArray(data)) {
        return null;
    }
    // Shallow copy of each field
    const result: any = {};
    for (const key in fields) {
        if (data.hasOwnProperty(key)) {
            result[key] = data[key];
        }
    }
    return result;
}

I plan to delve deeper into the ShareDB code to explore the possibility of implementing the proposed solution, aiming to move the logic to the type itself.

Again, thanks for your guidance. I'll keep you updated as I make progress and might reach out for assistance along the way.

Best regards.

from sharedb.

Related Issues (20)

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.