问:
Firebase Database Rules - Controlling access to collections
I've been trying to come up with a flat data structure for Firebase Database (as is recommended) and afterwards, a set of rules to control access correctly. My example is trying to demonstrate how you'd lock down/allow access to a db which is multi-tenanted across different organizations.

My first attempt was something like this:

Database structure: https://gist.github.com/peteski22/40b0a79a6854d7bb818919a5262f4a7e

{
    "admins" : {
        "8UnM6LIiZJYAHVdty6gzdD8oVI42" : true
    },
    "organizations": {
        "-JiGh_31GA20JabpZBfc" : {
            "name" : "Comp1 Ltd"
        },
        "-JiGh_31GA20JabpZBfd" : {
            "name" : "company2 PLC"
        }
    },
    "users"  : {
        "8UnM6LIiZJYAHVdty6gzdD8oVI42": {
            "firstName" : "Peter",
            "lastName" : "Piper",
            "email" : "peter.piper@testtest.com",
            "organization" : ""
        },
        "-JiGh_31GA20JabpZBfe" : {
            "firstName" : "Joe",
            "lastName" : "Blogs",
            "email" : "joe.blogs@co1.com",
            "organization" : "-JiGh_31GA20JabpZBfc"
        },
        "WgnHjk5D8xbuYeA7VDM3ngKwCYV2" : {
            "firstName" : "test",
            "lastName" : "user",
            "email" : "test.user@google.com",
            "organization" : "-JiGh_31GA20JabpZBfd"
        }
    },
    "employees" : {
        "-JiGh_31GA20JabpZBeb" : {
            "organization" : "-JiGh_31GA20JabpZBfc",
            "firstName" : "Johnny",
            "lastName" : "Baggs",
            "email" : "j.baggss@co1.com",
            "employeeNumber" : "ASV123456"
        },
        "-JiGh_31GA20JabpZBec" : {
            "organization" : "-JiGh_31GA20JabpZBfc",
            "firstName" : "Maheswari",
            "lastName" : "Sanjal",
            "email" : "mahe.sanjal@co1.com",
            "employeeNumber" : "ASV111111"
        },
        "-JiGh_31GA20JabpZBce" : {
            "organization" : "-JiGh_31GA20JabpZBfd",
            "firstName" : "Fedde",
            "lastName" : "le Grande",
            "email" : "fedde.grande@co2.com",
            "employeeNumber" : "ASV111111"
        }
    }
}
```

<p>Database rules: https://gist.github.com/peteski22/b038d81641c1409cec734d187272eeba</p>

<pre class="lang-js prettyprint-override"><code>{
    "rules" : {
        "admins" : {
            ".read" : "root.child('admins').hasChild(auth.uid)",
            ".write" : "root.child('admins').hasChild(auth.uid)"
        },
        "users" : {
            "$user" : {
                ".read" : "data.child('organization').val() === root.child('users').child(auth.uid).child('organization').val()",
                ".write" : "root.child('admins').hasChild(auth.uid)"
            }
        },
        "organizations" : {
            "$organization" : {
                ".read" : "$organization === root.child('users').child(auth.uid).child('organization').val()",
                ".write" : "root.child('admins').hasChild(auth.uid)"
            }
        },
        "employees" : {
            "$employee" : {
                ".read" : "data.child('organization').val() === root.child('users').child(auth.uid).child('organization').val()",
                ".write" : "data.child('organization').val() === root.child('users').child(auth.uid).child('organization').val()"
            }
        }
    }
}
</code>```

However the problem here seemed to be that I couldn't do something like:

```<code>[GET] /employees
</code>```

to see a collection of employees that belonged to the same organization as the logged in user.

After much faffing, I read this in the docs: https://firebase.google.com/docs/database/security/securing-data#rules_are_not_filters, which I think boils down to 'you're doing it wrong' if you want to get at the data the way I did.

Back to the drawing board, and after reading https://www.firebase.com/docs/web/guide/structuring-data.html / https://firebase.google.com/docs/database/web/structure-data

I made some changes to the db structure and rules:

Attempt #2 structure: https://gist.github.com/peteski22/4593733bf54815393a443dfcd0f34c04

{
    "admins" : {
        "8UnM6LIiZJYAHVdty6gzdD8oVI42" : true
    },
    "organizations": {
        "-JiGh_31GA20JabpZBfc" : {
            "name" : "Comp1 Ltd",
            "users" : {
                "-JiGh_31GA20JabpZBfe" : true,
                "-JiGh_31GA20JabpZBff" : true,
                "-JiGh_31GA20JabpZBea" : true
            },
            "employees" : {
                "-JiGh_31GA20JabpZBeb" : true,
                "-JiGh_31GA20JabpZBec" : true
            }
        },
        "-JiGh_31GA20JabpZBfd" : {
            "name" : "company2 PLC",
            "users" :{
                "WgnHjk5D8xbuYeA7VDM3ngKwCYV2" : true
            },
            "employees" :{
                "-JiGh_31GA20JabpZBce" : true
            }
        }
    },
    "users"  : {
        "8UnM6LIiZJYAHVdty6gzdD8oVI42": {
            "firstName" : "Peter",
            "lastName" : "Piper",
            "email" : "peter.piper@testtest.com",
            "organization" : ""
        },
        "-JiGh_31GA20JabpZBfe" : {
            "firstName" : "Joe",
            "lastName" : "Blogs",
            "email" : "joe.blogs@co1.com",
            "organization" : "-JiGh_31GA20JabpZBfc"
        },
        "-JiGh_31GA20JabpZBff" : {
            "firstName" : "Sally",
            "lastName" : "McSwashle",
            "email" : "sally.mcswashle@co1.com",
            "organization" : "-JiGh_31GA20JabpZBfc"
        },
        "-JiGh_31GA20JabpZBea" : {
            "firstName" : "Eva",
            "lastName" : "Rushtock",
            "email" : "eva.rushtock@payrollings.com",
            "organization" : "-JiGh_31GA20JabpZBfc"
        },
        "WgnHjk5D8xbuYeA7VDM3ngKwCYV2" : {
            "firstName" : "test",
            "lastName" : "user",
            "email" : "test.user@google.com",
            "organization" : "-JiGh_31GA20JabpZBfd"
        }
    },
    "employees" : {
        "-JiGh_31GA20JabpZBeb" : {
            "organization" : "-JiGh_31GA20JabpZBfc",
            "firstName" : "Johnny",
            "lastName" : "Baggs",
            "email" : "j.baggss@financeco.com",
            "employeeNumber" : "ASV123456"
        },
        "-JiGh_31GA20JabpZBec" : {
            "organization" : "-JiGh_31GA20JabpZBfc",
            "firstName" : "Maheswari",
            "lastName" : "Sanjal",
            "email" : "mahe.sanjal@financeco.com",
            "employeeNumber" : "ASV111111"
        },
        "-JiGh_31GA20JabpZBce" : {
            "organization" : "-JiGh_31GA20JabpZBfd",
            "firstName" : "Fedde",
            "lastName" : "le Grande",
            "email" : "fedde.grande@payrollings.com",
            "employeeNumber" : "ASV111111"
        }
    }
}
```

<p>Attempt #2 rules: https://gist.github.com/peteski22/e1be434cd1ea8ec2e630bec6d8aa714f</p>

<pre class="lang-js prettyprint-override"><code>{
    "rules" : {
        "admins" : {
            ".read" : "root.child('admins').hasChild(auth.uid)",
            ".write" : "root.child('admins').hasChild(auth.uid)"
        },
        "users" : {
            ".indexOn": [ "organization" ],
            "$user" : {
                ".read" : "data.child('organization').val() === root.child('users').child(auth.uid).child('organization').val()",
                ".write" : "root.child('admins').hasChild(auth.uid)"
            }
        },
        "organizations" : {
            ".indexOn": [ "users", "employees" ],
            "$organization" : {
                ".read" : "$organization === root.child('users').child(auth.uid).child('organization').val()",
                ".write" : "root.child('admins').hasChild(auth.uid)"
            }
        },
        "employees" : {
            ".indexOn": [ "organization" ],
            "$employee" : {
                ".read" : "data.child('organization').val() === root.child('users').child(auth.uid).child('organization').val()",
                ".write" : "data.child('organization').val() === root.child('users').child(auth.uid).child('organization').val()"
            }
        }
    }
}
</code>```

Now I can keep the data locked down fine in each collection, but the only way to get anything is to know the organization ID, get that organization, then get each and every employee by their ID. Although the docs above for structuring data (section: Joining Flattened Data) seem to suggest that it's fine to do this, coming from an OO and SQL background it feels very weird.. which usually means.. 'I'm doing it wrong'.

If anyone has any advice on whether I'm going down the right track, or what to try instead it would be greatly appreciated.

Thanks
Peter

答:
After reading the docs and chatting with people in the firebase-community slack, I've come to the conclusion that I was on the right track.

I found using a compiler called "Bolt" (firebase-bolt in npm) extremely useful for generating the rules too.

Here's my structure, bolt rules and compiled JSON rules:

Structure

{
    "admins" : {
        "8UnM6LIiZJYAHVdty6gzdD8oVI42" : true
    },
    "organizations": {
        "-JiGh_31GA20JabpZBfc" : {
            "name" : "Comp1 Ltd",
            "users" : {
                "-JiGh_31GA20JabpZBfe" : true,
                "-JiGh_31GA20JabpZBff" : true,
                "-JiGh_31GA20JabpZBea" : true
            },
            "employees" : {
                "-JiGh_31GA20JabpZBeb" : true,
                "-JiGh_31GA20JabpZBec" : true
            }
        },
        "-JiGh_31GA20JabpZBfd" : {
            "name" : "company2 PLC",
            "users" :{
                "WgnHjk5D8xbuYeA7VDM3ngKwCYV2" : true
            },
            "employees" :{
                "-JiGh_31GA20JabpZBce" : true
            }
        }
    },
    "users"  : {
        "8UnM6LIiZJYAHVdty6gzdD8oVI42": {
            "firstName" : "Peter",
            "lastName" : "Piper",
            "email" : "peter.piper@testtest.com",
            "organization" : ""
        },
        "-JiGh_31GA20JabpZBfe" : {
            "firstName" : "Joe",
            "lastName" : "Blogs",
            "email" : "joe.blogs@co1.com",
            "organization" : "-JiGh_31GA20JabpZBfc"
        },
        "-JiGh_31GA20JabpZBff" : {
            "firstName" : "Sally",
            "lastName" : "McSwashle",
            "email" : "sally.mcswashle@co1.com",
            "organization" : "-JiGh_31GA20JabpZBfc"
        },
        "-JiGh_31GA20JabpZBea" : {
            "firstName" : "Eva",
            "lastName" : "Rushtock",
            "email" : "eva.rushtock@payrollings.com",
            "organization" : "-JiGh_31GA20JabpZBfc"
        },
        "WgnHjk5D8xbuYeA7VDM3ngKwCYV2" : {
            "firstName" : "test",
            "lastName" : "user",
            "email" : "test.user@google.com",
            "organization" : "-JiGh_31GA20JabpZBfd"
        }
    },
    "employees" : {
        "-JiGh_31GA20JabpZBeb" : {
            "organization" : "-JiGh_31GA20JabpZBfc",
            "firstName" : "Johnny",
            "lastName" : "Baggs",
            "email" : "j.baggss@financeco.com",
            "employeeNumber" : "ASV123456"
        },
        "-JiGh_31GA20JabpZBec" : {
            "organization" : "-JiGh_31GA20JabpZBfc",
            "firstName" : "Maheswari",
            "lastName" : "Sanjal",
            "email" : "mahe.sanjal@financeco.com",
            "employeeNumber" : "ASV111111"
        },
        "-JiGh_31GA20JabpZBce" : {
            "organization" : "-JiGh_31GA20JabpZBfd",
            "firstName" : "Fedde",
            "lastName" : "le Grande",
            "email" : "fedde.grande@payrollings.com",
            "employeeNumber" : "ASV111111"
        }
    }
}
```

<p><strong>Bolt Rules</strong></p>

<pre class="lang-js prettyprint-override"><code>// **********
// FUNCTIONS
// **********

function isAdmin (auth) {
    return root.admins[auth.uid] != null
}

function isInSameOrganization(auth, orgUid) {
    return root.users[auth.uid].organization === orgUid
}

// **********
// PATHS
// **********

path /admins {
    read() { isAdmin(auth) }
    write() { isAdmin(auth) }
}

path /users {
    index() { ["organization"] }
    write() { isAdmin(auth) }
}

path /users/{id} is User {
    read() { isInSameOrganization(auth, id) || isAdmin(auth) }
}

path /organizations {
    write() { isAdmin(auth) }
}

path /organizations/{id} is Organization {
    read() { isInSameOrganization(auth, id) }
}

path /employees {
    index() { ["organization"] }
    write() { isInSameOrganization(auth, this.organization) || isAdmin(auth) }
}

path /employees/{id} is Employee {
    read() { isInSameOrganization(auth, id) || isAdmin(auth) }
}

// **********
// TYPES
// **********
type OrganizationID extends String {
    validate() { root.organizations[this] != null }
}

type UserID extends String {
    validate() { root.users[this] != null }
}

type EmployeeID extends String {
    // Validate that the user ID exists in the employees node (read rule access should prevent us reading a employees that isn't in our org)
    validate() { root.employees[this] != null }
}

type Email extends String {
    validate() {
        return this.matches(/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}$/i);
    }
}

type User {
    firstName: String,
    lastName: String
    email: Email,
    organization: OrganizationID
}

type Employee {
    organization: OrganizationID,
    firstName: String,
    lastName: String,
    email: Email,
    employeeNumber: String
}

type Organization {
    name: String,
    users: Map<UserID, Boolean> | Null,
    employees: Map<EmployeeID, Boolean> | Null
}
</code>```

JSON Rules (generated by Bolt)

{
  "rules": {
    "admins": {
      ".read": "root.child('admins').child(auth.uid).val() != null",
      ".write": "newData.parent().child('admins').child(auth.uid).val() != null"
    },
    "users": {
      ".write": "newData.parent().child('admins').child(auth.uid).val() != null",
      ".indexOn": [
        "organization"
      ],
      "$id": {
        ".validate": "newData.hasChildren(['firstName', 'lastName', 'email', 'organization'])",
        "firstName": {
          ".validate": "newData.isString()"
        },
        "lastName": {
          ".validate": "newData.isString()"
        },
        "email": {
          ".validate": "newData.isString() && newData.val().matches(/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\\\.[A-Z]{2,4}$/i)"
        },
        "organization": {
          ".validate": "newData.isString() && newData.parent().parent().parent().child('organizations').child(newData.val()).val() != null"
        },
        "$other": {
          ".validate": "false"
        },
        ".read": "root.child('users').child(auth.uid).child('organization').val() == $id || root.child('admins').child(auth.uid).val() != null"
      }
    },
    "organizations": {
      ".write": "newData.parent().child('admins').child(auth.uid).val() != null",
      "$id": {
        ".validate": "newData.hasChildren(['name'])",
        "name": {
          ".validate": "newData.isString()"
        },
        "users": {
          "$key1": {
            ".validate": "newData.parent().parent().parent().parent().child('users').child($key1).val() != null && newData.isBoolean()"
          },
          ".validate": "newData.hasChildren()"
        },
        "employees": {
          "$key2": {
            ".validate": "newData.parent().parent().parent().parent().child('employees').child($key2).val() != null && newData.isBoolean()"
          },
          ".validate": "newData.hasChildren()"
        },
        "$other": {
          ".validate": "false"
        },
        ".read": "root.child('users').child(auth.uid).child('organization').val() == $id"
      }
    },
    "employees": {
      ".write": "newData.parent().child('users').child(auth.uid).child('organization').val() == newData.child('organization').val() || newData.parent().child('admins').child(auth.uid).val() != null",
      ".indexOn": [
        "organization"
      ],
      "$id": {
        ".validate": "newData.hasChildren(['organization', 'firstName', 'lastName', 'email', 'employeeNumber'])",
        "organization": {
          ".validate": "newData.isString() && newData.parent().parent().parent().child('organizations').child(newData.val()).val() != null"
        },
        "firstName": {
          ".validate": "newData.isString()"
        },
        "lastName": {
          ".validate": "newData.isString()"
        },
        "email": {
          ".validate": "newData.isString() && newData.val().matches(/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\\\.[A-Z]{2,4}$/i)"
        },
        "employeeNumber": {
          ".validate": "newData.isString()"
        },
        "$other": {
          ".validate": "false"
        },
        ".read": "root.child('users').child(auth.uid).child('organization').val() == $id || root.child('admins').child(auth.uid).val() != null"
      }
    }
  }
}
```

I'm still finding bugs in this, but I think it's showing progress. Just to note that there are some good official & unofficial Firebase videos on Youtube, the docs are fairly decent for the most part, and the firebase-community seems to be friendly. So any new comers like me, you know where to start.

Body:
I've been trying to come up with a flat data structure for Firebase Database (as is recommended) and afterwards, a set of rules to control access correctly. My example is trying to demonstrate how you'd lock down/allow access to a db which is multi-tenanted across different organizations.

My first attempt was something like this:

Database structure: https://gist.github.com/peteski22/40b0a79a6854d7bb818919a5262f4a7e

{
    "admins" : {
        "8UnM6LIiZJYAHVdty6gzdD8oVI42" : true
    },
    "organizations": {
        "-JiGh_31GA20JabpZBfc" : {
            "name" : "Comp1 Ltd"
        },
        "-JiGh_31GA20JabpZBfd" : {
            "name" : "company2 PLC"
        }
    },
    "users"  : {
        "8UnM6LIiZJYAHVdty6gzdD8oVI42": {
            "firstName" : "Peter",
            "lastName" : "Piper",
            "email" : "peter.piper@testtest.com",
            "organization" : ""
        },
        "-JiGh_31GA20JabpZBfe" : {
            "firstName" : "Joe",
            "lastName" : "Blogs",
            "email" : "joe.blogs@co1.com",
            "organization" : "-JiGh_31GA20JabpZBfc"
        },
        "WgnHjk5D8xbuYeA7VDM3ngKwCYV2" : {
            "firstName" : "test",
            "lastName" : "user",
            "email" : "test.user@google.com",
            "organization" : "-JiGh_31GA20JabpZBfd"
        }
    },
    "employees" : {
        "-JiGh_31GA20JabpZBeb" : {
            "organization" : "-JiGh_31GA20JabpZBfc",
            "firstName" : "Johnny",
            "lastName" : "Baggs",
            "email" : "j.baggss@co1.com",
            "employeeNumber" : "ASV123456"
        },
        "-JiGh_31GA20JabpZBec" : {
            "organization" : "-JiGh_31GA20JabpZBfc",
            "firstName" : "Maheswari",
            "lastName" : "Sanjal",
            "email" : "mahe.sanjal@co1.com",
            "employeeNumber" : "ASV111111"
        },
        "-JiGh_31GA20JabpZBce" : {
            "organization" : "-JiGh_31GA20JabpZBfd",
            "firstName" : "Fedde",
            "lastName" : "le Grande",
            "email" : "fedde.grande@co2.com",
            "employeeNumber" : "ASV111111"
        }
    }
}

Database rules: https://gist.github.com/peteski22/b038d81641c1409cec734d187272eeba

{
    "rules" : {
        "admins" : {
            ".read" : "root.child('admins').hasChild(auth.uid)",
            ".write" : "root.child('admins').hasChild(auth.uid)"
        },
        "users" : {
            "$user" : {
                ".read" : "data.child('organization').val() === root.child('users').child(auth.uid).child('organization').val()",
                ".write" : "root.child('admins').hasChild(auth.uid)"
            }
        },
        "organizations" : {
            "$organization" : {
                ".read" : "$organization === root.child('users').child(auth.uid).child('organization').val()",
                ".write" : "root.child('admins').hasChild(auth.uid)"
            }
        },
        "employees" : {
            "$employee" : {
                ".read" : "data.child('organization').val() === root.child('users').child(auth.uid).child('organization').val()",
                ".write" : "data.child('organization').val() === root.child('users').child(auth.uid).child('organization').val()"
            }
        }
    }
}

However the problem here seemed to be that I couldn't do something like:

[GET] /employees

to see a collection of employees that belonged to the same organization as the logged in user.

After much faffing, I read this in the docs: https://firebase.google.com/docs/database/security/securing-data#rules_are_not_filters, which I think boils down to 'you're doing it wrong' if you want to get at the data the way I did.

Back to the drawing board, and after reading https://www.firebase.com/docs/web/guide/structuring-data.html / https://firebase.google.com/docs/database/web/structure-data

I made some changes to the db structure and rules:

Attempt #2 structure: https://gist.github.com/peteski22/4593733bf54815393a443dfcd0f34c04

{
    "admins" : {
        "8UnM6LIiZJYAHVdty6gzdD8oVI42" : true
    },
    "organizations": {
        "-JiGh_31GA20JabpZBfc" : {
            "name" : "Comp1 Ltd",
            "users" : {
                "-JiGh_31GA20JabpZBfe" : true,
                "-JiGh_31GA20JabpZBff" : true,
                "-JiGh_31GA20JabpZBea" : true
            },
            "employees" : {
                "-JiGh_31GA20JabpZBeb" : true,
                "-JiGh_31GA20JabpZBec" : true
            }
        },
        "-JiGh_31GA20JabpZBfd" : {
            "name" : "company2 PLC",
            "users" :{
                "WgnHjk5D8xbuYeA7VDM3ngKwCYV2" : true
            },
            "employees" :{
                "-JiGh_31GA20JabpZBce" : true
            }
        }
    },
    "users"  : {
        "8UnM6LIiZJYAHVdty6gzdD8oVI42": {
            "firstName" : "Peter",
            "lastName" : "Piper",
            "email" : "peter.piper@testtest.com",
            "organization" : ""
        },
        "-JiGh_31GA20JabpZBfe" : {
            "firstName" : "Joe",
            "lastName" : "Blogs",
            "email" : "joe.blogs@co1.com",
            "organization" : "-JiGh_31GA20JabpZBfc"
        },
        "-JiGh_31GA20JabpZBff" : {
            "firstName" : "Sally",
            "lastName" : "McSwashle",
            "email" : "sally.mcswashle@co1.com",
            "organization" : "-JiGh_31GA20JabpZBfc"
        },
        "-JiGh_31GA20JabpZBea" : {
            "firstName" : "Eva",
            "lastName" : "Rushtock",
            "email" : "eva.rushtock@payrollings.com",
            "organization" : "-JiGh_31GA20JabpZBfc"
        },
        "WgnHjk5D8xbuYeA7VDM3ngKwCYV2" : {
            "firstName" : "test",
            "lastName" : "user",
            "email" : "test.user@google.com",
            "organization" : "-JiGh_31GA20JabpZBfd"
        }
    },
    "employees" : {
        "-JiGh_31GA20JabpZBeb" : {
            "organization" : "-JiGh_31GA20JabpZBfc",
            "firstName" : "Johnny",
            "lastName" : "Baggs",
            "email" : "j.baggss@financeco.com",
            "employeeNumber" : "ASV123456"
        },
        "-JiGh_31GA20JabpZBec" : {
            "organization" : "-JiGh_31GA20JabpZBfc",
            "firstName" : "Maheswari",
            "lastName" : "Sanjal",
            "email" : "mahe.sanjal@financeco.com",
            "employeeNumber" : "ASV111111"
        },
        "-JiGh_31GA20JabpZBce" : {
            "organization" : "-JiGh_31GA20JabpZBfd",
            "firstName" : "Fedde",
            "lastName" : "le Grande",
            "email" : "fedde.grande@payrollings.com",
            "employeeNumber" : "ASV111111"
        }
    }
}

Attempt #2 rules: https://gist.github.com/peteski22/e1be434cd1ea8ec2e630bec6d8aa714f

{
    "rules" : {
        "admins" : {
            ".read" : "root.child('admins').hasChild(auth.uid)",
            ".write" : "root.child('admins').hasChild(auth.uid)"
        },
        "users" : {
            ".indexOn": [ "organization" ],
            "$user" : {
                ".read" : "data.child('organization').val() === root.child('users').child(auth.uid).child('organization').val()",
                ".write" : "root.child('admins').hasChild(auth.uid)"
            }
        },
        "organizations" : {
            ".indexOn": [ "users", "employees" ],
            "$organization" : {
                ".read" : "$organization === root.child('users').child(auth.uid).child('organization').val()",
                ".write" : "root.child('admins').hasChild(auth.uid)"
            }
        },
        "employees" : {
            ".indexOn": [ "organization" ],
            "$employee" : {
                ".read" : "data.child('organization').val() === root.child('users').child(auth.uid).child('organization').val()",
                ".write" : "data.child('organization').val() === root.child('users').child(auth.uid).child('organization').val()"
            }
        }
    }
}

Now I can keep the data locked down fine in each collection, but the only way to get anything is to know the organization ID, get that organization, then get each and every employee by their ID. Although the docs above for structuring data (section: Joining Flattened Data) seem to suggest that it's fine to do this, coming from an OO and SQL background it feels very weird.. which usually means.. 'I'm doing it wrong'.

If anyone has any advice on whether I'm going down the right track, or what to try instead it would be greatly appreciated.

Thanks
Peter

Body:
After reading the docs and chatting with people in the firebase-community slack, I've come to the conclusion that I was on the right track.

I found using a compiler called "Bolt" (firebase-bolt in npm) extremely useful for generating the rules too.

Here's my structure, bolt rules and compiled JSON rules:

Structure

{
    "admins" : {
        "8UnM6LIiZJYAHVdty6gzdD8oVI42" : true
    },
    "organizations": {
        "-JiGh_31GA20JabpZBfc" : {
            "name" : "Comp1 Ltd",
            "users" : {
                "-JiGh_31GA20JabpZBfe" : true,
                "-JiGh_31GA20JabpZBff" : true,
                "-JiGh_31GA20JabpZBea" : true
            },
            "employees" : {
                "-JiGh_31GA20JabpZBeb" : true,
                "-JiGh_31GA20JabpZBec" : true
            }
        },
        "-JiGh_31GA20JabpZBfd" : {
            "name" : "company2 PLC",
            "users" :{
                "WgnHjk5D8xbuYeA7VDM3ngKwCYV2" : true
            },
            "employees" :{
                "-JiGh_31GA20JabpZBce" : true
            }
        }
    },
    "users"  : {
        "8UnM6LIiZJYAHVdty6gzdD8oVI42": {
            "firstName" : "Peter",
            "lastName" : "Piper",
            "email" : "peter.piper@testtest.com",
            "organization" : ""
        },
        "-JiGh_31GA20JabpZBfe" : {
            "firstName" : "Joe",
            "lastName" : "Blogs",
            "email" : "joe.blogs@co1.com",
            "organization" : "-JiGh_31GA20JabpZBfc"
        },
        "-JiGh_31GA20JabpZBff" : {
            "firstName" : "Sally",
            "lastName" : "McSwashle",
            "email" : "sally.mcswashle@co1.com",
            "organization" : "-JiGh_31GA20JabpZBfc"
        },
        "-JiGh_31GA20JabpZBea" : {
            "firstName" : "Eva",
            "lastName" : "Rushtock",
            "email" : "eva.rushtock@payrollings.com",
            "organization" : "-JiGh_31GA20JabpZBfc"
        },
        "WgnHjk5D8xbuYeA7VDM3ngKwCYV2" : {
            "firstName" : "test",
            "lastName" : "user",
            "email" : "test.user@google.com",
            "organization" : "-JiGh_31GA20JabpZBfd"
        }
    },
    "employees" : {
        "-JiGh_31GA20JabpZBeb" : {
            "organization" : "-JiGh_31GA20JabpZBfc",
            "firstName" : "Johnny",
            "lastName" : "Baggs",
            "email" : "j.baggss@financeco.com",
            "employeeNumber" : "ASV123456"
        },
        "-JiGh_31GA20JabpZBec" : {
            "organization" : "-JiGh_31GA20JabpZBfc",
            "firstName" : "Maheswari",
            "lastName" : "Sanjal",
            "email" : "mahe.sanjal@financeco.com",
            "employeeNumber" : "ASV111111"
        },
        "-JiGh_31GA20JabpZBce" : {
            "organization" : "-JiGh_31GA20JabpZBfd",
            "firstName" : "Fedde",
            "lastName" : "le Grande",
            "email" : "fedde.grande@payrollings.com",
            "employeeNumber" : "ASV111111"
        }
    }
}

Bolt Rules

// **********
// FUNCTIONS
// **********

function isAdmin (auth) {
    return root.admins[auth.uid] != null
}

function isInSameOrganization(auth, orgUid) {
    return root.users[auth.uid].organization === orgUid
}

// **********
// PATHS
// **********

path /admins {
    read() { isAdmin(auth) }
    write() { isAdmin(auth) }
}

path /users {
    index() { ["organization"] }
    write() { isAdmin(auth) }
}

path /users/{id} is User {
    read() { isInSameOrganization(auth, id) || isAdmin(auth) }
}

path /organizations {
    write() { isAdmin(auth) }
}

path /organizations/{id} is Organization {
    read() { isInSameOrganization(auth, id) }
}

path /employees {
    index() { ["organization"] }
    write() { isInSameOrganization(auth, this.organization) || isAdmin(auth) }
}

path /employees/{id} is Employee {
    read() { isInSameOrganization(auth, id) || isAdmin(auth) }
}

// **********
// TYPES
// **********
type OrganizationID extends String {
    validate() { root.organizations[this] != null }
}

type UserID extends String {
    validate() { root.users[this] != null }
}

type EmployeeID extends String {
    // Validate that the user ID exists in the employees node (read rule access should prevent us reading a employees that isn't in our org)
    validate() { root.employees[this] != null }
}

type Email extends String {
    validate() {
        return this.matches(/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}$/i);
    }
}

type User {
    firstName: String,
    lastName: String
    email: Email,
    organization: OrganizationID
}

type Employee {
    organization: OrganizationID,
    firstName: String,
    lastName: String,
    email: Email,
    employeeNumber: String
}

type Organization {
    name: String,
    users: Map<UserID, Boolean> | Null,
    employees: Map<EmployeeID, Boolean> | Null
}

JSON Rules (generated by Bolt)

{
  "rules": {
    "admins": {
      ".read": "root.child('admins').child(auth.uid).val() != null",
      ".write": "newData.parent().child('admins').child(auth.uid).val() != null"
    },
    "users": {
      ".write": "newData.parent().child('admins').child(auth.uid).val() != null",
      ".indexOn": [
        "organization"
      ],
      "$id": {
        ".validate": "newData.hasChildren(['firstName', 'lastName', 'email', 'organization'])",
        "firstName": {
          ".validate": "newData.isString()"
        },
        "lastName": {
          ".validate": "newData.isString()"
        },
        "email": {
          ".validate": "newData.isString() && newData.val().matches(/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\\\.[A-Z]{2,4}$/i)"
        },
        "organization": {
          ".validate": "newData.isString() && newData.parent().parent().parent().child('organizations').child(newData.val()).val() != null"
        },
        "$other": {
          ".validate": "false"
        },
        ".read": "root.child('users').child(auth.uid).child('organization').val() == $id || root.child('admins').child(auth.uid).val() != null"
      }
    },
    "organizations": {
      ".write": "newData.parent().child('admins').child(auth.uid).val() != null",
      "$id": {
        ".validate": "newData.hasChildren(['name'])",
        "name": {
          ".validate": "newData.isString()"
        },
        "users": {
          "$key1": {
            ".validate": "newData.parent().parent().parent().parent().child('users').child($key1).val() != null && newData.isBoolean()"
          },
          ".validate": "newData.hasChildren()"
        },
        "employees": {
          "$key2": {
            ".validate": "newData.parent().parent().parent().parent().child('employees').child($key2).val() != null && newData.isBoolean()"
          },
          ".validate": "newData.hasChildren()"
        },
        "$other": {
          ".validate": "false"
        },
        ".read": "root.child('users').child(auth.uid).child('organization').val() == $id"
      }
    },
    "employees": {
      ".write": "newData.parent().child('users').child(auth.uid).child('organization').val() == newData.child('organization').val() || newData.parent().child('admins').child(auth.uid).val() != null",
      ".indexOn": [
        "organization"
      ],
      "$id": {
        ".validate": "newData.hasChildren(['organization', 'firstName', 'lastName', 'email', 'employeeNumber'])",
        "organization": {
          ".validate": "newData.isString() && newData.parent().parent().parent().child('organizations').child(newData.val()).val() != null"
        },
        "firstName": {
          ".validate": "newData.isString()"
        },
        "lastName": {
          ".validate": "newData.isString()"
        },
        "email": {
          ".validate": "newData.isString() && newData.val().matches(/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\\\.[A-Z]{2,4}$/i)"
        },
        "employeeNumber": {
          ".validate": "newData.isString()"
        },
        "$other": {
          ".validate": "false"
        },
        ".read": "root.child('users').child(auth.uid).child('organization').val() == $id || root.child('admins').child(auth.uid).val() != null"
      }
    }
  }
}

I'm still finding bugs in this, but I think it's showing progress. Just to note that there are some good official & unofficial Firebase videos on Youtube, the docs are fairly decent for the most part, and the firebase-community seems to be friendly. So any new comers like me, you know where to start.


来源: stackexchange

问:
Ignore non existing attributes in config file parser python
I have a function returning 1 list at a time, like below

```<code>['7:49', 'Section1', '181', '1578', '634', '4055']
['7:49', 'Section2', '181', '1578', '634', '4055']
</code>```

These values are time,section,count,avg,min,max (I know this will always be the sequence)
My aim is to alert if any of the values breaches limits defined in a config file.

So I create a config like below

```<code>[Section1]
Count:10
Min:20
Max:100
Avg:50
[Section2]
Count:10
Min:20
Max:100
Avg:50
</code>```

My function to check max and min limits

```<code>def checklimit(line):
    print "Inside CheckLimit", line[1],line[4],line[5]
    if line[4] < ConfigSectionMap(line[1])['min'] or line[5] > ConfigSectionMap(line[1])['max']:
        sendAlert(line)
</code>```

This works fine but this could be improved and has some corner cases.
Suppose someone leaves config as below

```<code>[Section1]
Count:10
Min:
Max:
Avg:50
[Section2]
Count:10
Avg:50
</code>```

Meaning the user only wants to check for Count and Avg. How should these cases be handled in my code so as to check only required parameters given in config file. I have used Config Parser from here

Suggestions for question title improvement are welcome. It was hard to put one. Thanks

答:
There's a many ways to approach this. With key lookups, in dictionaries you can use the dict.get() method and provide a fallback value.

so instead of

```<code>ConfigSectionMap(line[1])['min']
</code>```

You can use something like this, which will return 0 if the key does not exist.

```<code>ConfigSectionMap(line[1]).get('min', 0)
</code>```

ContentLicense: CC BY-SA 3.0

Body:
I have a function returning 1 list at a time, like below

['7:49', 'Section1', '181', '1578', '634', '4055']
['7:49', 'Section2', '181', '1578', '634', '4055']

These values are time,section,count,avg,min,max (I know this will always be the sequence)
My aim is to alert if any of the values breaches limits defined in a config file.

So I create a config like below

[Section1]
Count:10
Min:20
Max:100
Avg:50
[Section2]
Count:10
Min:20
Max:100
Avg:50

My function to check max and min limits

def checklimit(line):
    print "Inside CheckLimit", line[1],line[4],line[5]
    if line[4] < ConfigSectionMap(line[1])['min'] or line[5] > ConfigSectionMap(line[1])['max']:
        sendAlert(line)

This works fine but this could be improved and has some corner cases.
Suppose someone leaves config as below

[Section1]
Count:10
Min:
Max:
Avg:50
[Section2]
Count:10
Avg:50

Meaning the user only wants to check for Count and Avg. How should these cases be handled in my code so as to check only required parameters given in config file. I have used Config Parser from here

Suggestions for question title improvement are welcome. It was hard to put one. Thanks

Body:
There's a many ways to approach this. With key lookups, in dictionaries you can use the dict.get() method and provide a fallback value.

so instead of

ConfigSectionMap(line[1])['min']

You can use something like this, which will return 0 if the key does not exist.

ConfigSectionMap(line[1]).get('min', 0)


问:
MPEG-DASH: standard "available time" parameter in manifests for "live-dvr"
Related: Terminology: "live-dvr" in mpeg-dash streaming

I'm a little bit confused about the MPEG-DASH standard and an use case. I would like to know if there's a way to specify in MPEG-DASH manifests for a "live-dvr" setup the amount of available time for seeking back in players.

That is, for example, if a "live-dvr" stream has 30' of media available for replay, what would be a standard way to specify this in the manifest.

I know I can configure a given player for a desired behaviour. My question is not about players but about the manifests.

I don't fully understand yet if this use case is formally addresed in the standard or not (see the related link). I'm guessing a relation between @timeShiftBufferDepth and @presentationTimeOffset should work, but i'm confused regarding how it should manage "past time" instead of terms like "length" or "duration".

Thanks in advance.

答:
Yes - you are on the right lines.

The MPEG DASH implementation guidelines provide this formula (my bolding):

The CheckTime is defined on the MPD-documented media time axis; when the client’s playback time reaches CheckTime - MPD@minBufferTime it should fetch a new MPD.

Then, the Media Segment list is further restricted by the CheckTime together with the MPD attribute MPD@timeShiftBufferDepth such that only Media Segments for which the sum of the start time of the Media Segment and the Period start time falls in the interval [NOW- MPD@timeShiftBufferDepth - @duration, min(CheckTime, NOW)] are included.

The full guidelines are available at:

http://mpeg.chiariglione.org/standards/mpeg-dash/implementation-guidelines/text-isoiec-dtr-23009-3-2nd-edition-dash


问:
Angular 2 :put all form fields in json object when submitted
I have a form have multiple fields and I want to retrieve the data when submit it.

this is the component.html :

```<code><div class="ui raised segment">
  <h2 class="ui header">Demo Form: Sku</h2>
  <form #f="ngForm"
        (ngSubmit)="onSubmit(f.value)"
        class="ui form">

    <div class="field">
      <label for="skuInput">SKU</label>
      <input type="text"
             id="skuInput"
             placeholder="SKU"
             name="sku" ngModel>
    </div>
    <div class="field">
      <label for="select1" class="col-md-4 col-lg-4 col-sm-2 align form-control-label">Langues:français/anglais: </label>
      <select class="form-control" name="note" id="select1" ngModel>
        <option>1</option>
        <option>2</option>
        <option>3</option>
        <option>4</option>
        <option>5</option>
      </select>

      <label for="select2" class="col-md-4 col-lg-4 col-sm-2 align form-control-label">Langues:français/anglais: </label>
      <select class="form-control" name="note" id="select2" ngModel>
        <option>1</option>
        <option>2</option>
        <option>3</option>
        <option>4</option>
        <option>5</option>
      </select>

      <label for="select3" class="col-md-4 col-lg-4 col-sm-2 align form-control-label">Langues:français/anglais: </label>
      <select class="form-control" name="note" id="select3" ngModel>
        <option>1</option>
        <option>2</option>
        <option>3</option>
        <option>4</option>
        <option>5</option>
      </select>

    </div>

    <button type="submit" class="ui button">Submit</button>
  </form>
</div>
</code>```

and here the component.ts :

```<code>import {Component, OnInit} from '@angular/core';

@Component({
  selector: 'app-post-history',
  templateUrl: './post-history.component.html',
  styleUrls: ['./post-history.component.css']
})
export class PostHistoryComponent implements OnInit {

  constructor() {
  }

  ngOnInit() {
  }

  onSubmit(form: any): void {
    console.log('you submitted value:', form);
  }

}
</code>```

in the console log it show me only the text input and only one select value !

答:
All of your select elements have the same name attribute of "note". You could change them to match the id's of the elements:

```<code><select class="form-control" name="note1" id="select1" ngModel>

<select class="form-control" name="note2" id="select2" ngModel>

// etc
</code>```

Body:
I have a form have multiple fields and I want to retrieve the data when submit it.

this is the component.html :

Demo Form: Sku

  <form #f="ngForm"
        (ngSubmit)="onSubmit(f.value)"
        class="ui form">

      SKU
      <input type="text"
             id="skuInput"
             placeholder="SKU"
             name="sku" ngModel>

      Langues:français/anglais:
      <select class="form-control" name="note" id="select1" ngModel>
        <option>1</option>
        <option>2</option>
        <option>3</option>
        <option>4</option>
        <option>5</option>
      </select>

      Langues:français/anglais:
      <select class="form-control" name="note" id="select2" ngModel>
        <option>1</option>
        <option>2</option>
        <option>3</option>
        <option>4</option>
        <option>5</option>
      </select>

      Langues:français/anglais:
      <select class="form-control" name="note" id="select3" ngModel>
        <option>1</option>
        <option>2</option>
        <option>3</option>
        <option>4</option>
        <option>5</option>
      </select>

    <button type="submit" class="ui button">Submit</button>
  </form>

and here the component.ts :

import {Component, OnInit} from '@angular/core';

@Component({
  selector: 'app-post-history',
  templateUrl: './post-history.component.html',
  styleUrls: ['./post-history.component.css']
})
export class PostHistoryComponent implements OnInit {

  constructor() {
  }

  ngOnInit() {
  }

  onSubmit(form: any): void {
    console.log('you submitted value:', form);
  }

}

in the console log it show me only the text input and only one select value !

Body:
All of your select elements have the same name attribute of "note". You could change them to match the id's of the elements:

<select class="form-control" name="note1" id="select1" ngModel>

<select class="form-control" name="note2" id="select2" ngModel>

// etc


问:
Pros & cons between <MyComp /> and React.createElement(MyComp)
What's main idea to use  React.createElement ?

What's differences between :

```<code>const myProps = { initialValue: 1 };
const Comp = React.createElement(MyComp, { ...myProps, ref: "mycomp" });
</code>```

And

```<code><MyComp ref="mycomp"    initialValue ="1"   />
</code>```

答:
<MyComp ref="mycomp" initialValue ="1"   />

transpiles to:

```<code>"use strict";

React.createElement(MyComp, { ref: "mycomp", initialValue: "1" });
</code>```

Which is not too different from

```<code>const myProps = { initialValue: 1 };
const Comp = React.createElement(MyComp, { ...myProps, ref: "mycomp" });
</code>```

The main differences in terms of usages are quite subjective; some people find it the JSX declarative syntax easier to understand but this is beyond the scope of SO.

Body:
What's main idea to use  React.createElement ?

What's differences between :

const myProps = { initialValue: 1 };
const Comp = React.createElement(MyComp, { ...myProps, ref: "mycomp" });

And

<MyComp ref="mycomp"    initialValue ="1"   />

Body:
<MyComp ref="mycomp" initialValue ="1"   />

transpiles to:

"use strict";

React.createElement(MyComp, { ref: "mycomp", initialValue: "1" });

Which is not too different from

const myProps = { initialValue: 1 };
const Comp = React.createElement(MyComp, { ...myProps, ref: "mycomp" });

The main differences in terms of usages are quite subjective; some people find it the JSX declarative syntax easier to understand but this is beyond the scope of SO.


问:
Django group based User Access Control
In my Django admin i added new field called privilage class along with groups and user permissions .

so, my user table having privilage_class based on i am giving access to multiple groups. each groups having user_permissions.

I made admin setup but permissions was not implemented . what to do beyond?

for eg:

```<code>user having priv class executive
Privilege class if executive he having access to multiple groups->
    Assets
    Billing
    etc
</code>```

Each groups carries user permissions (URL),

```<code>Assets -> assets related URL
Billing -> related URLs
etc...
</code>```

setup has been done. but dont know how to apply permission.

i know difficult to understand but pro will understand. so kindly help me regarding this.

i am using django and DRF as well

Thanks in advance!

答:
You have to write your own custom permission class to decide what to do when a user belongs to a particular privilege class. This url [1] has the docs to implement it.

An example implementation:

```<code>class UserPrevilagePermission(permissions.BasePermission):

    def has_permission(self, request, view):
        user = request.user
        # check if the user has required permission based on request url
        if 'assets' in request.path:
           # decide which perm to check
           if user.has_perm('perm-name-to-check'):
               return True
        return False
</code>```

Now use this permission class in your views as explained here [2].

Check out django's permission methods on the user object from here [3].

[1] http://www.django-rest-framework.org/api-guide/permissions/#custom-permissions

[2] http://www.django-rest-framework.org/api-guide/permissions/#setting-the-permission-policy

[3] https://docs.djangoproject.com/en/1.11/ref/contrib/auth/#methods

Title: Django group based User Access Control

Body:
In my Django admin i added new field called privilage class along with groups and user permissions .

so, my user table having privilage_class based on i am giving access to multiple groups. each groups having user_permissions.

I made admin setup but permissions was not implemented . what to do beyond?

for eg:

user having priv class executive
Privilege class if executive he having access to multiple groups->
    Assets
    Billing
    etc

Each groups carries user permissions (URL),

Assets -> assets related URL
Billing -> related URLs
etc...

setup has been done. but dont know how to apply permission.

i know difficult to understand but pro will understand. so kindly help me regarding this.

i am using django and DRF as well

Thanks in advance!

Body:
You have to write your own custom permission class to decide what to do when a user belongs to a particular privilege class. This url [1] has the docs to implement it.

An example implementation:

class UserPrevilagePermission(permissions.BasePermission):

    def has_permission(self, request, view):
        user = request.user
        # check if the user has required permission based on request url
        if 'assets' in request.path:
           # decide which perm to check
           if user.has_perm('perm-name-to-check'):
               return True
        return False

Now use this permission class in your views as explained here [2].

Check out django's permission methods on the user object from here [3].

[1] http://www.django-rest-framework.org/api-guide/permissions/#custom-permissions

[2] http://www.django-rest-framework.org/api-guide/permissions/#setting-the-permission-policy

[3] https://docs.djangoproject.com/en/1.11/ref/contrib/auth/#methods


问:
How Indexing nested documents using spring solr data
can anybody help me to explain how I can index nested documents? I'am using the lastest version of spring-data-solr , I have a document with a List and annoted as follow:

```<code>@Indexed(required = true,stored = true)
@Field(child = true,value = "werkzeuge")
private List<Werkzeug> werkzeuge;
</code>```

But the document within solr has no field werkzeuge.

Iam using the SOLR schemaless configuration.

Many Thanks,

答: Nested Document support has been recently added to Spring Data for Apache Solr. At the time of writing there are only snapshot builds for this feature available. Please see DATASOLR-394 and the documentation for details.</p>

Body:
can anybody help me to explain how I can index nested documents? I'am using the lastest version of spring-data-solr , I have a document with a List and annoted as follow:

@Indexed(required = true,stored = true)
@Field(child = true,value = "werkzeuge")
private List<Werkzeug> werkzeuge;

But the document within solr has no field werkzeuge.

Iam using the SOLR schemaless configuration.

Many Thanks,


问:
Android Firebase Push notification not working when application is killed
I have created android application to receive push notification from server but it is not working.

When app is in foreground, it works perfectly. But when we force close app from system tray it does not work.

Below is my json code which I send to FCM.

```<code>{"to":"\/topics\/global","data":{"title":"Title","body":"Body"}}
</code>```

Below is my android function

```<code>public void onMessageReceived(RemoteMessage remoteMessage) {
        super.onMessageReceived(remoteMessage);

        Log.d("Msg", "Message received ["+remoteMessage+"]");

        Map<String, String> data = remoteMessage.getData();

        DatabaseHandler db = new DatabaseHandler(getApplicationContext());
        db.addData(data.get("body"));

        Intent resultIntent = new Intent(getApplicationContext(), MainActivity.class);
        resultIntent.putExtra("message", data.get("body"));

        showNotificationMessage(getApplicationContext(), data.get("title"), data.get("body"), "", resultIntent);
    }
</code>```

Below is code to show notification.

```<code>private void showNotificationMessage(Context context, String title, String message, String timeStamp, Intent intent) {
        notificationUtils = new NotificationUtils(context);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        notificationUtils.showNotificationMessage(title, message, timeStamp, intent);
    }
</code>```

I can not find any problem, can any one please help me.

Thank you very much in advance.

Edit :

I have solved my problem by changing phone, in samsung device code is working fine but its problem with MI device itself.

I have also got solution for MI devices, use below steps to enable auto start that application.

Go to settings --> permissions --> autostart. From there, pick the apps you want to receive notifications, and toggle the switch to turn it on.

答:
From here,

When in the background, apps receive the notification payload in the notification tray, and only handle the data payload when the user taps on the notification.

And from this,

This includes messages that contain both notification and data payload (and all messages sent from the Notifications console). In these cases, the notification is delivered to the device's system tray, and the data payload is delivered in the extras of the intent of your launcher Activity

You might get complete help from receive message tutorial

Body:
I have created android application to receive push notification from server but it is not working.

When app is in foreground, it works perfectly. But when we force close app from system tray it does not work.

Below is my json code which I send to FCM.

{"to":"\/topics\/global","data":{"title":"Title","body":"Body"}}

Below is my android function

public void onMessageReceived(RemoteMessage remoteMessage) {
        super.onMessageReceived(remoteMessage);

        Log.d("Msg", "Message received ["+remoteMessage+"]");

        Map<String, String> data = remoteMessage.getData();

        DatabaseHandler db = new DatabaseHandler(getApplicationContext());
        db.addData(data.get("body"));

        Intent resultIntent = new Intent(getApplicationContext(), MainActivity.class);
        resultIntent.putExtra("message", data.get("body"));

        showNotificationMessage(getApplicationContext(), data.get("title"), data.get("body"), "", resultIntent);
    }

Below is code to show notification.

private void showNotificationMessage(Context context, String title, String message, String timeStamp, Intent intent) {
        notificationUtils = new NotificationUtils(context);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        notificationUtils.showNotificationMessage(title, message, timeStamp, intent);
    }

I can not find any problem, can any one please help me.

Thank you very much in advance.

Edit :

I have solved my problem by changing phone, in samsung device code is working fine but its problem with MI device itself.

I have also got solution for MI devices, use below steps to enable auto start that application.

Go to settings --> permissions --> autostart. From there, pick the apps you want to receive notifications, and toggle the switch to turn it on.


问:
No qualifying bean of type 'javax.persistence.EntityManager' available: expected single matching bean but found 2
I have two entity manager configurations for two separate databases but when I try to auto-wire an entity manager in to configure my GraphQLExecutor bean I get an exception stating that there are two beans that match the criteria even though I've specified a unit name in the PersistenceContext.

Exception

org.springframework.beans.factory.BeanCreationException: Error
  creating bean with name 'graphQLExecutor': Injection of resource
  dependencies failed; nested exception is
  org.springframework.beans.factory.NoUniqueBeanDefinitionException: No
  qualifying bean of type 'javax.persistence.EntityManager' available:
  expected single matching bean but found 2:
  org.springframework.orm.jpa.SharedEntityManagerCreator#0,org.springframework.orm.jpa.SharedEntityManagerCreator#1
  at
  org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java:321)
  ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE] at
  org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1264)
  ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE] at
  org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
  ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE] at
  org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
  ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE] at
  org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
  ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE] at
  org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
  ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE] at
  org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
  ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE] at
  org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
  ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE] at
  org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)
  ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE] at
  org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
  ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE] at
  org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)
  ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE] at
  org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
  ~[spring-boot-1.5.4.RELEASE.jar:1.5.4.RELEASE] at
  org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693)
  [spring-boot-1.5.4.RELEASE.jar:1.5.4.RELEASE] at
  org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360)
  [spring-boot-1.5.4.RELEASE.jar:1.5.4.RELEASE] at
  org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
  [spring-boot-1.5.4.RELEASE.jar:1.5.4.RELEASE] at
  org.springframework.boot.SpringApplication.run(SpringApplication.java:1118)
  [spring-boot-1.5.4.RELEASE.jar:1.5.4.RELEASE] at
  org.springframework.boot.SpringApplication.run(SpringApplication.java:1107)
  [spring-boot-1.5.4.RELEASE.jar:1.5.4.RELEASE] at
  com.ogl.JpaDemoApplication.main(JpaDemoApplication.java:15)
  [classes/:na] Caused by:
  org.springframework.beans.factory.NoUniqueBeanDefinitionException: No
  qualifying bean of type 'javax.persistence.EntityManager' available:
  expected single matching bean but found 2:
  org.springframework.orm.jpa.SharedEntityManagerCreator#0,org.springframework.orm.jpa.SharedEntityManagerCreator#1
    at
  org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:173)
  ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]   at
  org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1116)
  ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]   at
  org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
  ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]   at
  org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:518)
  ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE]     at
  org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:496)
  ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE]     at
  org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:627)
  ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE]     at
  org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:169)
  ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]   at
  org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
  ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]   at
  org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java:318)
  ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE]     ... 17 common
  frames omitted

Entity Manager 1

```<code>package com.ogl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

@Configuration
@EnableJpaRepositories(basePackages = "com.ogl.system", entityManagerFactoryRef = "companyEntityManagerFactory", transactionManagerRef = "companyTransactionManager")
public class SystemJpaConfig {

  private final Environment environment;

  @Autowired
  public SystemJpaConfig(Environment environment) {
    this.environment = environment;
  }

  @Bean("systemEntityManagerFactory")
  public LocalContainerEntityManagerFactoryBean systemEntityManagerFactory() {
    LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
    entityManagerFactoryBean.setPackagesToScan("com.ogl.system");
    entityManagerFactoryBean.setPersistenceUnitName("system");
    entityManagerFactoryBean.setDataSource(systemDataSource());

    HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
    adapter.setDatabase(Database.POSTGRESQL);
    adapter.setShowSql(true);
    adapter.setGenerateDdl(false);

    entityManagerFactoryBean.setJpaVendorAdapter(adapter);

    return entityManagerFactoryBean;
  }

  @Bean
  DataSource systemDataSource() {
    DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setDriverClassName(environment.getProperty("p4.datasource.driver"));
    dataSource.setUrl(environment.getProperty("p4.system.url"));
    dataSource.setUsername(environment.getProperty("p4.system.user"));
    dataSource.setPassword(environment.getProperty("p4.system.password"));

    return dataSource;
  }

  @Bean
  public PlatformTransactionManager systemTransactionManager() {
    JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManager.setEntityManagerFactory((systemEntityManagerFactory().getObject()));

    return transactionManager;
  }
}
</code>```

Entity Manager 2

```<code>package com.ogl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

@Configuration
@EnableJpaRepositories(basePackages = "com.ogl.company", entityManagerFactoryRef = "companyEntityManagerFactory", transactionManagerRef = "companyTransactionManager")
public class CompanyJpaConfig {

  private final Environment environment;

  @Autowired
  public CompanyJpaConfig(Environment environment) {
    this.environment = environment;
  }

  @Primary
  @Bean("companyEntityManagerFactory")
  public LocalContainerEntityManagerFactoryBean companyEntityManagerFactory() {
    LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
    entityManagerFactoryBean.setPackagesToScan("com.ogl.company");
    entityManagerFactoryBean.setPersistenceUnitName("company");
    entityManagerFactoryBean.setDataSource(companyDataSource());

    HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
    adapter.setDatabase(Database.POSTGRESQL);
    adapter.setShowSql(true);
    adapter.setGenerateDdl(false);

    entityManagerFactoryBean.setJpaVendorAdapter(adapter);

    return entityManagerFactoryBean;
  }

  @Bean
  DataSource companyDataSource() {
    DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setDriverClassName(environment.getProperty("p4.datasource.driver"));
    dataSource.setUrl(environment.getProperty("p4.company.url"));
    dataSource.setUsername(environment.getProperty("p4.company.user"));
    dataSource.setPassword(environment.getProperty("p4.company.password"));

    return dataSource;
  }

  @Primary
  @Bean
  public PlatformTransactionManager companyTransactionManager() {
    JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManager.setEntityManagerFactory((companyEntityManagerFactory().getObject()));

    return transactionManager;
  }
}
</code>```

Injection

```<code>package com.ogl;

import org.crygier.graphql.GraphQLExecutor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Configuration
@ComponentScan
public class GraphQLJpaConfig {

  @PersistenceContext(unitName = "company")
  private EntityManager entityManager;

  @Bean
  public GraphQLExecutor graphQLExecutor() {
    return new GraphQLExecutor(entityManager);
  }
}
</code>```

pom.xml

```<code><?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.ogl</groupId>
    <artifactId>jpa-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>jpa-demo</name>
    <description>Demo project for JPA</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.graphql-java</groupId>
            <artifactId>graphql-java</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.crygier</groupId>
            <artifactId>graphql-jpa</artifactId>
            <version>0.3</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
</code>```

答:
You have defined two entitymanager. Now you have to tell spring which one should injected. For this you can use the  @Qualifier Annotation:

```<code>@PersistenceContext(unitName = "company")
@Qualifier(<Name of the entitimanager you want to use>)
private EntityManager entityManager;
</code>```

Body:
I have two entity manager configurations for two separate databases but when I try to auto-wire an entity manager in to configure my GraphQLExecutor bean I get an exception stating that there are two beans that match the criteria even though I've specified a unit name in the PersistenceContext.

Exception

org.springframework.beans.factory.BeanCreationException: Error
  creating bean with name 'graphQLExecutor': Injection of resource
  dependencies failed; nested exception is
  org.springframework.beans.factory.NoUniqueBeanDefinitionException: No
  qualifying bean of type 'javax.persistence.EntityManager' available:
  expected single matching bean but found 2:
  org.springframework.orm.jpa.SharedEntityManagerCreator#0,org.springframework.orm.jpa.SharedEntityManagerCreator#1
  at
  org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java:321)
  ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE] at
  org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1264)
  ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE] at
  org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
  ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE] at
  org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
  ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE] at
  org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
  ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE] at
  org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
  ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE] at
  org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
  ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE] at
  org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
  ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE] at
  org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)
  ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE] at
  org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
  ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE] at
  org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)
  ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE] at
  org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
  ~[spring-boot-1.5.4.RELEASE.jar:1.5.4.RELEASE] at
  org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693)
  [spring-boot-1.5.4.RELEASE.jar:1.5.4.RELEASE] at
  org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360)
  [spring-boot-1.5.4.RELEASE.jar:1.5.4.RELEASE] at
  org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
  [spring-boot-1.5.4.RELEASE.jar:1.5.4.RELEASE] at
  org.springframework.boot.SpringApplication.run(SpringApplication.java:1118)
  [spring-boot-1.5.4.RELEASE.jar:1.5.4.RELEASE] at
  org.springframework.boot.SpringApplication.run(SpringApplication.java:1107)
  [spring-boot-1.5.4.RELEASE.jar:1.5.4.RELEASE] at
  com.ogl.JpaDemoApplication.main(JpaDemoApplication.java:15)
  [classes/:na] Caused by:
  org.springframework.beans.factory.NoUniqueBeanDefinitionException: No
  qualifying bean of type 'javax.persistence.EntityManager' available:
  expected single matching bean but found 2:
  org.springframework.orm.jpa.SharedEntityManagerCreator#0,org.springframework.orm.jpa.SharedEntityManagerCreator#1
    at
  org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:173)
  ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]   at
  org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1116)
  ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]   at
  org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
  ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]   at
  org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:518)
  ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE]     at
  org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:496)
  ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE]     at
  org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:627)
  ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE]     at
  org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:169)
  ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]   at
  org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
  ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]   at
  org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java:318)
  ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE]     ... 17 common
  frames omitted

Entity Manager 1

package com.ogl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

@Configuration
@EnableJpaRepositories(basePackages = "com.ogl.system", entityManagerFactoryRef = "companyEntityManagerFactory", transactionManagerRef = "companyTransactionManager")
public class SystemJpaConfig {

  private final Environment environment;

  @Autowired
  public SystemJpaConfig(Environment environment) {
    this.environment = environment;
  }

  @Bean("systemEntityManagerFactory")
  public LocalContainerEntityManagerFactoryBean systemEntityManagerFactory() {
    LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
    entityManagerFactoryBean.setPackagesToScan("com.ogl.system");
    entityManagerFactoryBean.setPersistenceUnitName("system");
    entityManagerFactoryBean.setDataSource(systemDataSource());

    HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
    adapter.setDatabase(Database.POSTGRESQL);
    adapter.setShowSql(true);
    adapter.setGenerateDdl(false);

    entityManagerFactoryBean.setJpaVendorAdapter(adapter);

    return entityManagerFactoryBean;
  }

  @Bean
  DataSource systemDataSource() {
    DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setDriverClassName(environment.getProperty("p4.datasource.driver"));
    dataSource.setUrl(environment.getProperty("p4.system.url"));
    dataSource.setUsername(environment.getProperty("p4.system.user"));
    dataSource.setPassword(environment.getProperty("p4.system.password"));

    return dataSource;
  }

  @Bean
  public PlatformTransactionManager systemTransactionManager() {
    JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManager.setEntityManagerFactory((systemEntityManagerFactory().getObject()));

    return transactionManager;
  }
}

Entity Manager 2

package com.ogl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

@Configuration
@EnableJpaRepositories(basePackages = "com.ogl.company", entityManagerFactoryRef = "companyEntityManagerFactory", transactionManagerRef = "companyTransactionManager")
public class CompanyJpaConfig {

  private final Environment environment;

  @Autowired
  public CompanyJpaConfig(Environment environment) {
    this.environment = environment;
  }

  @Primary
  @Bean("companyEntityManagerFactory")
  public LocalContainerEntityManagerFactoryBean companyEntityManagerFactory() {
    LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
    entityManagerFactoryBean.setPackagesToScan("com.ogl.company");
    entityManagerFactoryBean.setPersistenceUnitName("company");
    entityManagerFactoryBean.setDataSource(companyDataSource());

    HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
    adapter.setDatabase(Database.POSTGRESQL);
    adapter.setShowSql(true);
    adapter.setGenerateDdl(false);

    entityManagerFactoryBean.setJpaVendorAdapter(adapter);

    return entityManagerFactoryBean;
  }

  @Bean
  DataSource companyDataSource() {
    DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setDriverClassName(environment.getProperty("p4.datasource.driver"));
    dataSource.setUrl(environment.getProperty("p4.company.url"));
    dataSource.setUsername(environment.getProperty("p4.company.user"));
    dataSource.setPassword(environment.getProperty("p4.company.password"));

    return dataSource;
  }

  @Primary
  @Bean
  public PlatformTransactionManager companyTransactionManager() {
    JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManager.setEntityManagerFactory((companyEntityManagerFactory().getObject()));

    return transactionManager;
  }
}

Injection

package com.ogl;

import org.crygier.graphql.GraphQLExecutor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Configuration
@ComponentScan
public class GraphQLJpaConfig {

  @PersistenceContext(unitName = "company")
  private EntityManager entityManager;

  @Bean
  public GraphQLExecutor graphQLExecutor() {
    return new GraphQLExecutor(entityManager);
  }
}

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.ogl</groupId>
    <artifactId>jpa-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>jpa-demo</name>
    <description>Demo project for JPA</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.4.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.graphql-java</groupId>
            <artifactId>graphql-java</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.crygier</groupId>
            <artifactId>graphql-jpa</artifactId>
            <version>0.3</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

Body:
You have defined two entitymanager. Now you have to tell spring which one should injected. For this you can use the  @Qualifier Annotation:

@PersistenceContext(unitName = "company")
@Qualifier(<Name of the entitimanager you want to use>)
private EntityManager entityManager;


问:
Why are non-placement `new` and `delete` built into the language and not just regular functions?
Why were the non-placement new expression and the delete expression implemented as language built-in instead of regular functions?

If we have...

a way of requesting/giving back memory to the OS

a way of explicitly invoking a constructor (placement new)

a way of explicitly invoking a destructor (~T())

...why couldn't non-placement new and delete just be regular functions in the Standard Library? Example:

```<code>template <typename T, typename... Ts>
T* library_new(Ts&&... xs)
{
    auto* ptr = /* request enough memory for `T` from OS */;
    new (ptr) T(std::forward<Ts>(xs)...);
    return ptr;
}

template <typename T>
void library_delete(T* ptr)
{
    ptr->~T();
    /* reclaim memory for `T` from OS */
}
</code>```

答:
Maybe this is not the best reference but this is what Wikipedia says about placement new in C++:

In earlier versions of C++ there was no such thing as placement new; instead, developers used explicit assignment to this within constructors to achieve similar effect. This practice has been deprecated and abolished later, and third edition of The "C++ Programming Language" doesn't mention this technique. Support for placement new operator has been added to compilers circa 1995.

Maybe in 2017 it is possible to implement new as a standard library function. Your suggested implementation uses language features that were added recently (many of them after 2010).

The C++ language, however, is much older (since 1983) and in the beginning there were no variadic templates, no typename, no placement new, no forwarding references.

In the beginning there was only the regular new and it had to be a language feature at that time because there was no way to implement it as a library function.

Body:
Why were the non-placement new expression and the delete expression implemented as language built-in instead of regular functions?

If we have...

a way of requesting/giving back memory to the OS

a way of explicitly invoking a constructor (placement new)

a way of explicitly invoking a destructor (~T())

...why couldn't non-placement new and delete just be regular functions in the Standard Library? Example:

template <typename T, typename... Ts>
T* library_new(Ts&&... xs)
{
    auto* ptr = /* request enough memory for `T` from OS */;
    new (ptr) T(std::forward<Ts>(xs)...);
    return ptr;
}

template <typename T>
void library_delete(T* ptr)
{
    ptr->~T();
    /* reclaim memory for `T` from OS */
}


问:
Five star rating component: How to get rid of all JavaScript code and replace it with only CSS?
I built a five star simple component (to be used for ranking). The component works fine. I used JavaScript to save the result (when user click on a star (which is not shown here) ).

The problem I have is that I didn't found a proper way to simulate the mouseover event (when you pass the mouse over a star and before clicking). So I did it using JavaScript.

Is there a proper way to use only CSS to achieve this (instead of all those JS lines)?

I'm sharing what I already did.

document.getElementById('star1').addEventListener('mouseover', function(){
 this.classList.remove("fa-star-o");
 this.classList.add("fa-star");
});
document.getElementById('star1').addEventListener('mouseout', function(){
 this.classList.remove("fa-star");
 this.classList.add("fa-star-o");
});

document.getElementById('star2').addEventListener('mouseover', function(){
 this.classList.remove("fa-star-o");
 this.classList.add("fa-star");
 document.getElementById('star1').classList.remove("fa-star-o");
 document.getElementById('star1').classList.add("fa-star");
});
document.getElementById('star2').addEventListener('mouseout', function(){
 this.classList.remove("fa-star");
 this.classList.add("fa-star-o");
 document.getElementById('star1').classList.remove("fa-star");
 document.getElementById('star1').classList.add("fa-star-o");
});

document.getElementById('star3').addEventListener('mouseover', function(){
 this.classList.remove("fa-star-o");
 this.classList.add("fa-star");
 document.getElementById('star1').classList.remove("fa-star-o");
 document.getElementById('star1').classList.add("fa-star");
 document.getElementById('star2').classList.remove("fa-star-o");
 document.getElementById('star2').classList.add("fa-star");
});
document.getElementById('star3').addEventListener('mouseout', function(){
 this.classList.remove("fa-star");
 this.classList.add("fa-star-o");
 document.getElementById('star1').classList.remove("fa-star");
 document.getElementById('star1').classList.add("fa-star-o");
 document.getElementById('star2').classList.remove("fa-star");
 document.getElementById('star2').classList.add("fa-star-o");
});

document.getElementById('star4').addEventListener('mouseover', function(){
 this.classList.remove("fa-star-o");
 this.classList.add("fa-star");
 document.getElementById('star1').classList.remove("fa-star-o");
 document.getElementById('star1').classList.add("fa-star");
 document.getElementById('star2').classList.remove("fa-star-o");
 document.getElementById('star2').classList.add("fa-star");
 document.getElementById('star3').classList.remove("fa-star-o");
 document.getElementById('star3').classList.add("fa-star");
});
document.getElementById('star4').addEventListener('mouseout', function(){
 this.classList.remove("fa-star");
 this.classList.add("fa-star-o");
 document.getElementById('star1').classList.remove("fa-star");
 document.getElementById('star1').classList.add("fa-star-o");
 document.getElementById('star2').classList.remove("fa-star");
 document.getElementById('star2').classList.add("fa-star-o");
 document.getElementById('star3').classList.remove("fa-star");
 document.getElementById('star3').classList.add("fa-star-o");
});

document.getElementById('star5').addEventListener('mouseover', function(){
 this.classList.remove("fa-star-o");
 this.classList.add("fa-star");
 document.getElementById('star1').classList.remove("fa-star-o");
 document.getElementById('star1').classList.add("fa-star");
 document.getElementById('star2').classList.remove("fa-star-o");
 document.getElementById('star2').classList.add("fa-star");
 document.getElementById('star3').classList.remove("fa-star-o");
 document.getElementById('star3').classList.add("fa-star");
 document.getElementById('star4').classList.remove("fa-star-o");
 document.getElementById('star4').classList.add("fa-star");
});
document.getElementById('star5').addEventListener('mouseout', function(){
 this.classList.remove("fa-star");
 this.classList.add("fa-star-o");
 document.getElementById('star1').classList.remove("fa-star");
 document.getElementById('star1').classList.add("fa-star-o");
 document.getElementById('star2').classList.remove("fa-star");
 document.getElementById('star2').classList.add("fa-star-o");
 document.getElementById('star3').classList.remove("fa-star");
 document.getElementById('star3').classList.add("fa-star-o");
 document.getElementById('star4').classList.remove("fa-star");
 document.getElementById('star4').classList.add("fa-star-o");
});```
<pre class="snippet-code-css lang-css prettyprint-override"><code>.content > span > i{
    font-size: 50px;
    margin-right: 10px;
    color: #f0d124;
 cursor: pointer;
}
#star1:hover #star1{
 /*an example: I don't know what to do here???*/
}</code>```

<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>

```

答:
Here is one way of doing it with FontAwesome that your are using:

body {
  font-size: 44px;
}

.content {
  width: 200px;
  display: flex;
  flex-direction: row-reverse;
}

span:hover i:before,
span:hover~span i:before {
  content: "\f005";
}```
<pre class="snippet-code-html lang-html prettyprint-override"><code><link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<div class="content">
  <span><i id="star1" class="fa fa-star-o"></i></span>
  <span><i id="star2" class="fa fa-star-o"></i></span>
  <span><i id="star3" class="fa fa-star-o"></i></span>
  <span><i id="star4" class="fa fa-star-o"></i></span>
  <span><i id="star5" class="fa fa-star-o"></i></span>
</div></code>```

It has some "limitations" that need to be considered. It needs flexbox to reverse the elements render order so that "~"-selector works as intended. So keep that in mind when generating the javascript when you save the results.

Flexbox is pretty well supported nowdays.

Body:
I built a five star simple component (to be used for ranking). The component works fine. I used JavaScript to save the result (when user click on a star (which is not shown here) ).

The problem I have is that I didn't found a proper way to simulate the mouseover event (when you pass the mouse over a star and before clicking). So I did it using JavaScript.

Is there a proper way to use only CSS to achieve this (instead of all those JS lines)?

I'm sharing what I already did.

document.getElementById('star1').addEventListener('mouseover', function(){
 this.classList.remove("fa-star-o");
 this.classList.add("fa-star");
});
document.getElementById('star1').addEventListener('mouseout', function(){
 this.classList.remove("fa-star");
 this.classList.add("fa-star-o");
});

document.getElementById('star2').addEventListener('mouseover', function(){
 this.classList.remove("fa-star-o");
 this.classList.add("fa-star");
 document.getElementById('star1').classList.remove("fa-star-o");
 document.getElementById('star1').classList.add("fa-star");
});
document.getElementById('star2').addEventListener('mouseout', function(){
 this.classList.remove("fa-star");
 this.classList.add("fa-star-o");
 document.getElementById('star1').classList.remove("fa-star");
 document.getElementById('star1').classList.add("fa-star-o");
});

document.getElementById('star3').addEventListener('mouseover', function(){
 this.classList.remove("fa-star-o");
 this.classList.add("fa-star");
 document.getElementById('star1').classList.remove("fa-star-o");
 document.getElementById('star1').classList.add("fa-star");
 document.getElementById('star2').classList.remove("fa-star-o");
 document.getElementById('star2').classList.add("fa-star");
});
document.getElementById('star3').addEventListener('mouseout', function(){
 this.classList.remove("fa-star");
 this.classList.add("fa-star-o");
 document.getElementById('star1').classList.remove("fa-star");
 document.getElementById('star1').classList.add("fa-star-o");
 document.getElementById('star2').classList.remove("fa-star");
 document.getElementById('star2').classList.add("fa-star-o");
});

document.getElementById('star4').addEventListener('mouseover', function(){
 this.classList.remove("fa-star-o");
 this.classList.add("fa-star");
 document.getElementById('star1').classList.remove("fa-star-o");
 document.getElementById('star1').classList.add("fa-star");
 document.getElementById('star2').classList.remove("fa-star-o");
 document.getElementById('star2').classList.add("fa-star");
 document.getElementById('star3').classList.remove("fa-star-o");
 document.getElementById('star3').classList.add("fa-star");
});
document.getElementById('star4').addEventListener('mouseout', function(){
 this.classList.remove("fa-star");
 this.classList.add("fa-star-o");
 document.getElementById('star1').classList.remove("fa-star");
 document.getElementById('star1').classList.add("fa-star-o");
 document.getElementById('star2').classList.remove("fa-star");
 document.getElementById('star2').classList.add("fa-star-o");
 document.getElementById('star3').classList.remove("fa-star");
 document.getElementById('star3').classList.add("fa-star-o");
});

document.getElementById('star5').addEventListener('mouseover', function(){
 this.classList.remove("fa-star-o");
 this.classList.add("fa-star");
 document.getElementById('star1').classList.remove("fa-star-o");
 document.getElementById('star1').classList.add("fa-star");
 document.getElementById('star2').classList.remove("fa-star-o");
 document.getElementById('star2').classList.add("fa-star");
 document.getElementById('star3').classList.remove("fa-star-o");
 document.getElementById('star3').classList.add("fa-star");
 document.getElementById('star4').classList.remove("fa-star-o");
 document.getElementById('star4').classList.add("fa-star");
});
document.getElementById('star5').addEventListener('mouseout', function(){
 this.classList.remove("fa-star");
 this.classList.add("fa-star-o");
 document.getElementById('star1').classList.remove("fa-star");
 document.getElementById('star1').classList.add("fa-star-o");
 document.getElementById('star2').classList.remove("fa-star");
 document.getElementById('star2').classList.add("fa-star-o");
 document.getElementById('star3').classList.remove("fa-star");
 document.getElementById('star3').classList.add("fa-star-o");
 document.getElementById('star4').classList.remove("fa-star");
 document.getElementById('star4').classList.add("fa-star-o");
});

.content > span > i{
    font-size: 50px;
    margin-right: 10px;
    color: #f0d124;
 cursor: pointer;
}
#star1:hover #star1{
 /*an example: I don't know what to do here???*/
}

<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>

Body:
Here is one way of doing it with FontAwesome that your are using:

body {
  font-size: 44px;
}

.content {
  width: 200px;
  display: flex;
  flex-direction: row-reverse;
}

span:hover i:before,
span:hover~span i:before {
  content: "\f005";
}

<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<div class="content">
  <span><i id="star1" class="fa fa-star-o"></i></span>
  <span><i id="star2" class="fa fa-star-o"></i></span>
  <span><i id="star3" class="fa fa-star-o"></i></span>
  <span><i id="star4" class="fa fa-star-o"></i></span>
  <span><i id="star5" class="fa fa-star-o"></i></span>
</div>

It has some "limitations" that need to be considered. It needs flexbox to reverse the elements render order so that "~"-selector works as intended. So keep that in mind when generating the javascript when you save the results.

Flexbox is pretty well supported nowdays.