MongoDB Data-Modelling: a pattern for text search in referenced documents - mongodb

I'm working on a project that use MongoDB; and I would like to hear your opinion about a feature I'd like to implement.
In paticular there are "Users" that reside in "Cities" where they offer "Services".
I have created three Collections representing the three above mentioned entities:
the User collection has a one-to-one reference with City and a one-to-many one with Service.
I would like making a search function that search in the user collection and in referenced collections for a given string available.
Therefor given the following two users, two cities and three services ...
User
{
_id:"u1",
name:"Jhon",
City: ObjectId("c1"),
Services: [
ObjectId("s1"),
ObjectId("s2")
]
}
{
_id:"u2",
name:"Jack",
City: ObjectId("c2"),
Services: [
ObjectId("s2"),
ObjectId("s3")
]
}
City
{
_id:"c1",
name: "Rome"
}
{
_id:"c2",
name: "London"
}
Services
{
_id:"s1",
name: "Repair"
}
{
_id:"s2",
name: "Sell"
}
{
_id:"s3",
name: "Buy"
}
...and searching for the word "R", the result should be the u1 user (due to the R in "Rome" and "Repair").
Given that I cannot do joins, I was thinking making a mongo shell script that adds an additional field to the User collection with all the searcheable referenced strings.
As in the following example
{
_id:"u1",
name:"Jhon",
City: ObjectId("c1"),
Services: [
ObjectId("s1"),
ObjectId("s2")
],
"idx":{
city: "Rome",
services:["Repair","Sell"]
}
}
Finally the question(s)...
Do you think is it a good choice? And Can you propose an alternative solution (or share a link about that, i didn't find nothing usefull)?
And how would you mantain that field constantly updated; for instance, What about if the referenced city name or the services offered by a user change?

Related

Create view from multiple collections that contains same data structure

I'm looking for a solution, using MongoDB, to regroup/aggregate/whateverthenameis a specific field present in each collection inside a new collection or view.
It is my first time using MongoDB so I'm not familiar with it. What the project I've joined has, is a MongoDB database with multiple collections that save the same kind of information but from different provider.
Each collection has the field called "legalInformation" that has a name and an identifier. What we actually have in our project is an other collection, called name-id that duplicates informations from the provider's collection legalInformation. The purpose of the name-id collection is to centralize every name-id in the app, regardless of the provider. But I think that we could create a collection/view instead of programmatically duplicates those data.
I don't know what MongoDB can offer to me to achieve this. I would like to have a way to fetch and aggregate all the legalInformation from all the providers inside on collection/view.
As anyone an idea about how I could do this ?
To illustrate, this is a representation of the DB schema:
providerA({
legalInformations: { name: ..., id: ... },
specificDataFromProviderA: { ... }
})
providerB({
legalInformations: { name: ..., id: ... },
specificDataFromProviderB: { ... }
})
providerC({
legalInformations: { name: ..., id: ... },
specificDataFromProviderC: { ... }
})
and I want a simple collection/view called legalInformation that aggregates all legalInformations
legalInformation({
name: ...,
id: ...
})
Thanks !

Compound queries

I have a RESTful service that accepts a custom query, like this:
/entities/User?actions=
{
"$link": {
"entityType": "Manager",
"entity": {
"name": "John Smith"
},
"linkName": "managers",
"backLinkName": "account",
"$set": {
"propertyName": "aclWrite",
"propertyValue": {
"$ref": {
"propertyName": "entityId"
}
}
}
}
}
Which simply means:
Create a new Entity of type User
Create a new Entity of type Manager with the field name, linking the User to be created to this Manager through link name "managers"
Then back-linking the Manager entity to be created to the User with a link name "account" and setting the Manager entity write ACL (Access Control List) to the ID of the User to be created.
I created this query structure because I can't find any suitable Query language that can seem to support such action/procedure.
The question here is are there any Query language that can support such compound action/procedure or can GraphQL handle such?
As a specification, GraphQL doesn't care what fields your schema defines, what arguments those fields take or what your field resolvers do with those arguments. So it's perfectly feasible to design a schema that would let the client compose an equivalent mutation:
mutation {
link(
entityType: "Manager"
entity: {
name: "John Smith"
}
linkName: "managers"
backLinkName: "account"
set: {
propertyName: "aclWrite"
propertyValue: {
ref: {
propertyName: "entityId"
}
}
}
) {
# some fields here to return in the response
}
}
GraphQL does not support references to other nodes inside the same query, so you would still probably want a single mutation whose input mirrored your existing API. That said, using GraphQL for this may still be preferable because of request validation, which is all the more important with complex requests like this. Having an IDE like GraphiQL or GraphQL Playground that lets you write your queries using autocomplete is a big plus too.

Handling a one-to-many relationship MongoDB schema design

I am currently designing the MongoDB schema for an event management system. The ER diagram is as follows:
The concept is fairly simple:
A company can create 1 or more events (estimating x500s of companies)
A client can attend 1 or more events from a multitude of companies (estimating x200 per client..also estimate x1000s of clients)
The is the classic many-to-many relationship, right?
Now I come from an RDBMS background, so my instincts on structuring a MongoDB schema might be incorrect. However I like MongoDB's flexible document nature and so I tried to come up with the following model structure:
Company model
{
_id: <CompanyID1>,
name: "Foo Bar",
events: [<EventID1>, <EventID2>, ...]
}
Event model
{ _id: <EventID1>,
name: "Rockestra",
location: LocationSchema, // (model below)
eventDate: "01/01/2019",
clients: [<ClientID1>, <ClientID2>, ...]
}
Client model
{ _id: <ClientID1>,
name: "Joe Borg"
}
Location model
{ _id: <LocationID1>,
name: "London, UK"
}
My typical query scenarios would probably be:
List all events organised by a specific company (including location details)
List all registered clients for a particular event
Would this design and approach be a sensible one to use given the cardinality I stated above? I guess one of the pitfalls of this design is that I could not get the company details if I just query the events model.
I would do
Company model
{
_id: <CompanyID1>,
name: "Foo Bar"
}
Event model
{ _id: <EventID1>,
name: "Rockestra",
location: LocationSchema, // embedded, not a reference
eventDate: "01/01/2019",
company: <CompanyID1> // indexed reference.
}
Client model
{ _id: <ClientID1>,
name: "Joe Borg",
events: [<EventID1>, <EventID2>, ...] // with index on events
}
List all events organised by a specific company (including location details):
db.events.find({company:<CompanyID1>})
List all registered clients for a particular event:
db.clients.find({events:<EventID1>})
It's not many-to-many unless a single event can be created by many companies. It looks like you are describing one-to-many.
This is the way I'd approach it.
Company model
{
_id:
name:
}
Client model
{
_id:
name:
}
ClientEvents model
{
_id
clientId
eventId
}
Event model
{
_id:
companyId:
name:
locationId:
eventDate:
}
Location model
{
_id:
name: "London, UK"
}

Mongo DB Storing References

In a Mongo environment it is beneficial to embed data in documents.
so for example an Employees document:
{
{
userid: 'someid',
username: 'user1'
isManager: true,
subordinates: [
{
userid: 'anotherid',
username: 'user2',
isManager: false
}
],
officeLocation: {
officeId: 'someofficeid',
officeName: 'Some Office'
}
},
{
userid: 'anotherid',
username: 'user2',
isManager: false,
officeLocation: {
officeId: 'someotherofficeid',
officeName: 'Some Other Office'
}
}
}
And the office document:
{
{
officeid: 'someofficeid',
officeName: 'Some Office'
},
{
officeid: 'someotherofficeid',
officeName: 'Some Other Office'
}
}
So lets assume that someone in the company decides that they don't like the name Some Other Office and they want to change it to Some Cool Office.
When they make the change in the office document how do we know to update all the embedded Some Other Office in the employee document as well?
It seems that every time that you take a piece of data from one document and embed it into an object in another document that the link between the two gets broken and then you have to write separate queries to update the data in all the different spots that you embedded that object into.
I like the idea of embedded documents rather than storing references, but without some kind of 2 way data-binding it seems impractical when it comes to updating information.
Is there any way that I would be able to bind the data two ways or is there an easier way to go about modeling my data?
Thanks
It remainds me about the traditional RDBMS systems when you model to normalize/denormalize an information. I'm not sure about the binding, but, if you need the "single true" for an information, the better way is never having the information stored in two different places. So, in your case, it may be better to store the Office information into a separated document and just link it by Id.

Meteorjs - What is the proper way to join collections on backend

I am very new to meteor.js and try to build an application with it. This time I wanted to try it over MEAN stack but at this point I am struggled to understand how to join two collection on server side...
I want very identical behaviour like mongodb populate to fetch some properties of inner document.
Let me tell you about my collection it is something like this
{
name: 'Name',
lastName: 'LastName',
anotherObject: '_id of another object'
}
and another object has some fields
{
neededField1: 'asd',
neededField2: 'zxc',
notNeededField: 'qwe'
}
So whenever I made a REST call to retrieve the first object I want it contains only neededFields of inner object so I need join them at backend but I cannot find a proper way to do it.
So far while searching it I saw some packages here is the list
Meteor Collections Helper
Publish with Relations
Reactive joins in Meteor (article)
Joins in Meteor.js (article)
Meteor Publish Composite
You will find the reywood:publish-composite useful for "joining" related collections even though SQL-like joins are not really practical in Mongo and Meteor. What you'll end up with is the appropriate documents and fields from each collection.
Using myCollection and otherCollection as pseudonyms for your two collections:
Meteor.publishComposite('pseudoJoin', {
find: function() {
return myCollection.find();
},
children: [
{
find: function(doc) {
return otherCollection.find(
{ _id: post.anotherObject },
{ fields: { neededField1: 1, neededField2: 1 } });
}
}
]
});
Note that the _id field of the otherCollection will be included automatically even though it isn't in the list of fields.
Update based on comments
Since you're only looking to return data to a REST call you don't have to worry about cursors or reactivity.
var myArray = myCollection.find().fetch();
var myOtherObject = {};
var joinedArray = myArray.map(function(el){
myOtherObject = otherCollection.findOne({ _id: el.anotherObject });
return {
_id: el._id,
name: el.name,
lastName: el.lastName,
neededField1: myOtherObject.neededField1,
neededField2: myOtherObject.neededField2
}
});
console.log(joinedArray); // These should be the droids you're looking for
This is based on a 1:1 relation. If there are many related objects then you have to repeat the parent object to the number of children.