How to structure these requirements? (microservices) - mongodb

I have a customer microservice (customer document) and a bank account microservice (bank account document). How do I structure these requirements?
There are two types of clients: personal and business.
A personal client can only have a maximum of one savings account, one checking account or fixed term accounts.
A business client may not have one savings or fixed-term account but may have multiple checking accounts.
In the customers document I have an ID attribute that identifies the bank account and that could fulfill the first requirement, however, the second one indicates that you can have multiple checking accounts if it is a business type.
----------------------
EDIT:
More requirements were added.
Business bank accounts may have one or more account holders and zero or more authorized signatories.
The system must allow to consult all the movements of a bank account that a client has and the type (withdrawal or deposit).

db
db={
"clients": [
{
"_id": 1,
"type": "personal",
"name": "Tom",
"createAt": ISODate("2022-01-10T11:23:25.184Z")
},
{
"_id": 2,
"type": "business",
"name": "Apple",
"createAt": ISODate("2022-01-12T05:10:42.220Z")
}
],
"accounts": [
{
"_id": 1,
"client_id": 1,
"type": "saving",
"money": 12000
},
{
"_id": 2,
"client_id": 1,
"type": "checking",
"money": 8000
},
{
"_id": 3,
"client_id": 2,
"type": "checking",
"money": 6000
},
{
"_id": 4,
"client_id": 2,
"type": "checking",
"money": 7000
}
]
}
aggregate
db.clients.aggregate([
{
"$lookup": {
"from": "accounts",
"localField": "_id",
"foreignField": "client_id",
"as": "account_docs"
}
}
])
mongoplayground
db
db={
"clients": [
{
"_id": 1,
"type": "personal",
"name": "Tom",
"createAt": ISODate("2022-01-10T11:23:25.184Z")
},
{
"_id": 2,
"type": "business",
"name": "Apple",
"createAt": ISODate("2022-01-12T05:10:42.220Z")
},
{
"_id": 3,
"type": "business",
"name": "Apple2",
"createAt": ISODate("2022-01-13T05:10:42.220Z")
}
],
"accounts": [
{
"_id": 1,
"type": "saving",
"money": 12000
},
{
"_id": 2,
"type": "checking",
"money": 8000
},
{
"_id": 3,
"type": "checking",
"money": 6000
},
{
"_id": 4,
"type": "checking",
"money": 7000
}
],
"clientRoles": [
{
"_id": 1,
"client_id": 1,
"account_id": 1,
"type": "holder"
},
{
"_id": 2,
"client_id": 2,
"account_id": 3,
"type": "holder"
},
{
"_id": 3,
"client_id": 3,
"account_id": 3,
"type": "signatory"
}
],
"clientMovements": [
{
"_id": 1,
"client_id": 1,
"account_id": 1,
"type": "deposit",
"money": 20000
},
{
"_id": 2,
"client_id": 1,
"account_id": 1,
"type": "withdraw",
"money": 8000
}
]
}
aggregate
db.clients.aggregate([
{
"$match": {
"_id": 1
}
},
{
"$lookup": {
"from": "clientMovements",
"localField": "_id",
"foreignField": "client_id",
"as": "movement_docs"
}
}
])
mongoplayground

Related

mongodb:complex nested aggregation

I have this collection:
[
{
"_id": {
"$oid": "60b22e1dbd46fa18a8308318"
},
"title": "basketball",
"price": 12,
"category": "Furniture",
"description": "",
"images": [
"http://res.cloudinary.com/hadarush100/image/upload/v1622289949/nfg948x3zro6gbiuknrz.jpg"
],
"categoryId": 1,
"userId": "60ad16493062eb11141d4927",
"createdAt": 1622289948232,
"chats": [
{
"id": 1,
"createdAt": 1622289948232,
"messages": [
{
"id": "1",
"createdAt": 1622289948232,
"senderId": "60ad16493062eb11141d4927",
"text": "Hello, Im the seller of this product."
}
]
},
{
"id": "2",
"createdAt": 1622289948232,
"messages": [
{
"id": 1,
"createdAt": 1622289948232,
"senderId": "60ad16493062eb11141d4927",
"text": "Hello, Im the seller of this product."
}
]
}
]
}
]
and i want to find specific document (by _id), then dive into specific chat in this document (by id), than use $lookup for replacing the "senderId" property in each message with a "sender" property that contains the full sender details (as a user), that exist in another collection (users). the result needs to look like this:
[
{
"_id": {
"$oid": "60b22e1dbd46fa18a8308318"
},
"title": "basketball",
"price": 12,
"category": "Furniture",
"description": "",
"images": [
"http://res.cloudinary.com/hadarush100/image/upload/v1622289949/nfg948x3zro6gbiuknrz.jpg"
],
"categoryId": 1,
"userId": "60ad16493062eb11141d4927",
"createdAt": 1622289948232,
"chats": [
{
"id": 1,
"createdAt": 1622289948232,
"messages": [
{
"id": "1",
"createdAt": 1622289948232,
"sender": {
"_id": {
"$oid": "60ad16493062eb11141d4927"
},
"username": "hadar",
"email": "hadarushha#gmail.com",
"profileImgUrl": "https://randomuser.me/api/portraits/men/79.jpg",
"createdAt": 1621956168518
},
"text": "Hello, Im the seller of this product."
}
]
},
{
"id": "2",
"createdAt": 1622289948232,
"messages": [
{
"id": 1,
"createdAt": 1622289948232,
"sender": {
"_id": {
"$oid": "60ad16493062eb11141d4927"
},
"username": "hadar",
"email": "hadarushha#gmail.com",
"profileImgUrl": "https://randomuser.me/api/portraits/men/79.jpg",
"createdAt": 1621956168518
},
"text": "Hello, Im the seller of this product."
}
]
}
]
}
]
You can use this aggregation:
$match to filter only selected document (_id)
$unwind multiple time to transform arrays into objects
$lookup to query external collection (users)
$group in reverse order
I assumed that your collections are more or less like this (next time, post both collections and also an example on a working playground)
db={
"products": [
{
"_id": {
"$oid": "60b22e1dbd46fa18a8308318"
},
"title": "basketball",
"price": 12,
"category": "Furniture",
"description": "",
"images": [
"http://res.cloudinary.com/hadarush100/image/upload/v1622289949/nfg948x3zro6gbiuknrz.jpg"
],
"categoryId": 1,
"userId": "60ad16493062eb11141d4927",
"createdAt": 1622289948232,
"chats": [
{
"id": 1,
"createdAt": 1622289948232,
"messages": [
{
"id": "1",
"createdAt": 1622289948232,
"senderId": "60ad16493062eb11141d4927",
"text": "Hello, Im the seller of this product."
}
]
},
{
"id": "2",
"createdAt": 1622289948232,
"messages": [
{
"id": 1,
"createdAt": 1622289948232,
"senderId": "60ad16493062eb11141d4927",
"text": "Hello, Im the seller of this product."
}
]
}
]
},
{
"_id": {
"$oid": "60b22e1dbd46fa18a8308319"
},
"title": "volleyball",
"price": 8,
"category": "Furniture",
"description": "",
"images": [
"http://res.cloudinary.com/hadarush100/image/upload/v1622289949/nfg948x3zro6gbiuknrz.jpg"
],
"categoryId": 1,
"userId": "60ad16493062eb11141d4927",
"createdAt": 1622289948232,
"chats": [
{
"id": 1,
"createdAt": 1622289948232,
"messages": [
{
"id": "1",
"createdAt": 1622289948232,
"senderId": "60ad16493062eb11141d4927",
"text": "Hello, Im the seller of this product."
}
]
},
{
"id": "2",
"createdAt": 1622289948232,
"messages": [
{
"id": 1,
"createdAt": 1622289948232,
"senderId": "60ad16493062eb11141d4928",
"text": "Hello, Im the seller of this product."
}
]
}
]
}
],
"users": [
{
"_id": {
"$oid": "60ad16493062eb11141d4927"
},
"username": "hadar",
"email": "hadarushha#gmail.com",
"profileImgUrl": "https://randomuser.me/api/portraits/men/79.jpg",
"createdAt": 1621956168518
},
{
"_id": {
"$oid": "60ad16493062eb11141d4928"
},
"username": "test",
"email": "test#gmail.com",
"profileImgUrl": "https://randomuser.me/api/portraits/men/49.jpg",
"createdAt": 1621956168528
},
]
}
And here is the working aggregation:
db.products.aggregate([
{
"$match": {
"_id": {
"$oid": "60b22e1dbd46fa18a8308319"
}
}
},
{
"$unwind": "$chats"
},
{
"$unwind": "$chats.messages"
},
{
"$addFields": {
"chats.messages.senderIdObjId": {
"$convert": {
"input": "$chats.messages.senderId",
"to": "objectId",
}
}
}
},
{
"$lookup": {
"from": "users",
"localField": "chats.messages.senderIdObjId",
"foreignField": "_id",
"as": "chats.messages.sender"
}
},
{
"$unwind": "$chats.messages.sender"
},
{
"$group": {
"_id": "$chats.id",
"messages": {
"$push": "$chats.messages"
},
"allFields": {
"$first": "$$ROOT"
}
}
},
{
"$addFields": {
"allFields.chats.messages": "$messages"
}
},
{
"$replaceWith": "$allFields"
},
{
"$group": {
"_id": "$_id",
"chats": {
"$push": "$chats"
},
"allFields": {
"$first": "$$ROOT"
}
}
},
{
"$addFields": {
"allFields.chats": "$chats"
}
},
{
"$replaceWith": "$allFields"
},
])
Working Playground here

MongoDB: How to merge original record back after lookup

I have the following collections which I am using a $lookup to bring together:
{
"organizations": [
{
"_id": 1,
"name": "foo",
"users": [1,2]
},
{
"_id": 2,
"name": "bar",
"users": [1]
}
],
"users": [
{
"_id": 1,
"name": "john1 smith"
},
{
"_id": 2,
"name": "bob johnson"
}
]
}
The query works fine:
[{
"$lookup": {
"from": "users",
"localField": "users",
"foreignField": "_id",
"as": "members"
}
},
{
"$unwind": "$members"
},
{
"$group": {
"_id": "$_id",
"original": { "$first": "$$ROOT" },
"members": {
"$push": "$members"
}
}
}
]
however, the resulting organizations records don't return all of their properties without adding the original prop which gives me back a nesting of the original organization:
{
"_id": 1,
"original": {
"_id": 1,
"name": "foo",
"users": [
1,
2
],
"members": {
"_id": 1,
"name": "john1 smith"
}
},
"members": [
{
"_id": 1,
"name": "john1 smith"
},
{
"_id": 2,
"name": "bob johnson"
}
]
}
I'm trying to get everything in the original prop back into the root along with the new members array.

merge two objects of different collections and get specific data in Mongodb

I have two collections, products and orders.
products collection look like this.
[
{
"_id": "5efb56741c32133bf43ea9aa",
"title": "Xyz",
"image": "172e4eb73415b3cc8540e651.jpg",
"quantity": "1 Ltr",
"price": 1500,
"status": true,
"creationDate": "2020-06-30T15:12:52.570Z",
"__v": 0
},
{
"_id": "5f0079bd27a734424cb3069a",
"title": "abc",
"image": "122e4eb73413b3cc854n42n1.jpg",
"quantity": "500 ml",
"price": 700,
"status": true,
"creationDate": "2020-06-30T15:12:52.570Z",
"__v": 0
}
]
orders collection look like this.
[
{
"_id": "5efca937def27b74fc9f6aa6",
"products": [
{
"Date": "2020-07-01T15:14:36.630Z",
"_id": "5efca937def27b74fc9f6aa8",
"productId": "5efb56741c32133bf43ea9aa",
"productQuantity": 2
},
{
"Date": "2020-07-01T15:14:36.630Z",
"_id": "5efca937def27b74fc9f6aa7",
"productId": "5f0079bd27a734424cb3069a",
"productQuantity": 1
}
],
"totalQuantity": 3,
"totalPrice": 3700,
"creationDate": "2020-07-01T15:18:15.756Z",
"__v": 0
},
{
"_id": "5efca897def27b74fc9f6aa2",
"products": [
{
"Date": "2020-07-01T15:14:36.630Z",
"_id": "5efca897def27b74fc9f6aa3",
"productId": "5f0079bd27a734424cb3069a",
"productQuantity": 1
}
],
"totalQuantity": 1,
"totalPrice": 700,
"creationDate": "2020-07-01T15:15:35.422Z",
"__v": 0
}
]
using this two collections how can I get result like this:
[
{
"_id": "5efca937def27b74fc9f6aa6",
"products": [
{
"Date": "2020-07-01T15:14:36.630Z",
"_id": "5efca937def27b74fc9f6aa8",
"productId": "5efb56741c32133bf43ea9aa",
"productQuantity": 2,
"title": "Xyz",
"image": "172e4eb73415b3cc8540e651.jpg",
"quantity": "1 Ltr",
"price": 1500
},
{
"Date": "2020-07-01T15:14:36.630Z",
"_id": "5efca937def27b74fc9f6aa7",
"productId": "5f0079bd27a734424cb3069a",
"productQuantity": 1,
"title": "abc",
"image": "122e4eb73413b3cc854n42n1.jpg",
"quantity": "500 ml",
"price": 700
}
],
"totalQuantity": 3,
"totalPrice": 3700,
"creationDate": "2020-07-01T15:18:15.756Z",
"__v": 0
},
{
"_id": "5efca897def27b74fc9f6aa2",
"products": [
{
"Date": "2020-07-01T15:14:36.630Z",
"_id": "5efca897def27b74fc9f6aa3",
"productId": "5f0079bd27a734424cb3069a",
"productQuantity": 1,
"title": "abc",
"image": "122e4eb73413b3cc854n42n1.jpg",
"quantity": "500 ml",
"price": 700
}
],
"totalQuantity": 1,
"totalPrice": 700,
"creationDate": "2020-07-01T15:15:35.422Z",
"__v": 0
}
]
I got the result using this query.
order.aggregate([
{ "$unwind": "$products" },
{
$lookup: {
from: 'products',
localField: "products.productId",
foreignField: "_id",
as: "product"
}
},
{
"$addFields": {
"products": { $mergeObjects: [{ $arrayElemAt: ["$product", 0] }, "$$ROOT.products"] }
}
},
{
"$group": {
"_id": "$_id",
"products": { "$push": "$products" },
"totalQuantity": { $first: '$totalQuantity' },
"totalPrice": { $first: '$totalPrice' },
"creationDate": { $first: '$creationDate' }
}
}
])

Mongo Aggs and total count

I experiencing some trouble in mongodb to do a query on collection to get all documents filtered, paginate(with skip and limit) and get the total count like that
The chat document look like that:
{
"_id": "c603db3e75ec76f5a4dff559bc525e29",
"members": [{
"user": "5b2b0d5135cfd60185034bf6",
"status": "creator"
}, {
"user": "5b31dc3f88284a009fa847e5",
"status": "member"
}],
"messages": [{
"_id": 2,
"from": "5b2b0d5135cfd60185034bf6",
"parse_mode": "none",
"text": "fffasdfsa",
"attachment": null,
"time": 1529995684119,
"type": "text"
},
{...},
{...}
]}
And i would like to get result like that
{
total_count: 3,
chats: [{
"_id": "c603db3e75ec76f5a4dff559bc525e29",
"members": [{
"user": "5b2b0d5135cfd60185034bf6",
"status": "creator"
}, {
"user": "5b31dc3f88284a009fa847e5",
"status": "member"
}],
"creator": {
"user": "5b2b0d5135cfd60185034bf6",
"status": "creator"
},
"lastMessage": {
"_id": 4,
"from": "5b2b0d5135cfd60185034bf6",
"parse_mode": "none",
"text": "aasdas",
"attachment": null,
"time": 1529995698736,
"type": "text"
},
"messages": [{
"_id": 2,
"from": "5b2b0d5135cfd60185034bf6",
"parse_mode": "none",
"text": "fffasdfsa",
"attachment": null,
"time": 1529995684119,
"type": "text"
}, {
"_id": 3,
"from": "5b2b0d5135cfd60185034bf6",
"parse_mode": "none",
"text": "weqw",
"attachment": null,
"time": 1529995690015,
"type": "text"
}, {
"_id": 4,
"from": "5b2b0d5135cfd60185034bf6",
"parse_mode": "none",
"text": "aasdas",
"attachment": null,
"time": 1529995698736,
"type": "text"
}]
},
{...},
{...}
]
}
My query is
collection<DbChat>('chats').aggregate([
{
$match: {
messages: { $exists: true }
}
},
{ $unwind: '$messages' },
{ $unwind: '$members' },
{ $group: {_id: '$_id', members: { $push: '$members' }, member: { $last: '$members' }, creator: { $first: '$members' }, messages: {$push: '$messages' } , lastMessage: {$last: '$messages'}}},
{ $sort: this.sort},
]}.skip(skip).limit(size)
with this query i dont get the total_count I tried lot of things but i didnt found a way to get the result and the count in the same query.
Thanks

Elasticsearch nested query

I'm new to elasticsearch, managed to set it up and import recordset from my mongodb collection using the river plugin. For a start, I want to query against the "desc" field but just can't manage to get the query .. not sure if the problem is driven by the way index was defined.. can anyone help please?
Sample recordset in elastic search looks like this
{
"took": 2,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 107209,
"max_score": 1,
"hits": [
{
"_index": "shiv",
"_type": "shiv",
"_id": "iG1eIzN7RGO7hFfxTlnLuA",
"_score": 1,
"_source": {
"_id": {
"$oid": "50901d7f485bf7bd1c000021"
},
"brand": "",
"category": {
"$ref": "categories",
"$id": {
"$oid": "4fbd2221758cb11d14000174"
}
},
"comments": [],
"count_comment": 0,
"count_fav": 2,
"count_hotness": 1.46,
"count_rekick": 0,
"count_share": 0,
"country": {
"$ref": "countries",
"$id": {
"$oid": "4fec98f7758cb18c6e0002c9"
}
},
"currency": "pound",
"desc": "A men's automatic watch, this Seamaster Bond model features a Co-Axial escapement and date function. Its blue dial is teamed with a stainless steel case and bracelet for a look that's sporty and refined.",
"gender": "male",
"ident": "omega-seamaster-diver-bond-men-s-automatic-watch---ernest-jones-1351622015",
"img_url": "http://s7ondemand4.scene7.com/is/image/Signet/5735793?$detail$",
"lifestyles": [
{
"$ref": "lifestyles",
"$id": {
"$oid": "508ff6ca485bf73112000060"
}
}
],
"location": "United Kingdom",
"owner": {
"$ref": "accounts",
"$id": {
"$oid": "50742fd8485bf74b7a00213f"
}
},
"price": 2400,
"store": "ernestjones.co.uk",
"tags": [
"ernest-jones",
"bond"
],
"timestamp_creation": 1351622015,
"timestamp_exp": 1356825600,
"timestamp_update": 1351622015,
"title": "Omega Seamaster Diver Bond men's automatic watch - Ernest Jones",
"url": "http%3A%2F%2Fwww.ernestjones.co.uk%2Fwebstore%2Fd%2F5735793%2Fomega%20seamaster%20diver%20bond%20men%27s%20automatic%20watch%2F%3Futm_source%3Dgooglebase%26utm_medium%3Dfeedmanager%26cm_mmc%3DFroogle-_-CKB-_-nurses_fobs-_-watches%26cm_mmca1%3Domega%26cm_mmca2%3Dmale%26cm_mmca3%3Dadult"
}
}
]
}
}
The mapping of the index "shiv" looks like
{
"shiv": {
"properties": {
"$oid": {
"type": "string"
}
}
}
}
Thanks again
There are lots of ways to query, have you tried a match query?
Using curl or a rest client of your choice...
http://[host]:9200/[index_name]/[doc_type]/_search
{
"query" : {
"match" : {
"desc" : "some value you want to find in desc"
}
}
}