How can I define ref and how to query a collection with ref populating related fields.
I defined a ref when inserting a product document in a product collection, but when querying I am getting info as inserted without populating the category field. I am expecting the mongo to populate category field with category document in category collection. The following are some code snippets:
Document Insertion:
category = "5126bc054aed4daf9e2ab772"
product_name = "Prod"
new_product_id = mongo.db.product.insert_one({
"product_name": product_name,
"category": {
"$ref": "category",
"$id": ObjectId(category)
}
}).inserted_id
Results Obtained:
{
"_id": {
"$oid": "61dd1612b898afc16e0f4325"
},
"category": {
"$id": {
"$oid": "5126bc054aed4daf9e2ab772"
},
"$ref": "category"
},
"product_name": "Prod"
}
I think you can try the below code, I feel it will work
category = "5126bc054aed4daf9e2ab772"
product_name = "Prod"
new_product_id = mongo.db.product.insert_one({
"product_name": product_name,
"category": category
}).inserted_id
Related
I have a collection in MongoDB containing search history of a user where each document is stored like:
"_id": "user1"
searchHistory: {
"product1": [
{
"timestamp": 1623482432,
"query": {
"query": "chocolate",
"qty": 2
}
},
{
"timestamp": 1623481234,
"query": {
"query": "lindor",
"qty": 4
}
},
],
"product2": [
{
"timestamp": 1623473622,
"query": {
"query": "table",
"qty": 1
}
},
{
"timestamp": 1623438232,
"query": {
"query": "ike",
"qty": 1
}
},
]
}
Here _id of document acts like a foreign key to the user document in another collection.
I have backend running on nodejs and this function is used to store a new search history in the record.
exports.updateUserSearchCount = function (userId, productId, searchDetails) {
let addToSetData = {}
let key = `searchHistory.${productId}`
addToSetData[key] = { "timestamp": new Date().getTime(), "query": searchDetails }
return client.db("mydb").collection("userSearchHistory").updateOne({ "_id": userId }, { "$addToSet": addToSetData }, { upsert: true }, async (err, res) => {
})
}
Now, I want to get search history of a user based on query only using the db.find().
I want something like this:
db.find({"_id": "user1", "searchHistory.somewildcard.query": "some query"})
I need a wildcard which will replace ".somewildcard." to search in all products searched.
I saw a suggestion that we should store document like:
"_id": "user1"
searchHistory: [
{
"key": "product1",
"value": [
{
"timestamp": 1623482432,
"query": {
"query": "chocolate",
"qty": 2
}
}
]
}
]
However if I store document like this, then adding search history to existing document becomes a tideous and confusing task.
What should I do?
It's always a bad idea to save values are keys, for this exact reason you're facing. It heavily limits querying that field, obviously the trade off is that it makes updates much easier.
I personally recommend you do not save these searches in nested form at all, this will cause you scaling issues quite quickly, assuming these fields are indexed you will start seeing performance issues when the arrays get's too large ( few hundred searches ).
So my personal recommendation is for you to save it in a new collection like so:
{
"user_id": "1",
"key": "product1",
"timestamp": 1623482432,
"query": {
"query": "chocolate",
"qty": 2
}
}
Now querying a specific user or a specific product or even a query substring is all very easily supported by creating some basic indexes. an "update" in this case would just be to insert a new document which is also much faster.
If you still prefer to keep the nested structure, then I recommend you do switch to the recommended structure you posted, as you mentioned updates will become slightly more tedious, but you can still do it quite easily using arrayFilters for updating a specific element or just using $push for adding a new search
I want to create a dependency between some documents in my collection.
For example I have document A, and I want to use some fields of it in document B as a subsection. So if I change document A, the fields that are also in document B will also change.
Here's a better explanation with actual JSONs:
Here's document A
{
"_id": ObjectId("1"),
"value": 10,
"name": "A_document_name",
"owner": "some_owner"
}
Here's document B
{
"_id": ObjectId("2"),
"description": "some_description",
"valid": true,
"name": "some_name",
"items": [
{
"id": "1",
"name": "A_object_name"
}
]
}
Now, what I want to happen is, if I change the name of document A, I want to see that it also changed in the items section of document B. Currently, when I change A's name, it changes only in A and B remains the same since it was created. Is it achievable in Mongo? How can I create a reference between items section in B with A? Do I need to change something in my document structure?
You need to use mongoDB's $lookup
You you'd a unique identifier from that you can use match the documents,
e.g:
The A document would be:
{
"_id": ObjectId("1"),
"b_id": ObjectId("2"),
"value": 10,
"name": "A_document_name",
"owner": "some_owner"
}
While the B document would just be:
{
"_id": ObjectId("2"),
"description": "some_description",
"valid": true,
"name": "some_name"
}
You'll then do an aggregate query like so:
db.B.aggregate([
{
$lookup:
{
from: "A", //The collection you're getting the items from
localField: "_id", //The local field you're using to lookup
foreignField: "b_id", //The field the `A` document you're using to match
as: "items" //The name of the field that will be populated with the results
}
}
])
There's a lot more you can do with aggregate take a minute to look through the docs. Let me know if this helps.
i have a person document, that have list of pets:
{
"personId": "kjadfh97r0",
"pets": [
{
"petId": "dfjkh32476",
"name": "kitty",
"kind": "cat"
},
{
"petId": "askdjfh2794857",
"name": "rexy",
"kind": "dog"
}
]
}
I want to find certain pen inside of certain person and update just some fields, so I did something like:
db.people.findAndModify({
query: { "personId": "kjadfh97r0", "pets.petId": "dfjkh32476" },
update: {"$set":{"pets.$":{"kind":"tiger"}}}
})
but what happens to me is that the whole document is replaced with "kind":"tiger", and I just wanted to update the "kind" field any keep the rest.
You should specify entire path for $set when you update nested document using positional operator, otherwise the document will be replaced:
db.people.findAndModify({
query: { "personId": "kjadfh97r0", "pets.petId": "dfjkh32476" },
update: { $set: {"pets.$.kind": "tiger"} }
})
I have a user document that looks like this:
{
"userId": "249869823570",
"name": "john",
"country": "usa",
"active": true,
"serviceProviders": [
{
"serviceProvidersId": "897892893",
"serviceProvidersName": "AT&T",
"active": true,
"serviceProviderContactPerson": []
},
{
"serviceProvidersId": "82589628569",
"serviceProvidersName": "T-Mobile",
"active": true,
"serviceProviderContactPerson": []
}
]
}
and I want to create 3 methods to insert/update/delete a serviceProviderContactPerson. my dilema is, my main document have array of serviceProviders, and a serviceProvider have a list of serviceProviderContactPerson...what would be the best practice in mongo to work this?
serviceProviderContactPerson document will look like this:
{
"contactId": "873498798",
"name": "Mark",
"email": "mark#tmobile.com",
"phone": "917-475-4637"
}
You can use update to perform all operations in 3.6.
Insert a new array element where serviceProvidersId = "897892893"
db.colname.update(
{"serviceProviders.serviceProvidersId":"897892893"},
{$push: {"serviceProviders.$.serviceProviderContactPerson":serviceProviderContactPersonDoc}}
)
Update phone where serviceProvidersId = "897892893" and contactId = "873498798"
db.colname.update(
{},
{$set: {"serviceProviders.$[sp].serviceProviderContactPerson.$[spc].phone":phone value}},
{arrayFilters:[{"sp.serviceProvidersId":"897892893"}, {"spc.contactId":"873498798"}]}
)
Delete array element where serviceProvidersId = "897892893" and contactId = "873498798"
db.colname.update(
{},
{$pull: {"serviceProviders.$[sp].serviceProviderContactPerson":{"contactId":"873498798"}}},
{arrayFilters:[{"sp.serviceProvidersId":"897892893"}]}
)
I have one document in my "params" collection like this:
{
"_id": ObjectId("4d124cef3ffcf6f410000037"),
"code": "color",
"productTypes": [
{
"$ref": "productTypes",
"$id": ObjectId("4d120a2d2b8d8d3010000000"),
"$db": "test"
}
]
}
the referenced document is this:
{
"_id": ObjectId("4d120a2d2b8d8d3010000000"),
"code": "car"
}
I'm using DoctrineODM to fetch the "param" documents which referenced "productType" is "car". I'm using this code:
$query = $dm->createQuery('Cms\Model\Param');
$query->field('productTypes.code')->equals('car');
$result = $query->execute();
var_dump($result);
but the result is an empty array. How can i do this?
If you using ReferenceMany or ReferenceOne you can't query by any reference document field, except reference document id.
If you need query on code from referenced collection you should use EmbedMany instead of ReferenceMany.
In this case your document will be:
{
"_id": ObjectId("4d124cef3ffcf6f410000037"),
"code": "color",
"productTypes": [
{
"_id": ObjectId("4d120a2d2b8d8d3010000000"),
"code": "car"
}
]
}
And following query will work:
$query = $dm->createQuery('Cms\Model\Param');
$query->field('productTypes.code')->equals('car');
$result = $query->execute();
var_dump($result);
Also if your ProductType code is unique you can use it instead of MongoId, in this case you can query on $id:
{
"_id": ObjectId("4d124cef3ffcf6f410000037"),
"code": "color",
"productTypes": [
{
"$ref": "productTypes",
"$id": 'car',
"$db": "test"
}
]
}
Referenced document:
{
"_id": 'car'
}
Query:
$query->field('productTypes.$id')->equals('car');
You must use the references() method of Query Builder for a #MongoDB\ReferenceOne like https://doctrine-mongodb-odm.readthedocs.org/en/latest/reference/query-builder-api.html
$productType = $dm->getRepository('Cms\Model\ProductTypes')->findOneByCode('car');
$queryBuilder = $dm->getRepository('Cms\Model\Param')->createQueryBuilder()
->field('productTypes')->references($productType);
$results = $queryBuilder->getQuery()->execute();
PS: use includesReferenceTo() a #MongoDB\ReferenceMany
->field('productTypes.code')->equals(new \MongoRegex('/.*car.*/i'))