Giter Club home page Giter Club logo

camljs's Introduction

Installation

Nuget:

PM> Install-Package CamlJs

Npm:

npm install camljs

Also check out CamlJs Console - Chrome extension for testing queries with live preview against real lists.

Usage

In browser:

<script type="text/javascript" src="//unpkg.com/camljs"></script>
<script>
    alert(new CamlBuilder().View().ToString());
</script>

In Node.js:

var CamlBuilder = require('camljs');
console.log(new CamlBuilder().View().ToString());

ES2015 modules:

import * as CamlBuilder from 'camljs';
console.log(new CamlBuilder().View().ToString());

Basics

Let's assume we need to fetch all Google-related emails from a SharePoint list where your company stores archived project emails. To generate the corresponding query using CamlJs, you could use following javascript code:

var camlBuilder = new CamlBuilder();

var caml = camlBuilder.Where()
    .TextField("Email").EqualTo("[email protected]")
    .Or()
    .TextField("Email").EqualTo("[email protected]")
    .Or()
    .TextField("Title").BeginsWith("[Google]")
    .Or()
    .TextField("Content").Contains("Google")
    .ToString();

This will generate the following CAML code:

<Where>
  <Or>
    <Eq>
      <FieldRef Name="Email" />
      <Value Type="Text">[email protected]</Value>
    </Eq>
    <Or>
      <Eq>
        <FieldRef Name="Email" />
        <Value Type="Text">[email protected]</Value>
      </Eq>
      <Or>
        <BeginsWith>
          <FieldRef Name="Title" />
          <Value Type="Text">[Google]</Value>
        </BeginsWith>
        <Contains>
          <FieldRef Name="Content" />
          <Value Type="Text">Google</Value>
        </Contains>
      </Or>
    </Or>
  </Or>
</Where>

It is also possible to generate SP.CamlQuery object, just change .ToString() to .ToCamlQuery().

Another example:

var caml = camlBuilder.Where()
    .LookupField("Category").Id().In([2, 3, 10])
    .And()
    .DateField("ExpirationDate").LessThanOrEqualTo(CamlBuilder.CamlValues.Now)
    .OrderByDesc("ExpirationDate")
    .ToString()

As you see, the code is pretty clean and readable. The resulting CAML is much more awkward, especially if you imagine it in javascript strings dress, without indentation and highlighting...

<Where>
  <And>
    <In>
      <FieldRef Name="Category" LookupId="TRUE" />
      <Values>
        <Value Type="Integer">2</Value>
        <Value Type="Integer">3</Value>
        <Value Type="Integer">10</Value>
      </Values>
    </In>
    <Leq>
      <FieldRef Name="ExpirationDate" />
      <Value Type="Date">
        <Now />
      </Value>
    </Leq>
  </And>
</Where><OrderBy>
  <FieldRef Name="ExpirationDate" Ascending="FALSE" />
</OrderBy>

Dynamic queries

It's very easy to create dynamic queries with CamlJs by leveraging the CamlBuilder.Expression() construction. It's like a standalone part of query that can be later used in the final new CamlBuilder.Where() or new CamlBuilder.View().

var categories = ["Category 1", "Category 2", "Category 3"];

var categoriesExpressions = categories.map(c => CamlBuilder.Expression().TextField("Category").EqualTo(c));

var caml = new CamlBuilder().Where()
    .Any(categoriesExpressions),
    .ToString();

Result:

<Where>
    <Or>
        <Eq>
            <FieldRef Name="Category" />
            <Value Type="Text">Category 1</Value>
        </Eq>
        <Or>
            <Eq>
                <FieldRef Name="Category" />
                <Value Type="Text">Category 2</Value>
            </Eq>
            <Eq>
                <FieldRef Name="Category" />
                <Value Type="Text">Category 3</Value>
            </Eq>
        </Or>
    </Or>
</Where>

While .Any() generates <Or> clauses, .All() will generate <And>.

Elements support

CamlJs supports all Query elements that are described on MSDN.

For example, seldom used Membership element:

var caml = camlBuilder.Where()
    .UserField("AssignedTo").EqualToCurrentUser()
    .Or()
    .UserField("AssignedTo").IsInCurrentUserGroups()
    .GroupBy("ProductTitle")
    .OrderBy("Priority").ThenBy("Title")
    .ToString();

This code will generate following CAML:

<Where>
  <Or>
    <Eq>
      <FieldRef Name="AssignedTo" />
      <Value Type="Integer">
        <UserID />
      </Value>
    </Eq>
    <Membership Type="CurrentUserGroups">
      <FieldRef Name="AssignedTo" />
    </Membership>
  </Or>
</Where>
<GroupBy>
  <FieldRef Name="ProductTitle" />
</GroupBy>
<OrderBy>
  <FieldRef Name="Priority" />
  <FieldRef Name="Title" />
</OrderBy>

Joins

You can also create the upper-level View element as supported by SP.CamlQuery object. Scope attribute, ViewFields, Joins and ProjectedFields are supported in this case.

Joining lists via CamlJs is very easy. Here's the example:

var query = new CamlBuilder()
    .View(["Title","Country","Population"])
    .LeftJoin("Country","Country").Select("People","Population")
    .Query()
    .Where()
    .NumberField("Population").LessThan(10)
    .ToString();

The resulting generated CAML query will be the following:

<View>
    <ViewFields>
        <FieldRef Name="Title" />
        <FieldRef Name="Country" />
        <FieldRef Name="Population" />
    </ViewFields>
    <Joins>
        <Join Type="LEFT" ListAlias="Country">
            <Eq>
                <FieldRef Name="Country" RefType="ID" />
                <FieldRef Name="ID" List="Country" />
            </Eq>
        </Join>
    </Joins>
    <ProjectedFields>
        <Field ShowField="People" Type="Lookup" Name="Population" List="Country" />
    </ProjectedFields>
    <Query>
        <Where>
            <Lt>
                <FieldRef Name="Population" />
                <Value Type="Number">10</Value>
            </Lt>
        </Where>
    </Query>
</View>

Modify existing queries

Often you need to modify existing query (e.g. that comes from an existing list view), rather than generate a completely new one. This use case is also supported by CamlJs:

  • CamlBuilder.FromXml(xml) method will create a CamlBuilder object from existing CAML string
  • ReplaceWhere method then allows to replace clause with one generated by CamlJs
  • ModifyWhere().AppendAnd() will add new conditions to existing query using "And" operator
  • ModifyWhere().AppendOr() will add new conditions to existing query using "Or" operator

Example:

var xml = new CamlBuilder().View().Query().Where()
    .UserField("Author").EqualToCurrentUser()
    .ToString();

var query = CamlBuilder.FromXml(xml)
    .ModifyWhere().AppendAnd()
    .LookupField("Country").ValueAsText().BeginsWith("G");

Result:

<View>
  <Query>
    <Where>
      <And>
        <Eq>
          <FieldRef Name="Author" LookupId="TRUE" />
          <Value Type="Integer">
            <UserID />
          </Value>
        </Eq>
        <BeginsWith>
          <FieldRef Name="Country" />
          <Value Type="Text">G</Value>
        </BeginsWith>
      </And>
    </Where>
  </Query>
</View>

More examples

More query examples can be found under tests folder.

Also, check out examples folder for usage examples in SP Addins and SPFx projects.

camljs's People

Contributors

andrei-markeev avatar dependabot[bot] avatar dmitrydyachenko avatar viceice avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

camljs's Issues

Create CAML from Object

Hello,
love this feature. I would like to ask if it is possible (if yes - how) to create dynamic CAML query from object (tree) like this:
[ { "type": "condition", "expression": "And", "children": [ { "type": "expression", "condition": [ { "id": 1, "loginName": "...", "imageUrl": "...", "imageInitials": ".", "text": "USER 1" } ], "expression": "Responsible", // internal field name "children": [] }, { "type": "condition", "expression": "Or", "condition": null, "children": [ { "type": "expression", "expression": "Projects", "condition": [ "A", "B" ], "children": [] }, { "type": "expression", "expression": "State", "condition": [ "AA", "BB" ], "children": [] } ] } ] } ]
I can customize the tree ... Parent element in tree is always Condition (And or Or), children can be Conditions or Expressions.

CamlBuilder TypeScript Generic

Hi Andrei,

Is it possible to add a Generic when instantiating the CamlBuilder class?

type Person = "Name" | "Age" | "Address" | "Mobile";

// Should Pass
const validQuery= new CamlBuilder<Person>().Where().TextField("Name").IsNotNull().ToString()

// Should Fail
const invalidQuery = new CamlBuilder<Person>().Where().TextField("Birthdate").IsNotNull().ToString()

Chrome Extension

The chrome extension is no longer providing IntelliSense when building queries in the extension.

Not sure what is causing the issue and noticed it start since the last update.

Uploading CamlJs Extension.PNG…

sometimes All() produces invalid CAML

Here's a simple reproduction of the issue:

CamlBuilder.Expression().All([
   CamlBuilder.Expression().ModStatField("_ModerationStatus").ModStatId().EqualTo(0),
   CamlBuilder.Expression().All([])
]).ToString()

produces

"<And><Eq><FieldRef Name="_ModerationStatus" /><Value Type="ModStat">0</Value></Eq></And>"

AppendAnd/AppendOr (enhancement)

Hi Andrie,

I know you are busy and this isn't a bug or urgent.

Would you be able to create a method of appending to an existing CamlBuilder object? For example I can achieve it with the existing examples but it would be nice to be able to build this at once instead of using the CamlBuilder.fromXml function.

An example of the use case is querying items against multiple user fields that are used within our site:

var query = new CamlBuilder().Where()
  .TextField("Title").Contains("Example").And()
  .UserField("Supervisor").EqualToCurrentUser().ToString();
  
query = "<Query>" + query + "</Query>";

query = CamlBuilder.FromXml(query).ModifyWhere().AppendOr()
	.TextField("Title").Contains("Example").And()
	.UserField("Supervisor").IsInCurrentUserGroups().ToString();

No worries if it can't be done as it still does work but would nice to have something like:

var query = new CamlBuilder().Where()
    .TextField("Title").Contains("Example").And()
    .UserField("Supervisor").EqualToCurrentUser()
  .AppendOr()
    .TextField("Title").Contains("Example").And()
    .UserField("Supervisor").IsInCurrentUserGroups()
  .ToString();

Thanks for your time.

FromXml doesn't work in node environments

First of all, great library. Makes it way nicer to work with CAML.

I'm trying to use FromXml in a node environment and it's throwing a window is not defined error, which makes sense. It looks like the code relies upon DOMParser, which doesn't exist in node.

If this isn't a known issue (or there's no workaround), I'd be happy to help out and submit a PR. Let me know!

Support new CamlBuilder.Query().Where()

Hi Andrei,

Could you add support for new CamlBuilder.Query().

Apologies if this already an option and I have missed it.

I am working with plugins like SPServices and SPJS that require the Query tags. I could add them manually but would rather use your CamlJs.

<Query>
  <Where>
      <Eq>
          <FieldRef Name="Title" />
          <Value Type="Text">A Title Name</Value>
      </Eq>
  </Where>
</Query>

Modifying existing queries only works when the query tag is included. It would be nice to have the following CAMLJS object return the xml above.

new CamlBuilder.Query().Where().TextField("Title").EqualTo("A Title Name").ToString();

TypeError: ...is not a function

I cannot form query which starts with CamlBuilder.FromXml() and appends 3 And().

I tried these scenarios, where all of them fail with "TypeError: ...is not a function"

var query = '<View Scope="Recursive"><Query><Where><Eq><FieldRef Name="MyField" /><Value Type="Text">MyValue</Value></Eq></Where></Query></View>'
var late = CamlBuilder.FromXml(query).ModifyWhere().AppendAnd().DateTimeField("Modified").GreaterThan("2018-08-01T00:00:00Z"); late = late.And().TextField("Request").Contains("Closed");

It works until this point.
But I need one more And() in this query. So I tried adding these:

  1. one more .AppendAnd() in the first ModifyWhere() query like this
    var late = CamlBuilder.FromXml(query).ModifyWhere().AppendAnd().DateTimeField("Modified").GreaterThan("2018-08-01T00:00:00Z").AppendAnd().TextField("Status").EqualsTo("Late");

  2. one more .And() in the second line like this:
    late = late.And().TextField("Request").Contains("Closed").And().TextField("Status").EqualsTo("Late");

  3. event tried to export query ToString() and then import again with CamlBuilder.FromXml() and add this one more time, but does not work also. Like this:
    var query = '<View Scope="Recursive"><Query><Where><Eq><FieldRef Name="MyField" /><Value Type="Text">MyValue</Value></Eq></Where></Query></View>'
    var late = CamlBuilder.FromXml(query).ModifyWhere().AppendAnd().DateTimeField("Modified").GreaterThan("2018-08-01T00:00:00Z");
    late = late.And().TextField("Request").Contains("Closed");
    late = CamlBuilder.FromXml(late.ToString()).ModifyWhere().AppendAnd().TextField("Status").EqualsTo("Late");

Any idea how to make it work ?

Error: Cannot read property 'create' of undefined

Hey Andrei,

I would like to know if there is a way to use your library outside of SharePoint ?
It seems your library need the sp.js script ?

I'm asking you this question because I'm facing the following issue

Uncaught TypeError: Cannot read property 'create' of undefined

Thanks,
Kind regards,

Guillaume Koehrlen

camljs is not working in an SPFx React WebPart

I would like to use your extension in a SPFx React WebPart.

I added your extension in the tsx file via import 'camljs'. I can use Intellisense and I added something like
private abc = new CamlBuilder().View().Query().Where().LookupField("Ppp").Id().In([2,3,4]).ToString();
without a syntax error.

But after adding the webpart to the page I get errors:
...Failed to load entry point from component...CamlBuilder is not defined...

Do you have any idea how to fix this error?

Support Aggregation

I'm using SharePoint Online CAML queries with their new RenderListItemsAsStream endpoint. I use typescript and want to add Aggregations, but I don't see it as an option. Can you support the aggregation property? If you do, can you instruct on how to use? Thanks!

Support ToString() when page is loading

Hello,

  1. I use various queries, not only this:
$(function (){
var query = new CamlBuilder().Where().LookupField("fieldName").Id().EqualTo(1).ToString()
});
  1. Result is:
    image

  2. Could you please explain workaround to use CamlBuilder with ToString() when page is loading? Or maybe is it a bug?

Thanks.

Allow to clone expressions

We currently do some parallel queries and those use some general expressions.
We can't simply reuse them, we need to clone them.
One workaround is to use lodash.deepClone, but i want to get rid of that library.
So for now i always need to recreate the partial expressionen for every query.

sample

import caml from 'camljs';

const w1 = new caml().View(['ID']).Query().Where();
const w2 = new caml().View(['ID']).Query().Where();
const expr = [caml.Expression().IntegerField('FSObjType').EqualTo(1)];

const q1 = w1.All(expr);
const q2 = w2.All(expr);

console.assert(q1.get_viewXml() === q2.get_viewXml(), "Error: query mismatch");

OrderBy Descending is setting CAML Query to Ascending="False", should be Ascending="FALSE"

Hey,

Small bug. Typically this does not matter as SharePoint Online is case insensitive to this, but we have been battling this for a few days and finally tracked down the issue using Postman. For some task lists at our clients their is case sensitivity to the OrderBy Query. Basically just need to adjust Ascending="False" to Ascending="FALSE," all caps. For reference here is the Microsoft documentation referencing the expected values:

https://docs.microsoft.com/en-us/sharepoint/dev/schema/orderby-element-query

InvalidOperationException when using OrderBy

I tried ordering the elements of a simple list by ID, but camljs throws an InvalidOperationException. In the chrome plugin, no caml query is generated.

var query = new CamlBuilder().View(['ID']).Query().OrderBy('ID').ToString()

image

npm issue (CamlBuilder not defined)

I'm using camljs in a commonjs/es6 environment, and CamlBuilder isn't exported correctly (or at all):

In camljs.js, there needs to be a module.exports = CamlBuilder and in index.js it should be:

var CamlBuilder = require('./camljs');
module.exports = CamlBuilder;

Otherwise, CamlBuilder is undefined.

I would create a PR, but I don't know where the npm stuff is defined in this project.

DateTime query not including IncludeTimeValue

I cannot figure out how to get a Where clause that uses a DateTime value to include the IncludeTimeValue="TRUE" attribute. Is this supported? If not, querying is very limited when looking for, say, all new records since a certain time.

Missing Lookup value type

Hi there,
your library is awesome. But could you pls add ILookupFieldExpression as type Lookup? :)

CamlBuilder.All breaks the source array of expressions.

I was crating a search ui and dynamically generated an array of expressions.
The generated array of expressions was stored in the variable.

so something like

       this.expressions = GenerateExpressions(); (should be at least two of them)
        var allAuditsCamlBuilder1 = new CamlBuilder()
            .View(["ID", "Created"])
            .RowLimit(pageSize, true)
            .Scope(CamlBuilder.ViewScope.RecursiveAll).Query().Where()

        var allAuditsCamlBuilder = allAuditsCamlBuilder1.All(this.expressions);
        var allAudits = allAuditsCamlBuilder.ToString();

then i was implementing paging so i was using the same array of expressions.
var allAuditsCamlBuilder = allAuditsCamlBuilder1.All(this.expressions);
var allAudits = allAuditsCamlBuilder.ToString();

and the caml query had an extra outside And.

so what happens is that the expressions are getting changed after the All and then ToString() calls, so i had to regenerate them each time.

Multi-Join Error

The problem:

var query = new CamlBuilder()
    .View(["Title", "CustomerName", "CustomerCity"])
    .LeftJoin("CustomerName", "customers")
    .Select("Name", "CustomerName")
    .LeftJoin("CityName", "customerCities", "customers")
    .Select("Title", "CustomerCity")
    .Query()
    .ToString();

Produces:

<View>
    <ViewFields>
        <FieldRef Name="Title" />
        <FieldRef Name="CustomerName" />
        <FieldRef Name="CustomerCity" />
    </ViewFields>
    <Joins>
        <Join Type="LEFT" ListAlias="customers">
            <Eq>
                <FieldRef Name="CustomerName" RefType="ID" />
                <FieldRef Name="ID" List="customers" />
            </Eq>
        </Join>
        <Join Type="LEFT" ListAlias="customerCities">
            <Eq>
                <FieldRef Name="CityName" RefType="ID" />
                <FieldRef Name="ID" List="customerCities" />
            </Eq>
        </Join>
    </Joins>
    <ProjectedFields>
        <Field ShowField="Name" Type="Lookup" Name="CustomerName" List="customers" />
        <Field ShowField="Title" Type="Lookup" Name="CustomerCity" List="customerCities" />
    </ProjectedFields>
    <Query />
</View>

The list attribute for CityName at customerCities is missing.

The JoinManager.ProjectField return the original view, which does not accept the fromList parameter,
but the return type IProjectableView allows the fromList param.

Bug with .toString()

Following code:

let camlString = new CamlBuilder().View().Query().Where()
 .DateTimeField("Modified").GreaterThanOrEqualTo(lastMonth.toISOString())
 .And()
 .All(caml);
console.log(camlString.ToString());
console.log(camlString.ToString());
console.log(camlString.ToString());

Produces following console output:

<View><Query><Where><And><Geq><FieldRef Name="Modified" /><Value IncludeTimeValue="True" Type="DateTime">2019-03-13T21:22:31.332Z</Value></Geq><Or><Eq><FieldRef Name="DocCategory" /><Value Type="Text">ALL</Value></Eq><Eq><FieldRef Name="DocCategory" /><Value Type="Text">Policy</Value></Eq></Or></And></Where></Query></View>
<View><Query><Where><And><Geq><FieldRef Name="Modified" /><Value IncludeTimeValue="True" Type="DateTime">2019-03-13T21:22:31.332Z</Value></Geq><Or><Eq><FieldRef Name="DocCategory" /><Value Type="Text">ALL</Value></Eq><Eq><FieldRef Name="DocCategory" /><Value Type="Text">Policy</Value></Eq></Or>
<View><Query><Where><And><Geq><FieldRef Name="Modified" /><Value IncludeTimeValue="True" Type="DateTime">2019-03-13T21:22:31.332Z</Value></Geq><Or><Eq><FieldRef Name="DocCategory" /><Value Type="Text">ALL</Value></Eq><Eq><FieldRef Name="DocCategory" /><Value Type="Text">Policy</Value></Eq></Or>

The last part of the caml string is missing when you're using the .toString() method more than once.

License

Hi,

please add some licensing information to your repository. On the old codeplex page you licensed under Ms-PL. What is the current license?

Regards
Michael

Please add methods after CamlBuilder.FromXml

I need to change OrderBy and ViewFields in an already created CAML query.

I made a React component for Lookup fields. I want to pass a clarifying CAML query into it, and not completely. That would not take out the logic of working with lookup field outside the component.

BooleanField gets improperly translated to Integer field

CamlJS translates BooleanField('fieldname').EqualTo(true) into




1


which does not return the proper values. I tested with U2U Caml Query Builder against SP 2013 and SP 2019 and found that the proper syntax should be

1

So, CamlJS gets it partly right by turning 'true' into '1' but it improperly marks the Value type as 'Integer'. It should be 'Boolean' since that is the type of field registered with SP. Otherwise, the wrong records are returned.

I am using the latest CamlJS of 2.12.0.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.