I'm currently working on a project where we are using couchbase 4.1 as of today for a eCommerce site.
I want to store our websites entire category structure in Couchbase as a single document and then query for a specific category and return that category in some cases and in other cases I would like to return the category and its child categories.
I'm pretty sure I have to use the array indexeer to make this work efficient but I'm quite new to Couchbase so I'm not sure how it should be structured (or even if it's possible).
Part of my document looks like this (there is 4 levels in the structure and about 8-10 top level categories):
{
"Categories": [
{
"DisplayName": "Category One",
"Id": 1,
"Categories": [
{
"DisplayName": "Child category",
"Id": 10,
"Categories": [
{
"DisplayName": "Child child category",
"Id": 100,
"Categories": [
{
"DisplayName": "Child child child category",
"Id": 1000
},
{
"DisplayName": "Sibling child category",
"Id": 1001
}
]
},
{
"DisplayName": "Child",
"Id": 101,
"Categories": [
{
"DisplayName": "Another child category",
"Id": 2001
}
]
}
]
}
]
}
]
}
If I query for Id = 100 I would like to have my result look like this:
{
"DisplayName": "Child child category",
"Id": 100,
"Categories": [
{
"DisplayName": "Child child child category",
"Id": 1000
},
{
"DisplayName": "Sibling child category",
"Id": 1001
}
]
}
In some cases I am not interessted having the childs. I have tried to create my query using the array (N1QL) to select into my arrays but I'm not sure whether it's even possible when having levels of complex objects.
Can give me some guidedance on how this is possible (even if it is?). We are using the Couchbase .NET client.
Best regards Martin
This is interesting, because you are trying to store everything inside one document, and then query into that document. Here is one approach.
To query Category Id 100 without sub-categories:
SELECT c.Id, c.DisplayName
FROM default
UNNEST ( ARRAY cat FOR cat WITHIN Categories WHEN cat.Id IS NOT NULL END ) AS c
WHERE c.Id = 100;
To query Category Id 100 with sub-categories:
SELECT c.Id, c.DisplayName, c.Categories
FROM default
UNNEST ( ARRAY cat FOR cat WITHIN Categories WHEN cat.Id IS NOT NULL END ) AS c
WHERE c.Id = 100;
To query Category Id 100 with only one level of sub-categories:
SELECT c.Id, c.DisplayName, sub.Id AS SubId, sub.DisplayName AS SubDisplayName
FROM default
UNNEST ( ARRAY cat FOR cat WITHIN Categories WHEN cat.Id IS NOT NULL END ) AS c
LEFT OUTER UNNEST c.Categories AS sub
WHERE c.Id = 100;
The only thing i can find is subdoc (first available in Couchbase 4.5): http://blog.couchbase.com/2016/february/subdoc-explained
Related
Say I have a document like below for each userId. And each userId has a child collection object named "lnk" and it can grow up to 100 items per userId.
I would like to sort the child collection for a given userId based on a single property
(ex: Topic or Pid or URL). for userId = "5663a8f7-6d2e-40ef-8972-515944080474"
SELECT * FROM c IN PageLinksContainer.lnk order by c.top asc OFFSET 1 LIMIT 3
Error I'm getting:
Failed to query item for container PageLinksContainer:
{
"errors": [
{
"severity": "Error",
"location": {
"start": 7,
"end": 25
},
"code": "SC2001",
"message": "Identifier 'PageLinksContainer' could not be resolved."
},
{
"severity": "Error",
"location": {
"start": 67,
"end": 85
},
"code": "SC2001",
"message": "Identifier 'PageLinksContainer' could not be resolved."
}
]
}
{
"userid": "5663a8f7-6d2e-40ef-8972-515944080474",
"lnk": [
{
"pid": 1,
"top": "Topic 1",
"por": "www.google.com",
"sdt": "10/26/2021"
},
{
"pid": 2,
"top": "Topic 2",
"por": "www.google.com",
"sdt": "10/26/2021"
},
{
"pid": 3,
"top": "Topic 3",
"por": "www.google.com",
"sdt": "10/26/2021"
}
]
}
C#
var query = "SELECT * FROM c IN PageLinksContainer.lnk OFFSET 1 LIMIT 10"; //Works fine with no order by.
var container = _cosmosClient.GetContainer(_databaseName, containerName);
using var iterator = container.GetItemQueryStreamIterator(new QueryDefinition(query),
requestOptions: new QueryRequestOptions
{
PartitionKey = new PartitionKey("5663a8f7-6d2e-40ef-8972-515944080474"),
MaxItemCount = 100
});
Links:
https://learn.microsoft.com/en-us/azure/cosmos-db/sql/sql-query-order-by
CosmosDB sql query with/without "ORDER BY" returns different number of items
You can't ORDER BY properties in arrays currently.
The following query better demonstrates that with the following error Order-by over correlated collections is not supported:
SELECT VALUE l
FROM c
JOIN l IN c.lnk
ORDER BY l.por
What you can do is create a User Defined Function that sorts the array before return. Although I would advise you to retrieve the results and use OrderBy in your C# code as it's easier, more transparant, and avoids spending unnecessary RU's on an UDF.
I have the following db config:
db={
"order": [
{
"id": 1,
"version": 1
},
{
"id": 1,
"version": 2
},
{
"id": 2,
"version": 1
},
{
"id": 2,
"version": 2
}
],
"orderDetail": [
{
"orderId": 1,
"orderDate": new Date("2020-01-18T16:00:00Z")
},
{
"orderId": 1,
"orderDate": new Date("2020-01-11T16:00:00Z")
},
{
"orderId": 1,
"orderDate": new Date("2020-01-12T16:00:00Z")
}
]
}
I'm using the fluent interface to perform a Lookup joining the orderDetails to the order collection (as shown in this post). Now that I have the join in place what's the best method to:
Sort the joined array such that the details are sorted by orderDate
Group the Orders (by OrderID) and sort by version to select the latest (largest Version #)
The workaround I implemented for #1 involves sorting the list after performing the lookup, but that's only because I wasn't able to apply a sort to the "as" of collection as part of the Lookup.
If anyone has any ideas, I'd appreciate it. Thanks!
If you are using MongoDB v3.6 or higher, you can use the $lookup with uncorrelated subqueries to use the inner pipelines to archive what you want.
Join Conditions and Uncorrelated Sub-queries
Since you didn't provide what collections or fields you are using, I will give a generic example:
db.customers.aggregate([
{
$lookup: {
from: "orders",
let: { customer_id: "$_id" },
pipeline: [
{ $match: { $expr: { $eq: [ "$customer_id", "$$customer_id" ] } } },
{ $sort: { orderDate: -1 } }
],
as: "orders"
}
}
]);
I hope that gives you a way to get where you want. =]
I am new in MongoDB and I am developing a software by C# and MongoDB. My data structure is like this
{
"Id": 1,
"Title": "myTitle",
"Geners": [ "Drama", "Action" ],
"Category": 1,
"Casts": [
{
"Id": 1,
"Name": "myName",
"Gender": "Male",
"Age": 35
},
{
"Id": 2,
"Name": "herName",
"Gender": "Female",
"Age": 30
},
{
"Id": 3,
"Name": "hisName",
"Gender": "Male",
"Age": 45
}
]
}
This is just one document and I have about 5 million documents. I want to run a query like below to count the records based on Category and shows me how many movie do I have in each category and I want to put Casts field in result.
db.getCollection('myCollection').aggregate([
{
$group:{"_id":"$Category", "count": {$sum:1},
"Casts":{$push:"$Casts"}}
}
])
this is close to something I want but the problem is, it puts Casts data in second level of array like {"Id":1, ... , "Casts":[[{},{},...]]} but I need it like this {"Id":1, ... , "Casts":[{},{},...]}
How can I show the data like that?
If duplicates are acceptable, then the following aggregation will suffice:
db.getCollection('myCollection').aggregate([
{ $unwind:"$Casts"},
{
$group:{"_id":"$Category", "count": {$sum:1},
"Casts":{$push:"$Casts"}}
}
])
Update:
Since you need the count to be valid, there's a few more hoops to jump through.
db.getCollection('myCollection').aggregate([
{ $group:{"_id":"$Category", "count": {$sum:1}, "Casts":{$addToSet:"$Casts"}}},
{$unwind:"$Casts"},
{$unwind:"$Casts"},
{ $group:{"_id":"$_id", "count": {$first:"$count"}, "Casts":{$addToSet:"$Casts"}}},
])
Let me know if that helps
I'm having trouble querying nested objects in DocumentDB. I have no control over the format of the data. Let's say an object looks like this in DocumentDB:
{
"SCHEMA_ID": {
"PROJECT": "A",
"MODEL": "B",
"GUID":"A GUID"
},
"STATE": {
"Active": "True"
},
"OBJECTS": {
"OBJECT": [
{
"ATTR_VALS": {
"NAME": "Header",
"ID": "0",
"VALUE": [
{
"NAME": "JobId",
"VAL": "1011656"
},
{
"NAM": "Region",
"VAL": "West Coast"
}
]
}
},
{
"ATTR_VALS": {
"NAME": "SampleData",
"ID": "0",
"VALUE": [
{
"NAME": "Height",
"VAL": "5"
},
{
"NAM": "Length",
"VAL": "3"
}
]
}
}
]
}
}
I want to find all the objects that have a 'ATTR_VALS' = 'SampleData' and where those items have a 'Height'=5
So Far I have:
SELECT test.GUID
FROM test
join OBJECTS in test.OBJECTS
join OBJECT in OBJECTS
join ATTR_VALS in OBJECT
join VALUE in ATTR_VALS
WHERE ATTR_VALS.NAME = 'SampleData' AND VALUE.NAME='Height' AND VALUE.VAL='5'
But this doesn't work, and returns no results. Thanks!
The query must be:
SELECT test.SCHEMA_ID.GUID
FROM test
join OBJ in test.OBJECTS.OBJECT
join VAL in OBJ.ATTR_VALS["VALUE"]
WHERE OBJ.ATTR_VALS.NAME = "SampleData" AND VAL.NAME='Height' AND VAL.VAL='5'
A couple things I changed:
JOIN must be performed against arrays, not objects. Objects can be expanded using the “.” Operator
VALUE is a special keyword and must be escaped
Small typo in the projection clause missing SCHEMA_ID
Lets say I have a nested documents with this stucture:
{
"_id": "a125",
"Language": null,
"Name": "Some name",
"Related": [{
"_id": "b125",
"Status": 0,
}, {
"_id": "b126",
"Status": 1,
}]
}
is it possible using c# drivers to select "Related" model where id is b126 and at the same time to get parent document id (a125)?
As I imagine outcome should look like this:
{
"_id": "a125",
"Related": {
"_id": "b126",
"Status": 1,
}
}
You can use dot notation with the positional $ projection operator to find the matching document and only include the matching Related element.
In the shell:
db.test.findOne({'Related._id': 'b125'}, {'Related.$': 1})
To do this in C#:
var filter = Builders<BsonDocument>.Filter.Eq("Related._id", "b125");
var projection = Builders<BsonDocument>.Projection.Include("Related.$");
var result = await collection.Find(filter).Project(projection).FirstAsync();
You should use dot notation for your purpose. Your query will look like this:
{"Related._id": "b126"}
This will bring you all the documents, with all the fields including your parent _id, where there is a document element in the Related array, which has a field _id with value "b126"